Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Routing

Routing happens at the agent layer. Each agent points at exactly one provider; channels point at agents.

Two layers of decisions:

  1. 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 own model_provider. Channels are routed to whichever agent should handle their traffic.
  2. 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_switch tool uses model_provider = "<type>.<alias>" plus model = "<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