One Agent, Many Providers: Routing and Fallback Reference

AI Tools Insight • 2026-09-20 • Hermes Agent Provider Routing Fallback OpenCode Go Multi-Provider Auxiliary Models

This page is my own reference. I run one agent against seven providers. When a request fails, I need to know which layer moved. This page answers that.

Last verified: 2026-09-20.

Where Each Setting Lives

Hermes resolves settings in this order. The first match wins:

  1. CLI arguments (hermes chat --model ...)
  2. config.yaml — all non-secret settings
  3. .env — secrets, and a fallback for env vars
  4. Built-in defaults

Rule: secrets go in .env. Everything else goes in config.yaml.

The Two Provider Slots

The main model and the fallback chain are different settings.

Main modelmodel in config.yaml:

model:
  default: deepseek-v4.1-flash
  provider: opencode-go
  base_url: https://opencode.ai/zen/go/v1
  api_mode: chat_completions
  extra_body: {"cache_prompt": true}

Fallback chain — top-level fallback_providers list:

fallback_providers:
  - provider: ollama-cloud
    model: deepseek-v4.1-flash
  - provider: agnes
    model: agnes-3.0-flash

Each entry needs both provider and model. An entry with one field is ignored.

Run hermes fallback to edit the chain interactively. It reuses the hermes model picker.

How Fallback Behaves

fallback_model (singular) is the legacy key. Hermes still reads it. fallback_providers (plural) wins when both are set.

Where Fallback Works

Context Supported
CLI sessions, interactive and one-shot Yes
Messaging gateway (Telegram, Discord) Yes
Desktop app and TUI Yes
Subagent delegation Yes, via delegation.fallback_providers
Cron jobs Yes, cron inherits the chain
Auxiliary tasks on provider: auto Yes

A chain added while a chat is open applies from the next turn.

Auxiliary Task Routing

Side tasks use their own resolution. Each one tries the main provider first:

Main provider + main model → auxiliary.<task>.fallback_chain →
fallback_providers → skip the task and warn

The built-in text discovery chain, used when the main route is unavailable:

OpenRouter → Nous Portal → Custom endpoint → Codex OAuth →
z.ai / Kimi / MiniMax / Xiaomi MiMo / Hugging Face / Anthropic → give up

Tasks with independent resolution: vision, compression, skills_hub, mcp, approval, title_generation, review, triage_specifier.

A billing or quota failure quarantines only the failed custom endpoint. Other endpoints stay eligible.

Named Custom Providers

Define extra endpoints under providers: in config.yaml:

providers:
  agnes:
    api: https://api.agnes-ai.cn/v1
    key_env: HERMES_CUSTOM_AGNES_API_KEY
    default_model: agnes-2.5-flash
  sensenova:
    api: https://token.sensenova.cn/v1
    key_env: HERMES_CUSTOM_SENSENOVA_API_KEY
    default_model: glm-5.2

Accepted fields: api, name, key_env, inline api_key, key_cmd, transport, default_model, models, context_length, discover_models, extra_body, extra_headers, session_affinity_header, ssl_ca_cert, ssl_verify, catalog_provider, enabled.

Use key_env and not a literal key. The key then stays out of config.yaml.

Set transport explicitly. The values are chat_completions, anthropic_messages, and codex_responses. Auto-detection runs only as a fallback.

OpenRouter Routing

Control how OpenRouter picks a backend:

provider_routing:
  sort: "throughput"    # price (default), throughput, latency
  data_collection: "deny"

Shortcuts: append :nitro to a model name for throughput sorting. Append :floor for price sorting.

Diagnostic: A 401 From Every Custom Provider

I hit this. Three custom providers returned 401 at the same time. The endpoints were correct.

Cause: key_env names an environment variable. The variable was not in the process environment. Hermes keeps those keys in its own secret store.

Check it this way:

# Does the variable exist in the shell?
echo "${HERMES_CUSTOM_AGNES_API_KEY:-NOT SET}"

# Does the endpoint answer at all?
curl -s -o /dev/null -w "%{http_code}\n" https://api.agnes-ai.cn/v1/models

A 401 proves the endpoint exists and asks for a key. A 000 means the network blocks it.

Fix: let Hermes resolve the key from its store. Do not export the value in a shell profile.

Check the Chain

hermes fallback list      # show the chain
hermes doctor             # config and dependency check
hermes config get model   # resolved main model
hermes status --all       # component status

hermes fallback list reads config.yaml. It does not prove that the keys work. Run one request per provider to prove that.

OpenCode Go Note

Hermes pins each OpenCode provider to its own relay. opencode-zen uses /zen/v1. opencode-go uses /zen/go/v1.

The model you pick decides the relay. Switching from a Zen model to a Go-only model never sends the request to Zen. The same rule applies when you resume a session.

A -vision marker in a model id marks the model as vision-capable. Example: deepseek-v4-flash-vision-exp.

Sources

Share this article
AI Tools Insight may earn a commission from some links. Editorial independence is always maintained.