你已经在为不需要的思考付钱:Hermes 的每个模型选择器现在都能顺手设推理强度


晚上十一点半,你让 Hermes 把 utils/date.ts 里的 formatTs 改名成 formatTimestamp。它交活了,全程七秒;账单上却是按 ultra 推理强度计费的几千个思考 token。问题不在模型笨,而在“ultra”这个档位是你上周为一次架构评审临时设的全局默认,之后再没人动过。Hermes 一直支持调推理强度,只是入口散落在 /reasoning、config.yaml 和 Desktop 一个需要悬停才能看到的子菜单里——知道它存在的人和真正去调的人,是两个集合。

PR #110346 之后这个局面变了:每一个模型选择器都会在选完模型之后,紧接着问你一句“这个模型用多大推理强度”。这篇把六个入口(一次性运行、会话、/model 参数、选择器、hermes model、Desktop)和它们的优先级一次讲清,让你按任务付钱,而不是按上周的心情付钱。

推理强度到底控制什么

推理强度(reasoning effort)指的是:模型在给出最终回答之前,允许它花多少“思考预算”——对支持该能力的模型,这一档会被翻译成请求里的 thinking / reasoning 参数。

Hermes 里这套档位的唯一真源是 hermes_constants.py 里的这一行:

VALID_REASONING_EFFORTS = ("minimal", "low", "medium", "high", "xhigh", "max", "ultra")

从低到高一共七档。parse_reasoning_effort() 负责把用户输入的单词翻译成配置字典:认得的档位返回 {"enabled": True, "effort": 档位},空字符串或识别不出的词返回 None(表示“交给提供商的默认值”),而 none 是明确的关闭——把 reasoning_effort 写成 false 或 none,意思就是“别思考”。

关键点在于:这是逐请求生效的。同一个模型,low 和 ultra 在质量上的差距,往往远小于它们在延迟和费用上的差距。所以真正要回答的问题不是“哪个档位最好”,而是“这件事值几个档位”。

生效范围 怎么设 落到哪里
只影响这一次运行 hermes --reasoning low -z "..." 本次进程,不写配置
当前会话(经典 CLI) /reasoning high 会话级覆盖,不动配置
当前会话(换个模型顺带设) /model X --reasoning high 会话级固定
你的全局默认 /reasoning high --global agent.reasoning_effort
指定模型的强度 agent.reasoning_overrides 按模型名匹配,覆盖全局默认
某一个辅助任务 hermes model → Configure auxiliary models… auxiliary.vision.reasoning_effort
所有子代理 delegation.reasoning_effort delegation 配置块
Desktop 里的某个会话 composer 上的强度药丸 那个会话

下面按“从最小作用域到最大”的顺序逐个讲。

最小的一环:给一次性运行设强度

如果你只是跑一条命令、做一件明确的小事,根本不需要碰配置。Hermes 顶层就有 --reasoning 参数:

# 一次性任务:改个名字,low 完全够用
hermes --reasoning low -z "把 utils/date.ts 里的 formatTs 重命名为 formatTimestamp"

# 开着交互式会话,但这一场会议纪要整理想让它多想一点
hermes chat --reasoning high

按帮助文本的说法,这个参数“只覆盖本次运行”,持久化的档位仍然在 config.yaml 里(或者按模型写在 agent.reasoning_overrides 下)。如果你在写 shell 脚本、跑 CI、批量处理文件,这是最干净的做法:强度跟着命令走,不会在某天忘记的时候变成你的新默认值。

会话级:/reasoning 与 /model --reasoning

在经典 CLI 里,/reasoning 是专职管这件事的命令——它同时管强度档位和推理过程的显示方式:

/reasoning high           # 本会话改用 high
/reasoning high --global  # 写进 agent.reasoning_effort,以后都是 high
/reasoning                # 不带参数:交互式选择
/reasoning show           # 显示/隐藏推理过程(hide 相反,full 展开全部)
/reasoning none           # 关掉推理

而 PR #110346 做的事,是让“换模型”和“设强度”变成同一个动作。/model 的解析器现在接受 --reasoning <档位>,一次请求同时携带模型和强度:

/model openrouter/anthropic/claude-opus-4.8 --reasoning high

几个常被问到的细节:

  • 作用域跟选择器一致:默认只作用于当前会话;加 --global 才会写进 agent.reasoning_effort;--once(只这一轮)会先拍快照、轮次结束后还原。
  • 强度是在模型切换完成之后才应用的。这不是实现细节上的吹毛求疵:切换模型的流程会重新解析一遍 reasoning_config,如果先写强度,反而会被覆盖掉,所以顺序是被刻意安排成这样的。
  • 写错档位会明确报错,而不是被悄悄忽略:/model --reasoning turbo 会返回 /model --reasoning takes none, minimal, low, medium, high, xhigh, max or ultra.

不用记参数也行——交互式 /model 选择器现在有三个阶段:选提供商、选模型、然后是第三个阶段 Reasoning effort for <模型名>,列出完整档位、一个 none(关闭推理)和一个“保持当前值”。如果能力表明确说这个路由没有推理开关,这一阶段会直接隐藏,不会给你一个没用的旋钮。

Ink TUI(hermes --tui)里是同一套三阶段流程,第三阶段的标题就是 Reasoning effort (step 3/3),选完会把整条命令交给网关:模型、提供商、强度、作用域一起下发。消息网关(Telegram、Slack 等平台)里的 /model --reasoning 走的是和 /reasoning 完全相同的应用逻辑,因此行为一致——这是有意的复用,免得两条路径将来各自漂移。

hermes model:主模型和每一个辅助任务都会问你一次

hermes model 是最常用的入口(命令详情见 hermes model 参考)。PR #110346 之前,它只对 Copilot 路由内联问一次强度,其余路由直接跳过;辅助任务那条路上则是完全没有。

现在:

  • 主模型选完之后,会紧跟一步共享的强度选择:七档、Disable reasoning、以及 Skip(保留当前值)。目录里标记为“没有推理控制”的模型会跳过这一步。
  • 提供商列表里多了一行 Reasoning effort for the current model…,只想改强度、不想换模型的时候用它。
  • Configure auxiliary models… 这条流程(先选提供商、再选模型)最后也接上了同样的强度步骤,多一个 Provider default 选项表示“交给提供商自己决定”。这些值写进 auxiliary.<任务>.reasoning_effort,delegation 则写进 delegation.reasoning_effort。
  • 任务列表里会直接把强度显示出来,形如 openrouter · model · high;Reset all to auto 会把这些覆盖一次性清空。
  • 设计上就没有这个键的块(MoA 的槽位、memory_query_rewrite)会跳过这一步——它们不是被漏掉了,而是各有各的配置位置。

这一步的现实意义:压缩历史、起标题、判断审批这类任务,是每一轮对话都会跑的。把它们从 high 降到 minimal 或直接关掉,省下来的钱比你精打细算主模型的那一档要多得多。

Desktop:composer 上的强度药丸 + Settings 里的按任务强度

桌面端的模型选择器在 composer 里、麦克风左边。PR #109907 之前,强度只藏在模型行的悬停子菜单里——从截图看,那颗胶囊写着 Deepseek V4 Flash · Med,用户读成了“这个模型只会 medium”,然后永远没找到子菜单。

现在它有了自己的位置:

  • composer 上多了一颗推理强度药丸,显示当前这个会话实际生效的档位(会话值优先,否则是配置默认),点开就是 Thinking / Fast / Effort 几行,不用再去模型行里翻。当目录明确说该模型没有推理控制时它会隐藏;能力未知时它保持显示——宁可短暂多一个旋钮,也不要在目录拉取期间闪来闪去。
  • 模型胶囊本身不再把强度拼进标签里,两个控件各说各的事。

而“按任务设强度”这件事在桌面端补齐是两步:PR #110435 给 Settings → Model → Auxiliary models 里每一行加了 Reasoning 下拉框,选项是 inherit · main model effort(默认,表示删掉该任务的覆盖)、Off、或直接选一个档位,写入的键和 hermes model 完全一样(auxiliary.<任务>.reasoning_effort)。这里有个不太显眼但重要的契约:这个字段省略表示不动,显式 null 表示清除(回继承),给档位表示设定;传个 turbo 进去会收到 400,并列出所有合法值——而不是静默丢弃。

PR #110535 则把辅助模型的固定关系在桌面端收口:11 个槽位都能固定,而且你在 Settings 里保存别的设置时,这些固定不会被无关的写入顺手清掉或改写(配置写入改成按节合并,而不是把整份展开后的快照写回去);当审查任务被路由到另一个模型时,它的推理强度会真正生效,而不是被父级会话的设置吞掉。

一个容易踩的点:桌面端会给每个模型记住自己的强度/快速度预设,这是本机便利,不改变 cron 和子代理。想让计划任务也变,得走 hermes model 或配置文件那一层。

这些层怎么叠加,以及 model_overrides 在其中的位置

优先级从高到低(这也是运行时的解析顺序):会话级覆盖(/reasoning --session、/model --reasoning)→ 按模型覆盖 agent.reasoning_overrides → 全局默认 agent.reasoning_effort。一份能直接抄的 config.yaml:

agent:
  # 全局默认;/reasoning high --global 写的就是这一行
  reasoning_effort: medium
  # 按模型覆盖:键支持各种写法(精确名、点/横线互换、可省提供商前缀),先匹配到的生效
  # 注意:这个键没有 `hermes config set` 支持,请直接编辑 YAML
  reasoning_overrides:
    "openrouter/anthropic/claude-opus-4.8": high
    "deepseek/deepseek-v4-flash": low

auxiliary:
  compression:
    reasoning_effort: minimal   # 压缩对话摘要,不需要深思
  title_generation:
    reasoning_effort: none      # 起标题,直接关掉
  vision:
    reasoning_effort: ""        # 空字符串 = 用提供商默认

delegation:
  reasoning_effort: low         # 子代理;留空 = 继承父级

model_overrides 在这里扮演的是另一个角色(详见 model_overrides 指南):它是模型元数据的地方,声明 context_window、supports_vision、supports_reasoning 这类能力。它不直接设强度,但决定了强度这块旋钮存不存在——能力表说某路由没有推理控制时,hermes model 的强度步骤、/model 选择器的第三阶段、Desktop 的药丸会一起隐藏。所以如果你接的是本地模型或刚发布的模型、目录里没有它,先把能力声明对了,这些入口才会出现。

两个值得记住的例外:

  • background_review.reasoning_effort 在审查留在主模型上时是被忽略的——那种情况下分叉进程会原样继承会话的推理配置,以保住父级 prompt 缓存的字节前缀。要做到“另一个模型 + 另一个强度”,就得同时给它配 provider/model;否则 Hermes 会明确警告你一次。
  • MoA 的强度不在 aux 块里,而在预设内部按槽位配:moa.presets.<名字>.reference_models[].reasoning_effort 和 aggregator.reasoning_effort。每个参考模型可以不同深度,聚合器可以更深——这也是为什么 MoA 块本身没有 reasoning_effort 键。

一套能立刻见效的起手配置

如果你不想研究完整优先级,只想要一个省钱的起点,照这个顺序做:

  1. 主模型留 medium,需要深想时用 /model ... --reasoning high 临时抬一档,而不是改全局。
  2. hermes model → Configure auxiliary models…,把 compression 调到 minimal、title_generation 关掉。这两个是每轮都跑的常客,收益最直接。
  3. delegation.reasoning_effort: low:子代理通常在执行已经拆解好的任务,不需要再自己规划一遍。
  4. 一次性任务和脚本用 hermes --reasoning low -z "...",别让自己的默认值被临时需求带偏。
  5. 长任务如果卡在别的地方,先看看是不是轮次上限的问题,而不是无脑加推理强度——max_turns 指南讲的就是这个;同理,免密钥起步那篇里的免费档模型,配 minimal 反而更划算。

一句话总结

推理强度不是“越高越好”的调优项,而是一个作用域问题:同一份 config.yaml 里,会话级、按模型、按辅助任务、按子代理各有各的旋钮。PR #110346 把这些旋钮搬到了你本来就在用的选择器上;剩下的只是决定哪件事值几个档位——别再让上个月的一次评审,替这个月的改名任务付 ultra 的账。