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.

[providers.models.anthropic.sonnet]
model   = "claude-sonnet-4-6"
api_key = "sk-ant-..."

[providers.models.anthropic.haiku]
model   = "claude-haiku-4-5-20251001"
api_key = "sk-ant-..."

[providers.models.deepseek.reasoner]
model   = "deepseek-reasoner"
api_key = "sk-..."

[providers.models.gemini.vision]
model   = "gemini-2.5-pro"
api_key = "..."

[channels.telegram.home]
bot_token = "..."

[channels.slack.engineering]
bot_token = "..."

[channels.slack.research]
bot_token = "..."

[channels.discord.media]
bot_token = "..."

[agents.fast]
model_provider  = "anthropic.haiku"
risk_profile    = "hardened"
runtime_profile = "tight"             # snappy public replies
channels        = ["telegram.home"]

[agents.deep]
model_provider  = "anthropic.sonnet"
risk_profile    = "hardened"
runtime_profile = "deep"              # extended engineering tasks
channels        = ["slack.engineering"]

[agents.reasoner]
model_provider  = "deepseek.reasoner"
risk_profile    = "hardened"
runtime_profile = "deep"              # research-style reasoning chains
channels        = ["slack.research"]

[agents.eyes]
model_provider  = "gemini.vision"
risk_profile    = "hardened"
runtime_profile = "tight"             # quick image-bearing replies
channels        = ["discord.media"]

[risk_profiles.hardened]
level                            = "supervised"
workspace_only                   = true
require_approval_for_medium_risk = true
block_high_risk_commands         = true

[runtime_profiles.tight]
max_tool_iterations  = 5
max_actions_per_hour = 30

[runtime_profiles.deep]
max_tool_iterations  = 50
max_actions_per_hour = 200

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.

[[model_routes]]
hint           = "reasoning"
model_provider = "deepseek"
model          = "deepseek-reasoner"

Routes only fire when a prompt explicitly carries the matching hint. The default request path uses the agent’s primary model_provider.

可观测性

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}

对于生产部署,将日志输出连接到 Loki / Grafana。请参阅 运维 → 日志与可观测性

另见