最近在深度使用Hermes Agent中发现一个怪事:我的 Hermes Agent 和 Claude Code 都接的同一个 LiteLLM 网关,底下是 AWS Bedrock 的 Claude 模型。Hermes 用 Sonnet 4.6,Claude Code 用 Opus 4.7,按定价 Opus 比 Sonnet 贵 67%,但 4 天账单下来,Hermes 每次请求比 Claude Code 贵了将近一倍。
直觉告诉我不对,决定认真查一下。
先看数据
从 LiteLLM spend logs 里拉了两个 key 的数据,统计区间是 2026-04-25 到 04-26:
| 指标 | hermess-agent | claude-code |
|---|---|---|
| 使用模型 | Claude Sonnet 4.6 | Claude Opus 4.7 |
| 总请求数 | 1,043 次 | 156 次 |
| 平均单次费用 | $0.1490 | $0.0818 |
| 平均输入 token | 47,378 | 94,108 |
| 平均输出 token | 457 | 361 |
| 缓存读取 token | 0 | 89,613 |
| 缓存命中率 | 0% | 95.1% |
Hermes 的输入 token 比 Claude Code 少一半,但单次费用贵了近一倍。缓存命中率:0% vs 95%。一眼就看出来问题在哪了。
为什么命中率是 0%
看 LiteLLM 日志里的 request_tags:
json
// Hermes
"request_tags": [
"Credential: AWS-AK/SK",
"User-Agent: OpenAI",
"User-Agent: OpenAI/Python 2.32.0"
]
// Claude Code
"request_tags": [
"Credential: AWS-AK/SK",
"User-Agent: claude-cli",
"User-Agent: claude-cli/2.1.114 (external, claude-vscode, agent-sdk/0.2.114)"
]
Hermes 用的是 OpenAI Python SDK 在发请求。走 OpenAI 兼容格式(/v1/chat/completions),请求里根本没有 cache_control 字段,缓存从来没有触发过。
但 Hermes 明明支持 Prompt Cache------翻它的源码 run_agent.py,有一个 _anthropic_prompt_cache_policy 方法,专门决定是否对这次请求启用缓存。问题是它的判断前提:它必须先"认出"对面是 Anthropic 协议,才会走缓存分支。
根因:api_mode 没配
我当时的配置是这样的:
yaml
# ~/.hermes/config.yaml
model:
default: claude-sonnet-4-6
provider: custom
base_url: https://api.huancode.com/v1
api_key: ${HUNCODE_API_KEY}
provider: custom 加上普通的 base_url,Hermes 内部的 determine_api_mode 方法拿到这个 URL,不命中任何特殊规则,返回 chat_completions------OpenAI 兼容模式。后续缓存逻辑判断 is_anthropic_wire = False,直接返回 (False, False),cache_control 从未被加到请求里。
LiteLLM 那边其实完整支持 /v1/messages(Anthropic 原生格式),但 Hermes 压根没用这个端点。
解决方法:加一行 api_mode
yaml
# ~/.hermes/config.yaml
model:
default: claude-sonnet-4-6
provider: custom
base_url: https://api.huancode.com
api_key: ${HUNCODE_API_KEY}
api_mode: anthropic_messages # ← 这一行
两处变化:base_url 去掉了末尾的 /v1,以及显式加了 api_mode: anthropic_messages。
从日志里可以验证效果。修复前,LiteLLM 收到的 request_tags 是:
json
["Credential: AWS-AK/SK", "User-Agent: OpenAI", "User-Agent: OpenAI/Python 2.32.0"]
修复后变成:
json
["Credential: AWS-AK/SK", "User-Agent: Anthropic", "User-Agent: Anthropic/Python 0.96.0"]
协议切换之后,缓存开始命中。以下是一次完整会话的连续请求记录:
ini
14:51:38 cache_read= 0 cache_write=23,686 ← 第一轮,写入缓存
14:51:40 cache_read=23,686 cache_write= 4,031
14:51:45 cache_read=27,717 cache_write=10,721
14:52:13 cache_read=38,438 cache_write= 822
14:52:17 cache_read=39,260 cache_write= 1,162
14:52:20 cache_read=40,422 cache_write= 292
14:52:28 cache_read=40,714 cache_write= 853
每轮对话的历史内容持续命中缓存,只有新增部分触发少量写入,和 Claude Code 的命中模式完全一致。
api_mode 是什么
api_mode 是 Hermes 决定用哪种 wire protocol 跟 API 通信的配置项。有四个合法值:
chat_completions(默认) 走 OpenAI /v1/chat/completions 格式。几乎所有第三方 API 都兼容,是 Hermes 的默认值。适用于 OpenAI、大多数国产模型、OpenRouter 等。
anthropic_messages 走 Anthropic /v1/messages 格式。使用原生 Anthropic SDK 发请求,支持 cache_control(prompt caching)、thinking block、extended output 等 Anthropic 专有特性。适用于:
api.anthropic.com(官方)- 兼容 Anthropic 协议的第三方网关(如本例中的 LiteLLM)
codex_responses 走 OpenAI /v1/responses 格式(Responses API)。用于 OpenAI Codex、xAI Grok 等支持 Responses API 的端点。
bedrock_converse 走 AWS Bedrock Converse API 格式。直接对接 AWS Bedrock,不走任何兼容层。
自动推断逻辑
如果不写 api_mode,Hermes 会根据 base_url 自动推断:
| base_url 包含 | 自动推断为 |
|---|---|
api.anthropic.com |
anthropic_messages |
openai.com/v1/responses |
codex_responses |
| 其他 | chat_completions |
provider: custom 的情况不在自动推断范围内,所以即使 LiteLLM 完整支持 /v1/messages,Hermes 也不知道------必须手动写 api_mode: anthropic_messages。
这是文档里没有特别强调的一个坑:custom provider 时,api_mode 的默认行为是 chat_completions,不会自动探测对端协议能力。
成本影响有多大
把缓存打开之后按同等命中率估算,每次请求从 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0.149 降到大约 0.149 降到大约 </math>0.149降到大约0.039,整体节省约 73%。
两天的 1043 次请求,如果缓存一直是生效的,总费用从实际的 <math xmlns="http://www.w3.org/1998/Math/MathML"> 153 降到约 153 降到约 </math>153降到约42,差了 $111。
用框架接模型时,协议模式的选择比框架选型本身更重要。Hermes 支持缓存,但只对它"认识的" Anthropic 协议生效。默认配置走了 OpenAI 兼容模式,缓存悄悄关掉了,账单悄悄涨上去,不认真看日志根本发现不了。
数据来源:LiteLLM spend logs,统计区间 2026-04-25 至 2026-04-26,基础设施:Bedrock (us-east-1)。