Routing
Routing happens at the agent layer. Each agent points at exactly one provider; channels point at agents.
Two layers of decisions:
- Per-call backend selection: “use the cheap model unless this prompt looks like reasoning.” Each routing target is its own
[agents.<alias>]entry with its ownmodel_provider. Channels are routed to whichever agent should handle their traffic. - Provider reliability: vendor-redundancy lives behind a single first-class provider. Configure OpenRouter (or an equivalent) as one provider and let it handle vendor fan-out at its endpoint.
Per-agent dispatch
Define each routing target as its own agent, then point channels at the agent that should handle their traffic.
Each channel binds to one agent. Channels move between agents by editing channels = [...] on the agent that should pick them up; Config::validate() makes sure references resolve.
For ad-hoc multi-step routing inside a single conversation, the spawn_subagent tool lets an agent run an ephemeral child under its own identity. The child inherits the parent’s permissions envelope (see [risk_profiles.<alias>].allowed_tools) and returns its final response to the parent’s tool loop.
Hint-based model routes
A narrower mechanism: [[model_routes]] lets an agent override the configured model_provider for prompts marked with a hint string. Useful when one agent should occasionally reach for a different model without spinning up a second agent. Each route entry carries a hint (the string a prompt must declare to fire it), a model_provider (the dotted <type>.<alias> profile to switch to, e.g. deepseek.reasoner), and a model (the provider-local model id, e.g. deepseek-reasoner). Configure routes through the gateway, zerocode, or zeroclaw config set; see the Config reference for the field schema.
Routes only fire when a prompt explicitly carries the matching hint. The default request path uses the agent’s primary model_provider.
model_provider is always a provider profile reference in dotted <type>.<alias> form, such as anthropic.sonnet or openai.default. The profile carries the endpoint, credential reference, compatibility flavor, fallback chain, and configured default model. The model field is provider-local state under that profile.
Runtime model switching
Runtime switches use the same provider-profile contract as config-backed routing:
/models <type>.<alias>selects the active provider profile for the sender session. Channel runtimes can also accept a bare<type>shorthand when exactly one configured alias exists for that provider family./model <model-id>selects a model within the active provider profile. If the value matches a[[model_routes]]hint or model, that route can switch both provider profile and model together.- The
model_switchtool usesmodel_provider = "<type>.<alias>"plusmodel = "<provider-local-model-id>".
Runtime switches are session/runtime state. They do not edit config.toml; persisted defaults require an explicit config write. For tool-driven switches, bare provider family names such as openai are not switch targets because they do not identify which configured profile, credential, endpoint, or compatibility mode should be used.
Observability
Per-agent dispatch decisions are visible in tracing logs:
INFO channel=telegram.home routed to agent=fast
INFO agent=fast model_provider=anthropic.haiku turn_id=...
INFO model_provider=anthropic.haiku stream complete tokens={input=512, output=128}
For production deployments, wire the log output to Loki / Grafana. See Operations → Logs & observability.
See also
- Overview: provider model and per-agent dispatch
- Configuration: full
[providers.*]schema - Provider catalog: every canonical slot