docs/book/src/getting-started/multi-model-setup.md
A walkthrough of the common patterns for using multiple model providers: per-agent dispatch, cost tiering, local-first with hosted backup, API key rotation, and rate-limit handling.
Reference material for the provider system lives in:
- Model Providers → Overview: what providers are, configuration shape
- Model Providers → Routing: per-agent dispatch and OpenRouter
- Model Providers → Catalog: every provider's config shape
Multi-model configuration is useful for:
429 (rate limit) responsesEach [agents.<alias>] entry points at exactly one [providers.models.<type>.<alias>]. If the model goes down, the agent goes down; the operator routes affected channels to a different agent. See Routing for the full pattern.
To run multiple models, run multiple agents, each binding to one model provider. Each channel binds to one agent at a time. To move a channel to a different agent, edit the channels list on the agent that should pick it up; Config::validate() makes sure references resolve at startup.
OpenRouter is treated as a single first-class provider. It handles vendor fan-out and uptime behind one endpoint. If your goal is "one provider goes down, automatically use another", that's OpenRouter's job, not ZeroClaw's. The runtime sees one provider; OpenRouter does the cross-vendor work upstream.
For transient errors (network blip, 503, timeout) against the same provider, ZeroClaw retries with exponential backoff, configurable globally under reliability (defaults: 2 retries, 500 ms initial backoff). These are inside-one-provider retries.
For providers that frequently encounter rate limits, supply additional API keys on the provider entry that ZeroClaw rotates through on 429 responses. The primary api_key is always tried first; extras are rotated on rate-limit errors. All keys must belong to the same provider account class; this is rate-limit smoothing, not multi-tenant key juggling.
Run a local-Ollama agent and a hosted-provider agent side by side; route each channel to whichever you want it to use.
The dev agent runs from the CLI (no channel binding required, zeroclaw agent -a dev is enough). When Ollama is down, the dev agent fails fast and surfaces the error. The prod channels are unaffected.
Small local models usually need a runtime profile, not a provider-specific mode. Keep the Ollama provider focused on connection details, then use [runtime_profiles.<alias>] to tighten the prompt/tool loop behavior.
[providers.models.ollama.local]
uri = "http://localhost:11434"
model = "qwen2.5-coder:7b"
[agents.local]
model_provider = "ollama.local"
risk_profile = "supervised"
runtime_profile = "local_small"
[risk_profiles.supervised]
level = "supervised"
workspace_only = true
require_approval_for_medium_risk = true
block_high_risk_commands = true
[runtime_profiles.local_small]
compact_context = true
strict_tool_parsing = true
max_tool_iterations = 4
max_history_messages = 20
max_context_tokens = 8000
max_tool_result_chars = 4000
keep_tool_context_turns = 1
This profile composes existing primitives:
compact_context keeps startup context small.strict_tool_parsing treats XML/JSON-looking fallback text as assistant text unless the provider returns native tool calls.max_tool_iterations, max_context_tokens, and max_tool_result_chars bound runaway loops and oversized tool context.With Ollama, this is a no-text-fallback profile: authorized tools remain configured in risk_profile, but text-form tool markup from the model is not executed. Use it for chat-first local agents, or for providers that return native/structured tool calls. If a local model must use ZeroClaw's text fallback tool syntax, set strict_tool_parsing = false and keep the other small-model limits.
Run two agents and route channels to the appropriate tier. The delegate tool lets one agent hand off to another mid-conversation. Delegation is gated: the caller's risk profile must set delegation_policy mode = "allow", and the target must be reachable from the caller (a same-profile peer, or an explicit entry in the caller's delegates list). The frontline and heavy agents below run on the same trusted risk profile, so they reach each other as same-profile peers; they differ in model and runtime profile (iteration budget), not in trust surface.
The frontline agent handles every inbound message on Haiku. When it needs deeper reasoning, it calls the delegate tool with agent = "heavy"; because both agents share the trusted risk profile and that profile allows delegation, the heavier agent picks up the sub-task on Opus.
Inside-one-provider retries trigger on:
Retries are NOT triggered by:
When all retries are exhausted on a single provider, the failure surfaces to the calling channel. There is no automatic cross-provider retry, that's the point of using OpenRouter or splitting traffic across multiple agents.
Persisted logs ("rolling" is the default) capture retry and key-rotation behaviour. Then query traces:
zeroclaw doctor traces --contains "retry"
zeroclaw doctor traces --contains "429"
zeroclaw doctor traces --contains "model_provider"
[reliability] api_keys should be from the same provider account, this is rate-limit smoothing, not multi-tenancy.zeroclaw agent -a <alias> runs an agent without channel plumbing in the way.# comment lines explaining which channels each agent serves and why.ZEROCLAW_providers__models__<type>__<alias>__api_key=... sets api_key at startup; see Environment variables.[agents.<alias>] entry bound to its own channels.Each provider entry resolves credentials in this order:
api_key on the provider entry.~/.zeroclaw/secrets.ZEROCLAW_providers__models__<type>__<alias>__api_key=... at startup. See Environment variables for the full grammar.ANTHROPIC_API_KEY / ANTHROPIC_OAUTH_TOKEN for Anthropic; OPENROUTER_API_KEY for OpenRouter).Credentials are not shared between providers, set them per provider entry.