模型进阶:路由、Fallback 与凭据池
本教程共 25 篇 · 第 6 篇 · 更新于 2026-07-26 · 约 16 分钟阅读
6. 模型进阶:路由、Fallback 与凭据池
本节目标:搞懂 Hermes Agent 的三层弹性机制—Provider Routing、Fallback Providers、Credential Pools,以及怎么用 Subscription Proxy 把你的订阅借给外部应用用。学完你能让智能体在一个 Provider 限流时自动切到另一个、让多个 API Key 轮换扛量、还能让 OpenViking 这类非 Hermes 工具复用你的 Nous Portal 订阅。
三层弹性机制全景
上一章讲的是「怎么配一个模型」。但生产环境里,单个 Provider 早晚会出问题—限流、宕机、欠费、网络抖动。Hermes Agent 设计了三层弹性机制,从内到外依次兜底:
- Credential Pools(凭据池):同一个 Provider 下挂多个 API Key,一个 Key 限流了自动换下一个。最先尝试。
- Fallback Providers(故障转移):主模型整个 Provider 挂了,自动切到另一个 Provider 的另一个模型。
- Auxiliary Task Fallback(辅助任务回退):辅助任务有自己独立的 Provider 解析链,跟主模型的弹性互不干扰。
打个比方,凭据池是你钱包里好几张同一家银行的卡,一张刷不过换另一张;Fallback 是这家银行系统崩了,你掏出另一家银行的卡接着刷。两者独立工作,凭据池先试,全挂了再 Fallback。
Note这三层都是可选的,不配也能跑。但只要你的智能体要长期跑网关、跑定时任务,配一层 Fallback 几乎是必须的—不然凌晨三点 Provider 限流,你的机器人就哑巴了。
Provider Routing:聚合器内部路由
当你的 Provider 是 OpenRouter 或 Nous Portal 这种聚合器时,背后其实有好多家子 Provider(Anthropic、Google、AWS Bedrock、Together AI 等)。Provider Routing 让你控制请求走哪几家子 Provider、按什么优先级排。
NoteProvider Routing 只在使用 OpenRouter 或 Nous Portal 时生效。直连某个 Provider(比如直连 Anthropic API)时无效—你都直连了,没有子 Provider 可选。
配置
在 ~/.hermes/config.yaml 加一个 provider_routing 段:
provider_routing:
sort: "price" # 怎么排序子 Provider
only: [] # 白名单:只用这些
ignore: [] # 黑名单:绝不用这些
order: [] # 显式优先级顺序
require_parameters: false # 只用支持全部参数的子 Provider
data_collection: null # 控制数据收集("allow" 或 "deny")
五个选项详解
sort 控制排序方式:
| 值 | 含义 |
|---|---|
"price" | 最便宜的子 Provider 优先 |
"throughput" | 每秒 token 数最高的优先 |
"latency" | 首 token 延迟最低的优先 |
only 是白名单,设了就只用这几家,其他全排除:
provider_routing:
only:
- "anthropic"
- "google"
ignore 是黑名单,这几家永远不用,哪怕它们最便宜最快:
provider_routing:
ignore:
- "together"
- "deepinfra"
order 是显式优先级,列前面的优先,没列的当 fallback:
provider_routing:
order:
- "anthropic"
- "google"
- "amazon-bedrock"
require_parameters 设 true 时,OpenRouter 只把请求路由到支持你所有参数(temperature、top_p、tools 等)的子 Provider。避免参数被静默丢弃。
data_collection 控制子 Provider 能不能拿你的 prompt 训练,"allow" 或 "deny"。
推荐配方
# 省钱党:最便宜优先,但排除几家不信任的,禁止数据收集
provider_routing:
sort: "price"
ignore: ["together"]
require_parameters: true
data_collection: "deny"
# 交互党:低延迟优先
provider_routing:
sort: "latency"
# 一致性党:锁死一家
provider_routing:
only: ["anthropic"]
TipPortal 订阅用户走 Nous Portal 时,同样的 provider_routing 偏好也生效,而且 Portal 订阅用户在按量计费的子 Provider 上还能再打 9 折。
NoteProvider Routing 控制的是聚合器内部的子 Provider。如果你要的是「主 Provider 整个挂了切到另一个 Provider」,那不是 Routing 的事,是下面要讲的 Fallback。
Fallback Providers:跨 Provider 故障转移
当主模型所在的 Provider 出错—限流、过载、鉴权失败、连接中断—Hermes 可以在会话中途自动切到一个备用的 provider:model 组合,对话不丢。
配置
最省心的方式是交互式管理器:
hermes fallback
hermes fallback 复用 hermes model 的选择器,子命令有 add、list(别名 ls)、remove(别名 rm)、clear。改动持久化到 config.yaml 顶层的 fallback_providers: 列表。
或者直接编辑 ~/.hermes/config.yaml:
fallback_providers:
- provider: openrouter
model: anthropic/claude-sonnet-4
每个条目必须同时有 provider 和 model,缺一个会被忽略。
Note
fallback_providers(复数,列表)是当前配置形态,支持多个 fallback 按顺序试。fallback_model(单数)是旧版单 fallback 键,Hermes 仍兼容它,但hermes fallback写的是新的fallback_providers键,并在写入时迁移旧配置。两者同时存在时,fallback_providers优先。
支持的 Provider
Fallback 支持的 Provider 非常多,下面是主流的:
| Provider | 值 | 凭据要求 |
|---|---|---|
| OpenRouter | openrouter | OPENROUTER_API_KEY |
| Nous Portal | nous | hermes setup --portal 或 hermes auth add nous |
| OpenAI Codex | openai-codex | hermes model 走 ChatGPT OAuth |
| GitHub Copilot | copilot | COPILOT_GITHUB_TOKEN / GH_TOKEN / GITHUB_TOKEN |
| Anthropic | anthropic | ANTHROPIC_API_KEY 或 Claude Code 凭据 |
| Google AI Studio | gemini | GOOGLE_API_KEY |
| xAI (Grok) | xai | XAI_API_KEY |
| AWS Bedrock | bedrock | boto3 标准鉴权 |
| DeepSeek | deepseek | DEEPSEEK_API_KEY |
| Alibaba / DashScope | alibaba | DASHSCOPE_API_KEY |
| Ollama Cloud | ollama-cloud | OLLAMA_API_KEY |
| LM Studio(本地) | lmstudio | LM_API_KEY + LM_BASE_URL |
| Custom endpoint | custom | base_url + key_env |
Custom Endpoint Fallback
自定义 OpenAI 兼容端点也能当 fallback,加 base_url 和可选的 key_env:
fallback_providers:
- provider: custom
model: my-local-model
base_url: http://localhost:8000/v1
key_env: MY_LOCAL_KEY # 存 API Key 的环境变量名
什么时候触发
Fallback 在主模型遇到这些错误时自动激活:
- 限流(HTTP 429):重试耗尽后
- 服务器错误(HTTP 500/502/503):重试耗尽后
- 鉴权失败(HTTP 401/403):立即(不用重试)
- 未找到(HTTP 404):立即
- 无效响应:API 反复返回畸形或空响应时
触发时 Hermes 会:
- 解析 fallback Provider 的凭据
- 构建新的 API 客户端
- 原地替换 model、provider、client
- 重置重试计数,继续对话
切换是无缝的—对话历史、工具调用、上下文都保留,智能体从断点继续,只是换了个模型。
关键特性:每回合作用域
NoteFallback 是回合级的,不是会话级。每条新用户消息开始时,主模型都会被还原。如果主模型在回合中失败,Fallback 只为这个回合激活。下一条消息,Hermes 又试主模型。单个回合内 Fallback 最多激活一次—如果 fallback 也失败,就走正常错误处理。这既防止回合内的级联故障循环,又给主模型每回合一次新机会。
Fallback 在哪些场景生效
| 场景 | Fallback 支持 |
|---|---|
| CLI 会话 | 是 |
| 消息网关(Telegram/Discord 等) | 是 |
| 子智能体委托 | 是(子智能体继承父级的 fallback 链) |
| 定时任务 | 是(cron 智能体继承配置的 fallback) |
辅助任务(provider: auto) | 是(先试任务级 fallback,再试主 fallback 链) |
Tip主 fallback 链没有环境变量配置,只能通过
config.yaml或hermes fallback配。这是故意的—fallback 配置是个慎重决定,不该被一个过期的 shell export 覆盖。推荐配方
# Anthropic 原生为主,OpenRouter 兜底
model:
provider: anthropic
default: claude-sonnet-4-6
fallback_providers:
- provider: openrouter
model: anthropic/claude-sonnet-4
# 本地模型为主,云端兜底(本地跑 90%,硬题打云端)
model:
default: "gemma4:31b"
provider: "custom"
base_url: "http://localhost:11434/v1"
fallback_providers:
- provider: openrouter
model: anthropic/claude-sonnet-4
# OpenRouter 为主,Codex OAuth 兜底
fallback_providers:
- provider: openai-codex
model: gpt-5.3-codex
WarningFallback 会重置提示词缓存。提示词缓存按模型(多数 Provider 还按账号)绑定。Fallback 触发时,新 provider:model 对你的对话没有缓存前缀,下一条请求按全价输入 token 重读整个历史,而不是 75-90% 折扣的缓存价。回合结束主模型还原时,第一条回主模型的请求也是全价重读(除非主模型的缓存 TTL 还没过)。这是熬过故障的代价—长会话在 Provider 之间来回弹跳,成本会明显高于一直待在一个 Provider。
Credential Pools:同 Provider 多 Key 轮换
凭据池让你给同一个 Provider 注册多个 API Key 或 OAuth token。一个 Key 限流或欠费了,Hermes 自动轮换到下一个健康 Key—不用切 Provider,会话继续。
这跟 Fallback 不一样:Fallback 是切到另一个 Provider,凭据池是同 Provider 内换 Key。凭据池先试,全挂了才轮到 Fallback。
Tip凭据池主要给 API Key 类 Provider 用(OpenRouter、Anthropic)。一个 Nous Portal OAuth 就覆盖 300+ 模型,Portal 用户基本不需要配池子。
工作流程
你的请求
-> 从池里选 Key(round_robin / least_used / fill_first / random)
-> 发给 Provider
-> 429 限流?
-> 是套餐/用量上限(如 ChatGPT/Codex "usage limit reached")?
-> 立即轮换到下一个池 Key(不重试,上限不会因重试而清)
-> 普通/瞬时 429?
-> 同一个 Key 重试一次(瞬时抖动)
-> 第二次 429 -> 轮换到下一个池 Key
-> 所有 Key 耗尽 -> fallback_model(换 Provider)
-> 402 计费错误?
-> 立即轮换到下一个池 Key(24 小时冷却)
-> 401 鉴权过期?
-> 先试刷新 token(OAuth)
-> 刷新失败 -> 轮换到下一个池 Key
-> 成功 -> 正常继续
快速上手
如果你已经在 .env 里设了 API Key,Hermes 会自动把它发现成一个 1 Key 的池。要享受池化,加更多 Key:
# 加第二个 OpenRouter Key
hermes auth add openrouter --api-key sk-or-v1-your-second-key
# 加第二个 Anthropic Key
hermes auth add anthropic --type api-key --api-key sk-ant-api03-your-second-key
# 加 Anthropic OAuth 凭据(需要 Claude Max 套餐 + 额外用量额度)
hermes auth add anthropic --type oauth
# 会打开浏览器做 OAuth
查看池子:
hermes auth list
输出长这样:
openrouter (2 credentials):
#1 OPENROUTER_API_KEY api_key env:OPENROUTER_API_KEY ←
#2 backup-key api_key manual
anthropic (3 credentials):
#1 hermes_pkce oauth hermes_pkce ←
#2 claude_code oauth claude_code
#3 ANTHROPIC_API_KEY api_key env:ANTHROPIC_API_KEY
← 标记当前选中的凭据。
轮换策略
通过 hermes auth -> “Set rotation strategy” 或 config.yaml 配:
credential_pool_strategies:
openrouter: round_robin
anthropic: least_used
| 策略 | 行为 |
|---|---|
fill_first(默认) | 用第一个健康 Key 直到耗尽,再换下一个 |
round_robin | 均匀循环,每次选完后轮换 |
least_used | 总是选请求计数最低的 Key |
random | 在健康 Key 里随机选 |
错误恢复
池子对不同错误处理方式不同:
| 错误 | 行为 | 冷却 |
|---|---|---|
| 429 限流 | 同 Key 重试一次(瞬时)。连续第二次 429 轮换 | 1 小时 |
| 402 计费/配额 | 立即轮换 | 24 小时 |
| 401 鉴权过期 | 先试刷新 OAuth token。刷新失败才轮换 | - |
| 所有 Key 耗尽 | 透传到 fallback_model(换 Provider) | - |
has_retried_429 标志在每次成功 API 调用时重置,所以单次瞬时 429 不会触发轮换。
自动发现
Hermes 启动时从多个来源自动发现凭据并填充池子:
| 来源 | 示例 | 自动入池 |
|---|---|---|
| 环境变量 | OPENROUTER_API_KEY、ANTHROPIC_API_KEY | 是 |
| OAuth token(auth.json) | Codex 设备码、Nous 设备码 | 是 |
| Claude Code 凭据 | ~/.claude/.credentials.json | 是(Anthropic) |
| Hermes PKCE OAuth | ~/.hermes/auth.json | 是(Anthropic) |
| 自定义端点配置 | config.yaml 里的 model.api_key | 是(自定义端点) |
| 手动添加 | hermes auth add | 持久化到 auth.json |
自动入池的条目每次加载池时更新—删了环境变量,对应的池条目自动修剪。手动添加的条目不会被自动修剪。
委托与子智能体共享
智能体通过 delegate_task 派生子智能体时,父级的凭据池自动共享给子级:
- 同 Provider:子级继承父级完整池子,限流时能轮换 Key
- 不同 Provider:子级加载该 Provider 自己的池子(如果配了)
- 没配池子:子级回退到继承的单个 API Key
子智能体不用额外配置就能享受跟父级一样的限流韧性。按任务租借凭据,保证子级并发轮换 Key 时不互相冲突。
WarningKey 轮换会重置提示词缓存。Provider 侧的提示词缓存(Anthropic、OpenAI、OpenRouter)按发起请求的账号/API Key 作用域。会话中途池子轮换到另一个 Key 时,新 Key 对你的对话没有缓存前缀,下一条请求按全价重读整个历史,轮换回去时又是一次全价重读(除非前一个 Key 的缓存 TTL 还活着)。轮换保住了会话不中断,但长对话里每次轮换都是一次全价过上下文。
Auxiliary Task Fallback:辅助任务的独立链
辅助任务(视觉、压缩、网页抽取、技能搜索等)有自己独立的 Provider 解析链,跟主模型的弹性互不干扰。
自动检测链
当任务的 provider 设为 "auto"(默认)时,Hermes 先试主 Provider + 主模型。如果该路由不可用或之后遇到容量级错误,Hermes 按这个顺序兜底:
主 Provider + 主模型 -> auxiliary.<task>.fallback_chain ->
fallback_providers / fallback_model -> 内置辅助发现链
任务级链最精确,有就先用。顶层 fallback_providers 链跟主智能体用的一样,所以免费或同 Provider 的 fallback 规则也作用于 auto 的辅助任务。
内置文本发现链(压缩、网页抽取、标题生成等):
OpenRouter -> Nous Portal -> Custom endpoint -> Codex OAuth ->
API-key providers(z.ai、Kimi、MiniMax、Xiaomi MiMo、Hugging Face、Anthropic)-> 放弃
内置视觉发现链:
主 Provider(如果支持视觉)-> OpenRouter -> Nous Portal ->
Codex OAuth -> Anthropic -> Custom endpoint -> 放弃
这些内置链是给没声明任务级或主 fallback 策略的用户的便利兜底。
配置辅助任务
每个任务可以独立配:
auxiliary:
vision:
provider: "auto" # auto | openrouter | nous | codex | main | anthropic
model: "" # 如 "openai/gpt-4o"
base_url: "" # 直连端点(优先于 provider)
api_key: "" # base_url 的 API Key
compression:
provider: "auto"
model: ""
fallback_chain: # 可选,任务级 fallback 策略
- provider: openrouter
model: inclusionai/ring-2.6-1t:free
省略 fallback_chain 时,provider: auto 会先走顶层 fallback_providers 链,再走内置辅助发现链。
任务级 fallback_chain
想要跟「主智能体模型优先」不同的顺序,就显式配 fallback_chain:
auxiliary:
vision:
provider: glm
model: glm-4v-flash
fallback_chain:
- provider: openrouter
model: google/gemini-3-flash-preview
- provider: nous
model: anthropic/claude-sonnet-4
compression:
provider: openrouter
fallback_chain:
- provider: openai
model: gpt-4o-mini
timeout: 240 # 可选,这个候选自己的超时(秒)
每个 fallback_chain 条目还能声明自己的 timeout。不声明就继承任务级超时—而任务级超时可能是为主 Provider 调的。声明 per-entry timeout 能让一个慢但可靠的 fallback(比如大上下文摘要器)拿到它真正需要的预算。
Note不配
fallback_chain也有 fallback—主智能体模型的安全网无论如何都会跑。fallback_chain只在你想要跟默认不同的顺序时才配。
容量错误回退
当你显式设了辅助 Provider(比如 auxiliary.vision.provider: glm),Hermes 把它当首选。但如果该 Provider 因为容量错误(HTTP 402 欠费、HTTP 429 日配额耗尽、连接失败)实在没法服务请求,Hermes 走分层链兜底,而不是静默失败:
- 主辅助 Provider—你配的那个(总是先试)
auxiliary.<task>.fallback_chain—你写的 per-task 覆盖列表- 主智能体 Provider + 模型—最后的安全网(总会试,即使你没写链)
- 警告 + 重抛—全挂了就记
Auxiliary <task>: ... all fallbacks exhausted警告,重抛原始错误
瞬时 HTTP 429 限流(Retry-After: ...)被当作请求约束,不是容量问题—它们尊重你显式选的 Provider,不触发 fallback 阶梯。只有日/月配额耗尽、计费错误、连接失败才绕过显式 Provider 门槛。
TipHermes 识别这些短语为容量等效于 402 信用耗尽(不是瞬时限流):Bedrock/LiteLLM 的
Too many tokens per day、daily limit;Vertex AI/GCP 的quota exceeded、resource exhausted、RESOURCE_EXHAUSTED;通用的daily quota、quota_exceeded。如果你的 Provider 返回了不同的配额耗尽措辞而 Hermes 没触发 fallback,那是 bug—带完整错误字符串提 issue。
Subscription Proxy:把订阅给外部应用用
Subscription Proxy 是一个本地 HTTP 服务器,让外部应用—OpenViking、Karakeep、Open WebUI,任何会讲 OpenAI 兼容 chat completions 的应用—把你 Hermes 管理的 Provider 订阅当它们的 LLM 端点用。Proxy 自动挂上正确的凭据(自动刷新),应用永远不需要静态 API Key。
这跟 API Server 不一样:
| API Server | Subscription Proxy | |
|---|---|---|
| 服务什么 | 你的智能体(完整工具集、记忆、技能) | 裸模型推理 |
| 用例 | 「把 Hermes 当聊天后端」 | 「让别的应用用我的 Portal 订阅」 |
| 鉴权 | 你的 API_SERVER_KEY | 任意 bearer(proxy 挂真凭据) |
| 工具调用 | 是—智能体跑工具 | 否—纯透传 |
要智能体当后端用 API Server。只要模型走你的订阅用 Subscription Proxy。
快速上手
- 登录 Provider(一次性):
hermes portal
浏览器走 Nous Portal OAuth,refresh token 存 ~/.hermes/auth.json。
- 启动 proxy:
hermes proxy start
输出:
Starting Hermes proxy for Nous Portal
Listening on: http://127.0.0.1:8645/v1
Forwarding to: (resolved per-request from your subscription)
Use any bearer token in the client - the proxy attaches your real credential.
- 把应用指向它。任何 OpenAI 兼容应用配置三件套:
Base URL: http://127.0.0.1:8645/v1
API key: anything (e.g. "sk-unused")
Model: Hermes-4-70B # 或 Hermes-4.3-36B, Hermes-4-405B
Proxy 忽略应用发的 Authorization 头,挂上你真实的 Portal 凭据发上游。bearer 快过期时自动刷新。
支持的 Provider
hermes proxy providers
目前内置 nous(Nous Portal)和 xai(xAI / Grok)。更多 OAuth Provider 可以通过实现 hermes_cli/proxy/adapters/ 里的 UpstreamAdapter 接口加。
允许的路径
Proxy 只转发上游真正服务的路径。Nous Portal 的:
| 路径 | 用途 |
|---|---|
/v1/chat/completions | 聊天补全(流式 + 非流式) |
/v1/completions | 旧版文本补全 |
/v1/embeddings | 嵌入 |
/v1/models | 模型列表 |
其他路径(/v1/images/generations、/v1/audio/speech 等)返回 404 带清晰错误,指向允许的路径。防止乱七八糟的客户端往上游漏奇怪请求。
配 Karakeep 之类应用
Karakeep 接受 OpenAI 兼容 API 做书签摘要。在它的 .env 里:
OPENAI_API_BASE_URL=http://127.0.0.1:8645/v1
OPENAI_API_KEY=any-non-empty-string
INFERENCE_TEXT_MODEL=Hermes-4-70B
同样的模式适用于 Open WebUI、LobeChat、NextChat 或任何其他 OpenAI 兼容客户端。
WarningProxy 默认绑
127.0.0.1(仅本机)。要让局域网其他机器用:hermes proxy start --host 0.0.0.0 --port 8645注意:网络上任何人都能用你的 Portal 订阅。Proxy 自己没鉴权—它接受任何 bearer。暴露到可信网络之外时,用防火墙、VPN 或带正确鉴权的反向代理。
多 Profile 网关路由
结合第 04 章的 Profile 体系,你可以给不同 Profile 配不同的主模型 + Fallback 链,再让网关按用户/频道路由到对应 Profile。这是多团队、多场景共享一个 Hermes 实例的标准玩法。
典型场景:一台服务器跑一个 Hermes 网关,给三个团队用—A 团队走便宜模型省钱,B 团队走 Opus 拼质量,C 团队走本地 Ollama 零成本。每个团队一个 Profile,各自配自己的 model 和 fallback_providers,网关层按 Telegram 群组或 Discord 频道路由。
NoteNous Portal 的 refresh token 在所有 Profile 之间通过共享 token store 自动共享。在任一 Profile 登录一次,其他 Profile 自动拿到。多用户共享一台机器时,每个用户有自己的 Portal 账号 -> 每个家目录有自己的
~/.hermes/auth.json-> 用户之间不共享 token。这是正确的边界。
委托的 Provider 覆盖
delegate_task 派生的子智能体继承父级的 fallback 链。但你也可以给所有子智能体统一指定一个不同的主 provider:model,做成本优化:
delegation:
provider: "openrouter" # 覆盖所有子智能体的 Provider
model: "google/gemini-3-flash-preview" # 覆盖模型
# base_url: "http://localhost:1234/v1" # 或用直连端点
# api_key: "local-key"
主智能体用 Opus 思考,子任务用 Flash 跑—这是常见的省钱姿势。
定时任务的 Provider 覆盖
定时任务创建智能体时继承你配的 fallback_providers 链。要给某个 cron 任务用不同的主 Provider,在任务上配 provider 和 model 覆盖:
cronjob(
action="create",
schedule="every 2h",
prompt="Check server status",
provider="openrouter",
model="google/gemini-3-flash-preview"
)
这一章讲的是「怎么让多个 Provider 协同、扛住故障」。下一章我们钻进 Hermes Agent 的内核—Agent 循环(Agent Loop),看一条用户消息是怎么在模型、工具、记忆之间流转的,以及 iteration-budget、bounded-response 这些机制怎么防止智能体跑飞。