一次「This conversation is too long」死循环排查:压缩摘要的 provider 缺 key
症状:每一轮都在同一句话上撞墙
用 Agent 写代码写到下午,对话突然开始反复报同一句:
This conversation is too long (266571 tokens)
诡异的是它并不是"报一次就结束",而是每轮结束都报,数字在 266517 和 266571 之间来回跳。看起来像在重试,但重试之后 token 数没降下来——说明压缩根本没生效。
继续往下翻日志,真正的原因写在另一行:
WARNING agent.context_compressor: compression aborted
failure_class=summary_auth_failure
summary_auth_failure ——摘要(summary)的鉴权失败。也就是说:上下文压缩这一步,调不动用来写摘要的模型。
先搞清楚压缩链路
Agent 的上下文管理和大多数人的直觉不一样:它不是一个"到上限就丢老消息"的滑动窗口,而是一个两段式过程:
- 上下文增长到阈值(我这里是 192k,留了余量给下一次响应)时,触发压缩;
- 压缩不是简单截断,而是把前面的对话交给一个辅助模型,让它写成一份结构化摘要,然后用摘要替换掉原始消息。
第 2 步是关键:压缩本身是一次模型调用。所以压缩能不能成功,取决于"辅助模型"这个 provider 配置得对不对。
我的配置里是这样:
auxiliary:
compression:
provider: clinepass # ← 名字在,但 key 不在
vision:
provider: clinepass
title_generation:
provider: clinepass
clinepass 这个 provider 的历史配置靠环境变量 CLINE_API_KEY。而这台机器上这个变量从来没设过——主对话用的是另一个走网关的 provider,独立配置、能用;辅助模型这一路是空壳。
于是就出现了最难受的形态:
- 主模型调用正常,对话能继续;
- 压缩触发 → 辅助模型找不到 key → 抛鉴权错误 → 被归类成
summary_auth_failure; - 因为压缩失败,会话仍然处于超限状态;
- 下一轮请求进来,又超限 → 再触发压缩 → 再失败。
死循环就这么形成了,而且每一轮都在消耗真实 token。
为什么它不直接报错停下
从设计上看,压缩失败时选择"不中断对话"是合理的:丢了压缩还能靠裁剪硬撑,中途终止对话反而更糟。但这里的问题是失败被降级成了警告,只写进日志,UI 上只看到"太长了"这种笼统提示。真正的 failure_class 藏在 logs/agent.log 里。
所以排查第一步永远是:别信 UI 上那句话,去日志里找 failure_class。
定位过程(3 分钟)
# 1. 看日志里真正的失败分类
grep -n "failure_class\|compression aborted" ~/.hermes/logs/agent.log | tail -20
# 2. 看辅助模型配的是哪个 provider
hermes config get auxiliary.compression.provider
hermes config get auxiliary.vision.provider
hermes config get auxiliary.title_generation.provider
# 3. 看那个 provider 的 key 到底在不在
hermes config get providers.<name> # 或直接看 .env 里对应的 *_API_KEY
第 3 步几乎一击即中:provider 名字写着一个需要 XXX_API_KEY 的服务,而环境里根本没有这个变量。
修复:把辅助模型指向一个真的能用的 provider
三个辅助用途(压缩、视觉、标题生成)都要换,别只改压缩——不然下次是图片识别或会话标题先炸。
hermes config set auxiliary.compression.provider custom:cpamp
hermes config set auxiliary.vision.provider custom:cpamp
hermes config set auxiliary.title_generation.provider custom:cpamp
custom:<name> 指向已经在 providers: 下配好 base_url 和 key 的网关,这样辅助调用和主对话共用同一套凭据,不会再出现"主能用、辅不能用"的割裂。
注意两点:
- 不要直接手改 config.yaml,Hermes 会拒绝写自己的配置文件,用
hermes config set; - 配置是热加载的,改完不需要重启进程,下一轮就生效。
改完验证一下:
# 用同样的模型和 key 打一次最小请求,确认不是空响应
curl -s -X POST "$BASE_URL/chat/completions" \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"model":"<你的模型>","max_tokens":300,"messages":[{"role":"user","content":"说 OK"}]}'
附带的第二个坑:推理模型的 max_tokens
第一版验证时我把 max_tokens 设成 40,直接收到:
HTTP 500: empty response content
不是鉴权问题,是推理模型(带 thinking 的那类)会先花 token 思考,40 个 token 全被思考吃掉,正文为空,网关就判成空响应。验证连通性时 max_tokens 至少给 300,不然会把"模型能通"误判成"模型不可用",绕一大圈。
已经在死循环里了怎么办
修复配置只对下一轮生效,当前这个会话已经超限且压缩失败,最简单的是开新会话;如果不想丢上下文,先把关键结论手抄出来,或者把摘要需要的材料导出成文件,在新会话里再喂回去。
复盘清单
| 检查项 | 说明 |
|---|---|
| 辅助模型独立可用 | 压缩/视觉/标题三个用途各配一个真实可用的 provider,别只配主模型 |
日志看 failure_class |
UI 的"太长了"没信息量,summary_auth_failure 才是线索 |
| token 数是否在反复横跳 | 反复出现且不下降 = 压缩一直失败,不是正常重试 |
| 配置改动用 CLI | 直接编辑 config.yaml 会被拒写 |
| 验证连通性别省 max_tokens | 推理模型给 40 会得到 empty response content |
这类问题的麻烦之处在于它伪装成"上下文太长",而实际是辅助链路的凭据问题。看到"压缩失败"四个字,第一反应应该是去查辅助 provider,而不是急着删历史消息。
- 点赞
- 收藏
- 关注作者
评论(0)