Last updated on

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.yamlproviders 块的 request_timeout_secondsstale_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_guardrailshard_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单轮用户请求内的工具迭代上限,不是整个会话的消息上限。你可以通过 /newHermes 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 重启才能读到新配置。


验证:改完真的有用吗?

三个简单验证方法:

  1. 故意触发长思考:让 agent 处理一个 500 行日志文件或一次性读取 10 个源码文件,看是否还会触发 context length exceeded
  2. 模拟 API 抖动:用本地 timeoutiptables 短暂阻塞 provider IP,观察 agent 是否会在配置的超时时间内失败并尝试 fallback,而不是死等。
  3. 查看压缩日志:长任务中执行 /compress 或等待自动压缩,检查压缩后是否保留了关键的最近 30 条消息和系统提示。

如果你的任务类型更复杂,也可以参考我们关于 Hermes 错误处理与恢复机制 的深入解析,把超时、fallback、retry 做成一套组合拳。


总结

长任务卡住通常不是模型变笨了,而是 API 超时、上下文压缩、工具循环上限 三个环节没有协同好。调优后:

  • 请求到 provider 不再死等,该超时超时,该 fallback fallback
  • 上下文在合理时机压缩,既不会太早丢细节,也不会太晚爆窗口
  • 工具调用有明确上限,避免 retry 地狱和账单失控

如果你刚装好 Hermes,也可以先看 安装指南 把环境搭稳,再把这套配置作为「长任务专用模板」保存下来。下次遇到复杂任务,直接复制一份就能跑。


参考:本文基于 Hermes Agent 官方 cli-config.yaml.example官方文档 整理。