Hermes 的 config.yaml 我逐行调了一遍——这 3 个设置改完,长任务再也不卡了

用 Hermes Agent 处理复杂任务时,最崩溃的不是模型答错,而是做到一半突然卡住:API 请求迟迟不回、上下文一长就开始压缩报错、工具调用反复 retry 停不下来。这些问题多半不是模型本身的问题,而是 ~/.hermes/config.yaml 里的默认参数没有针对长任务调优。
我把自己跑了上百个长任务后的配置逐行过了一遍,最后发现真正决定长任务是否稳定的,其实只有 3 个设置。把它们从默认改成适合自己的值,复杂任务基本都能稳跑到终点。
下面按「症状 → 原因 → 改法 → 推荐值」的顺序展开,每一节都附带可直接复制的 YAML 片段。
设置 1:给 API 请求戴上「紧箍咒」—— providers 超时
症状
- 任务跑到 50%,终端突然没动静,光标闪了半分钟才回一句「Connection timed out」。
- 后台任务(cron/background)明明显示 running,但日志里久久没有新工具调用。
- 换了 OpenRouter 的某个 provider 后,响应忽快忽慢,偶尔直接假死。
原因
Hermes 的 API 调用默认走 HERMES_API_TIMEOUT(1800 秒)和 HERMES_API_CALL_STALE_TIMEOUT(90 秒)这两个环境变量。但 config.yaml 里 providers 块的 request_timeout_seconds 和 stale_timeout_seconds 优先级更高,未设置时才会回退到环境变量。
默认没有针对 provider 做区分,于是:
- 云端模型(如 Anthropic、OpenRouter)的慢思考响应可能没到 90 秒就被判 stale;
- 本地模型(LM Studio / Ollama)冷启动可能要几十秒,却被默认的 30 秒请求超时打断;
- 一个 provider 假死后,没有超时保护,agent 会死等。
改法
在 config.yaml 顶部添加 providers 块,按 provider 分别设置请求超时和 stale 超时:
providers:
anthropic:
request_timeout_seconds: 600 # Claude 长思考可容忍 10 分钟
stale_timeout_seconds: 300 # 非流式调用 5 分钟没动静才算 stale
openrouter:
request_timeout_seconds: 300
stale_timeout_seconds: 120
lmstudio:
request_timeout_seconds: 300 # 本地模型冷启动慢
stale_timeout_seconds: 900 # 本地端点默认关闭 stale 检测,这里显式恢复
ollama-local:
request_timeout_seconds: 300
stale_timeout_seconds: 900
如果你主要用某一个 provider,也可以只配一个 default 或具体 provider 名。request_timeout_seconds 会作为 timeout= 参数传给底层 SDK,直接覆盖环境变量。
推荐值
| 场景 | request_timeout_seconds | stale_timeout_seconds |
|---|---|---|
| 云端快速模型(Claude 3.5 Sonnet / GPT-4o mini) | 60–120 | 60–90 |
| 云端长思考模型(Claude Opus / o1 / deep-research) | 300–600 | 120–300 |
| 本地模型(LM Studio / Ollama / vLLM) | 180–300 | 600–900 |
| 后台任务 / cron | 300–600 | 120–300 |
注意:
stale_timeout_seconds只对非流式调用生效。流式调用(streaming)本身以 token 到达为活跃信号,不需要 stale 检测。
设置 2:别让上下文先撑爆——compression 压缩策略
症状
- 多轮工具调用后,模型开始答非所问,甚至直接提示「context length exceeded」。
- 压缩触发得太晚,等到 80% 窗口才压,已经把关键中间结果压丢了。
- 压缩后「失忆」——前面刚确认的 API 密钥、文件路径被总结吞掉。
原因
Hermes 的 compression 块会在 token 使用量达到 threshold × context_length 时自动压缩中间对话。但默认值不一定适合你的任务:
threshold: 0.50对 200K 上下文很激进,对 32K 上下文可能太晚;protect_last_n: 20只保留最近 20 条消息,长任务里可能只覆盖到 2–3 个关键回合;target_ratio: 0.20决定压缩后保留多少尾巴,太小会丢细节,太大等于没压。
官方代码里还有一条隐藏规则:上下文窗口低于 512K 的模型,threshold 会被强制 floor 到 0.75。也就是说,小窗口模型其实不会按 0.50 触发,而是 75% 才触发。知道这个规则,你才能正确估计压缩时机。
改法
compression:
enabled: true
threshold: 0.65 # 在 65% 窗口时触发压缩,比默认更提前
target_ratio: 0.25 # 保留 25% 的近期尾巴,平衡细节与空间
protect_last_n: 30 # 保留最近 30 条消息(约 15 个完整回合)
protect_first_n: 1 # 只保留系统提示 + 第一条用户消息,减少头锁死
codex_app_server_auto: native
如果你的任务需要反复回溯早期约定(比如「请始终用 Python 3.11」「项目用 pnpm」),可以把 protect_first_n 设到 3;如果早期约定不多,设 1 更省空间。
推荐值
| 模型上下文 | threshold | target_ratio | protect_last_n |
|---|---|---|---|
| ≤ 32K(Claude 3.5 Sonnet、GPT-4o) | 0.75(默认下限) | 0.25 | 30–40 |
| 128K–200K | 0.60–0.65 | 0.20–0.25 | 20–30 |
| ≥ 1M(Gemini、Kimi k1.5) | 0.50–0.55 | 0.15–0.20 | 20 |
提示:压缩总结模型默认走 Gemini Flash,速度快、成本低。如果你的任务涉及大量代码,可以固定到
auxiliary.compression配一个更懂代码的模型,但通常不必动。
设置 3:给工具循环上把锁——agent.max_turns
症状
- 一个简单任务,agent 调了 50 轮工具还在「我再确认一下」。
- 网络波动导致某个工具反复失败,agent 进入「retry 地狱」,账单蹭蹭涨。
- 后台任务跑了半小时,回头一看在死循环里。
原因
agent.max_turns 控制一次会话里 agent 最多能进行多少轮工具调用。默认 60 对日常问答够用,但遇到复杂调试、批量处理、循环确认时,60 轮可能不够;而如果没有上限,失败工具又可能无限 retry。
配合 tool_loop_guardrails 的 hard_stop_enabled,可以在异常失败时直接熔断,避免无效循环。
改法
agent:
max_turns: 100 # 复杂任务给足迭代空间
api_max_retries: 2 # 单次 API 出错最多重试 2 次,配合 fallback 更快切换
reasoning_effort: medium
tool_loop_guardrails:
warnings_enabled: true
hard_stop_enabled: true # 工具循环异常时直接熔断
warn_after:
exact_failure: 2
same_tool_failure: 3
idempotent_no_progress: 2
hard_stop_after:
exact_failure: 5
same_tool_failure: 8
idempotent_no_progress: 5
推荐值
| 任务类型 | max_turns | hard_stop_enabled |
|---|---|---|
| 日常问答 / 单步查询 | 30–40 | false |
| 代码调试 / 中等复杂度 | 60–80 | true |
| 批量处理 / 长时间后台任务 | 100–150 | true |
| 探索性研究 / 多文件重构 | 100–200 | true |
注意:
max_turns是单轮用户请求内的工具迭代上限,不是整个会话的消息上限。你可以通过/new或 Hermes v0.18 命令全景图 里的会话管理命令随时重启上下文。
完整参考配置:一份直接能用的 config.yaml 片段
把上面三个设置合在一起,适合「主要用 OpenRouter + 偶尔本地模型、经常跑长任务」的场景:
model:
default: "anthropic/claude-opus-4.6"
provider: "auto"
base_url: "https://openrouter.ai/api/v1"
providers:
anthropic:
request_timeout_seconds: 600
stale_timeout_seconds: 300
openrouter:
request_timeout_seconds: 300
stale_timeout_seconds: 120
lmstudio:
request_timeout_seconds: 300
stale_timeout_seconds: 900
compression:
enabled: true
threshold: 0.65
target_ratio: 0.25
protect_last_n: 30
protect_first_n: 1
codex_app_server_auto: native
codex_gpt55_autoraise: true
agent:
max_turns: 100
api_max_retries: 2
reasoning_effort: medium
tool_loop_guardrails:
warnings_enabled: true
hard_stop_enabled: true
warn_after:
exact_failure: 2
same_tool_failure: 3
idempotent_no_progress: 2
hard_stop_after:
exact_failure: 5
same_tool_failure: 8
idempotent_no_progress: 5
改完后保存到 ~/.hermes/config.yaml,新会话立即生效。已运行的会话需要 /new 重启才能读到新配置。
验证:改完真的有用吗?
三个简单验证方法:
- 故意触发长思考:让 agent 处理一个 500 行日志文件或一次性读取 10 个源码文件,看是否还会触发
context length exceeded。 - 模拟 API 抖动:用本地
timeout或iptables短暂阻塞 provider IP,观察 agent 是否会在配置的超时时间内失败并尝试 fallback,而不是死等。 - 查看压缩日志:长任务中执行
/compress或等待自动压缩,检查压缩后是否保留了关键的最近 30 条消息和系统提示。
如果你的任务类型更复杂,也可以参考我们关于 Hermes 错误处理与恢复机制 的深入解析,把超时、fallback、retry 做成一套组合拳。
总结
长任务卡住通常不是模型变笨了,而是 API 超时、上下文压缩、工具循环上限 三个环节没有协同好。调优后:
- 请求到 provider 不再死等,该超时超时,该 fallback fallback;
- 上下文在合理时机压缩,既不会太早丢细节,也不会太晚爆窗口;
- 工具调用有明确上限,避免 retry 地狱和账单失控。
如果你刚装好 Hermes,也可以先看 安装指南 把环境搭稳,再把这套配置作为「长任务专用模板」保存下来。下次遇到复杂任务,直接复制一份就能跑。
参考:本文基于 Hermes Agent 官方 cli-config.yaml.example 和 官方文档 整理。