凭证池
凭证池允许你为同一服务商注册多个 API 密钥或 OAuth 令牌。当某个密钥达到速率限制或账单配额时,Hermes 会自动切换到下一个健康的密钥——在不更换服务商的情况下保持会话持续运行。
这与 备用服务商 不同:后者会切换到另一个服务商。凭证池是同服务商轮换;备用服务商则是跨服务商故障转移。系统会优先尝试池内密钥——只有当所有池内密钥均耗尽后,才会激活备用服务商。
工作原理
Your request
→ Pick key from pool (round_robin / least_used / fill_first / random)
→ Send to provider
→ 429 rate limit?
→ Retry same key once (transient blip)
→ Second 429 → rotate to next pool key
→ All keys exhausted → fallback_model (different provider)
→ 402 billing error?
→ Immediately rotate to next pool key (24h cooldown)
→ 401 auth expired?
→ Try refreshing the token (OAuth)
→ Refresh failed → rotate to next pool key
→ Success → continue normally
快速入门
如果你已在 .env 中设置过 API 密钥,Hermes 会自动将其识别为一个仅含单个密钥的凭证池。要享受池化带来的优势,请添加更多密钥:
# Add a second OpenRouter key
hermes auth add openrouter --api-key sk-or-v1-your-second-key
# Add a second Anthropic key
hermes auth add anthropic --type api-key --api-key sk-ant-api03-your-second-key
# Add an Anthropic OAuth credential (Claude Code subscription)
hermes auth add anthropic --type oauth
# Opens browser for OAuth login
查看你的凭证池状态:
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 且不带子命令时,将启动交互式向导:
hermes auth
该界面会显示你完整的池状态,并提供菜单选项:
What would you like to do?
1. Add a credential
2. Remove a credential
3. Reset cooldowns for a provider
4. Set rotation strategy for a provider
5. Exit
对于同时支持 API 密钥和 OAuth 的服务商(如 Anthropic、Nous、Codex),添加流程会询问你选择哪种类型:
anthropic supports both API keys and OAuth login.
1. API key (paste a key from the provider dashboard)
2. OAuth login (authenticate via browser)
Type [1/2]:
CLI 命令
| 命令 | 说明 |
|---|---|
hermes auth | 交互式凭证池管理向导 |
hermes auth list | 显示所有凭证池及其中的凭证 |
hermes auth list <provider> | 显示特定服务商的凭证池 |
hermes auth add <provider> | 添加一个凭证(会提示选择类型和密钥) |
hermes auth add <provider> --type api-key --api-key <key> | 非交互式添加 API 密钥 |
hermes auth add <provider> --type oauth | 通过浏览器登录方式添加 OAuth 凭证 |
hermes auth remove <provider> <index> | 根据从 1 开始的索引移除凭证 |
hermes auth reset <provider> | 清除所有冷却/耗尽状态 |
轮换策略
可通过 hermes auth → “设置轮换策略” 或在 config.yaml 中配置:
credential_pool_strategies:
openrouter: round_robin
anthropic: least_used
| 策略 | 行为 |
|---|---|
fill_first(默认) | 一直使用第一个健康的密钥,直到其耗尽,再切换至下一个 |
round_robin | 均匀循环使用各密钥,每次选择后进行轮换 |
least_used | 总是选择请求次数最少的密钥 |
random | 在健康密钥中随机选择 |
错误恢复机制
凭证池对不同错误的处理方式如下:
| 错误 | 行为 | 冷却时间 |
|---|---|---|
| 429 速率限制 | 尝试重试当前密钥一次(瞬态错误)。连续两次 429 后切换至下一密钥 | 1 小时 |
| 402 账单/配额超限 | 立即切换至下一密钥 | 24 小时 |
| 401 认证已过期 | 先尝试刷新 OAuth 令牌。若刷新失败才进行轮换 | — |
| 所有密钥均已耗尽 | 若已配置,则进入 fallback_model | — |
has_retried_429 标志会在每次成功调用 API 后重置,因此单次瞬态 429 不会触发轮换。
自定义端点凭证池
自定义 OpenAI 兼容端点(如 Together.ai、RunPod、本地服务器)拥有独立的凭证池,以 config.yaml 中的端点名称作为池键。
当你通过 hermes model 设置自定义端点时,系统会自动生成类似 "Together.ai" 或 "Local (localhost:8080)" 的名称。该名称将成为池的键名。
# After setting up a custom endpoint via hermes model:
hermes auth list
# Shows:
# Together.ai (1 credential):
# #1 config key api_key config:Together.ai ←
# Add a second key for the same endpoint:
hermes auth add Together.ai --api-key sk-together-second-key
自定义端点的凭证池存储于 auth.json 下,键名为 credential_pool,并带有 custom: 前缀:
{
"credential_pool": {
"openrouter": [...],
"custom:together.ai": [...]
}
}
自动发现
Hermes 会从多个来源自动发现凭证,并在启动时填充凭证池:
| 来源 | 示例 | 是否自动填充? |
|---|---|---|
| 环境变量 | OPENROUTER_API_KEY, ANTHROPIC_API_KEY | 是 |
| OAuth 令牌(auth.json) | Codex 设备码、Nous 设备码 | 是 |
| Claude Code 凭证 | ~/.claude/.credentials.json | 是(Anthropic) |
| Hermes PKCE OAuth | ~/.hermes/auth.json | 是(Anthropic) |
| 自定义端点配置 | model.api_key 在 config.yaml 中 | 是(自定义端点) |
| 手动添加项 | 通过 hermes auth add 添加 | 保存在 auth.json 中 |
自动填充的条目会在每次加载池时更新——如果你删除了一个环境变量,其对应的池条目也会自动清理。而手动添加的条目(通过 hermes auth add 添加)不会被自动清理。
委托与子代理共享
当代理通过 delegate_task 派生子代理时,父代理的凭证池会自动共享给子代理:
- 相同服务商 —— 子代理获得父代理的完整凭证池,可在速率限制下实现密钥轮换
- 不同服务商 —— 子代理加载该服务商自身的凭证池(如果已配置)
- 未配置凭证池 —— 子代理回退到继承的单一 API 密钥
这意味着子代理可获得与父代理相同的速率限制容错能力,无需额外配置。基于任务的凭证租赁机制确保子代理在并发轮换时不会相互冲突。
线程安全
凭证池对所有状态变更操作(select()、mark_exhausted_and_rotate()、try_refresh_current()、mark_used())均使用线程锁保护。这确保了当网关同时处理多个聊天会话时,仍能安全地并发访问。
架构设计
有关完整的数据流图,请参见仓库中的 docs/credential-pool-flow.excalidraw。
凭证池集成在服务商解析层:
agent/credential_pool.py—— 池管理器:负责存储、选择、轮换和冷却逻辑hermes_cli/auth_commands.py—— CLI 命令与交互式向导hermes_cli/runtime_provider.py—— 支持池感知的凭证解析run_agent.py—— 错误恢复:429/402/401 → 池轮换 → 备用服务商
存储机制
池状态存储于 ~/.hermes/auth.json 中,键名为 credential_pool:
{
"version": 1,
"credential_pool": {
"openrouter": [
{
"id": "abc123",
"label": "OPENROUTER_API_KEY",
"auth_type": "api_key",
"priority": 0,
"source": "env:OPENROUTER_API_KEY",
"access_token": "sk-or-v1-...",
"last_status": "ok",
"request_count": 142
}
]
},
}
轮换策略存储于 config.yaml(而非 auth.json):
credential_pool_strategies:
openrouter: round_robin
anthropic: least_used