看不见的第二个模型:Hermes 的后台调用链该怎么配、怎么查

某个周一早上,你打开会话列表,发现昨天那场重要对话的标题是 {"title": "修复 CI 里...——一大串被截断的 JSON。另一次,你把主模型换成便宜的快速模型之后,命令审批开始变得不像话:明明无害的命令被拦、明显危险的却放行。还有人在本地跑 llama.cpp 时抱怨“压缩一次要等好几分钟”。这三件事看起来毫无关系,但它们的根往往是同一条你看不见的调用链——auxiliary(辅助)任务。
你每天在用、却从没配过的那条调用链
Hermes 的主模型并不是什么活都自己干。除了你看到的对话,它还会在后台发起一批小请求:
- 每个会话开始时起标题;
- 上下文快满时做总结压缩;
- 一条命令要不要拦下来问你,由一个小模型先分类;
- 你把截图丢给一个纯文本模型时,是另一个视觉模型在描述图片;
- 技能库检索、记忆检索的查询改写、看板任务拆解、
/goal是否达成的判定、邮箱打分,以及每轮结束后那个自我复盘(存记忆 / 给技能打补丁)。
这些任务在代码里有明确的名字。截至 main,内置 13 个:
| 任务 key | 显示名 | 干什么 |
|---|---|---|
vision |
Vision | 图片/截图分析(纯文本主模型的描述器) |
compression |
Compression | 上下文摘要 |
approval |
Approval | 智能命令审批分类 |
mcp |
MCP | MCP 工具相关推理 |
title_generation |
Title generation | 会话标题 |
review |
Review | /review 评审子代理 |
memory_query_rewrite |
Memory query rewrite | 记忆检索的查询改写 |
tts_audio_tags |
TTS audio tags | Gemini TTS 的标签插入 |
skills_hub |
Skills hub | 技能搜索与安装 |
triage_specifier |
Triage specifier | 看板 Triage 便签扩写成规格 |
kanban_decomposer |
Kanban decomposer | 任务拆解成子任务图 |
profile_describer |
Profile describer | Profile 自动描述 |
curator |
Curator | 技能使用情况的复盘审视 |
另有 goal_judge(/goal 判定)、monitor(重要邮件打分)、background_review(回合后的自我改进分支)以及 MoA 的 moa_reference / moa_aggregator 两个槽位。它们默认继承你的主模型,所以你很可能一直是“一个模型扛下所有”,从未察觉这条链路的存在——直到它出问题。
配置结构:一个键决定它跑在哪个模型上
每个任务的配置块形状是一样的:
auxiliary:
# 全局旋钮
transient_retries: 2 # 同 provider 内的瞬时重试(超时/5xx/408),clamp 0..6
free_only: false # true = 后台流量绝不走付费的 OpenRouter 通道
openrouter_model: "" # 覆盖自动链上的 OpenRouter 模型
stream_only_base_urls: [] # 只接受流式请求的端点(HTTP 400 那类)
# 每个任务一个块,形状相同
title_generation:
enabled: true # 关掉就完全不起标题
model_upgrade_enabled: true # false = 永远保留即时推导出的标题,不调模型
provider: auto # auto = 继承主模型
model: "" # 留空 = 跟随 provider
prefer_fast_model: false # true = 用 provider 的快速档
language: "" # 标题用哪种语言
timeout: 30
approval:
provider: auto
model: "" # 建议指一个又快又便宜的模型
timeout: 30
curator:
timeout: 600 # 技能审视在推理模型上可能要跑几分钟
三个容易踩的点:
provider: auto是“继承主模型”的写法,不是一个真实 provider 名。要钉死某个便宜模型,就同时写provider和model。- 超时值不是随便填的:审批分类默认 30 秒(要快),
kanban_decomposer180 秒、curator600 秒(要够)。给本地模型配压缩时,120 秒往往不够——这正是“本地模型压缩慢”最常见的抱怨来源。 background_review有个额外的max_input_tokens: 600000,用来给自我复盘重放的输入总量封顶;如果你把复盘路由到别的模型上,它改走“紧凑摘要”而不是完整对话重放,大概便宜 3~5 倍。
改配置最省事的入口是模型菜单底部那一项:
hermes model # 菜单里选 “Configure auxiliary models...”
hermes config get auxiliary.title_generation # 看当前生效的值
插件也能注册自己的辅助任务,注册完会出现在同一个菜单里。配置前先 hermes config get <键> 看一眼——这些键仍在变动:本机上 v0.21.0 的 CLI 输出里,title_generation 还没有 model_upgrade_enabled 这一项,而 main 上已经有了。
9 月 17 日的一批修复:静默失败的十种长相
2026 年 9 月 17 日,仓库里合并了一整组针对这条链路的修复。它们的共同点是:失败不会弹错误,只会让你看到奇怪的症状。
| 你看到的现象 | 真正的原因 | PR |
|---|---|---|
| 标题偶尔是一段 JSON 碎片 | 标题生成把 reasoning_content 当正文,或存了被截断的 JSON |
#113960 |
| 标题一直生成不出来,重试不停 | 参数被一层层拒绝后没有恢复路径 | #113958 |
| 关掉模型升级后标题反而变空 | 应保留“即时推导”的标题,却被模型调用覆盖 | #113955 |
| 某个端点挂掉导致后台卡很久 | 自动标题该在一个超时窗口后放弃,并指明是哪个 endpoint 超时 | #113968 |
| 主模型配额用尽后整个后台链停摆 | fallback_providers 走查没有跳过配额受限的候选 |
#113965 |
| DeepSeek 上结构化输出报 400,随后 fallback 全废 | 400 不该杀掉 fallback 候选,也不该先付出一次注定失败的请求 | #113966 |
| xAI 的 OAuth 令牌过期后后台全挂 | 辅助调用应自动恢复,而不是直接失败 | #113964 |
| 自定义 provider 报静默 401 | key_cmd 是个可调用对象,在五个解析分支里被 .strip() 或把 repr 当 bearer token 发了出去 |
#114333 |
| ollama 的本地 base_url 不生效 | 该分支没有解析本地地址、也没真的调到本地服务 | #113961 |
/goal 判定失败,只给一句含糊的 judge error |
应该直接指出是 Nous 认证失败 | #113963 |
举个最有代表性的例子:自定义 provider 用 key_cmd 从密钥管理工具里动态取令牌(这是令牌会过期的场景下唯一正确的做法)。主链路一直把返回的可调用对象原样传下去,而辅助链路的五个分支假设“凭证一定是个字符串”,于是要么崩溃,要么把 <CommandTokenSource object at 0x…> 这个对象地址当成 bearer token 发给服务端——后者更糟,因为它不报错,只是安静地 401。
为什么这类 bug 特别难查
因为它们走的是“辅助”路径,设计上就允许降级:标题生成失败不该影响你聊天,审批分类超时应该放行或升级,压缩失败要有兜底。降级是正确设计,代价是失败被吞掉。你看到的是症状——标题奇怪、审批不智能、压缩慢——而不是一条错误信息。
所以有一条很实用的建议:给每个辅助任务单独钉一个又快又便宜的模型(尤其 approval 和 title_generation)。这样你换主模型、把主模型降级、或者主模型临时不可用时,这些后台行为保持稳定,排错时也能一眼分辨“是我的主模型变了,还是后台链路换了”。
版本状态
上面这些修复都是 2026 年 9 月 17 日合并进 main 的,目前最新的正式发布 v0.21.3(tag v2026.9.14,9 月 14 日)不包含它们。先看看你的版本:
hermes --version
hermes config get auxiliary.title_generation
如果你正在被其中某一条折磨(尤其是标题碎片和自定义 provider 的静默 401),hermes update 之后等下一个 tag 落地,或者先在本地跟 main 走。
延伸阅读:想让辅助调用少烧钱,先看推理强度(reasoning effort)怎么配;压缩频繁触发也是同类问题,见压缩与精简尾部;本地模型做辅助任务时的超时与端口细节在本地 llama.cpp 指南里;而新 provider 接入讲的是把 aux 任务挂到新供应商上时要注意的事。
小结
- Hermes 有一条后台调用链(auxiliary),包含 13 个内置任务加 MoA/复盘等槽位,默认全部继承主模型。
- 每个任务一个同形状的配置块:
provider/model/base_url/api_key/timeout/extra_body/reasoning_effort;provider: auto就是“跟随主模型”。 - 想省钱又稳定:给
approval、title_generation这类高频小任务钉便宜模型;给本地模型的compression调高 timeout。 - 9 月 17 日合并的一批修复解决了十种“静默失败”(标题碎片、配额 fallback 卡住、自定义 provider 静默 401 等),尚未进入任何正式发布。