Memory:对话越来越长,为什么不崩

背景

Claude 有 token 上限(约 200k)。复杂任务跑久了,对话历史会撑爆上下文窗口。三种策略应对这个问题。

阈值体系

上下文窗口(例如 200k)
  └── 减去输出预留(20k)= 有效窗口(180k)
        ├── 再减缓冲区(13k)= 自动压缩触发阈值(167k)
        ├── 再减(20k)= 警告阈值(显示 token 余量警告)
        └── 接近极限 = 阻塞阈值(停止接受新输入)

三种策略

1. autoCompact — 自动压缩(主线策略)

  • 触发:token 用量达到自动压缩阈值
  • 做什么:用一个独立的 Claude API 调用,把历史对话总结成摘要,替换旧记录
  • 防滥用:最多连续失败 3 次就停止(circuit breaker)
    • 原因:统计显示有 1,279 个会话连续失败 50 次以上,每天浪费约 25 万次 API 调用
  • 用户可控:可通过设置或 DISABLE_AUTO_COMPACT 环境变量关闭

2. reactiveCompact — 响应式压缩(错误恢复)

  • 触发:遇到特定 API 错误(max_output_tokens 输出被截断、prompt_too_long 输入超限)
  • 做什么:收到错误时不暴露给用户,先压缩上下文再重试,用户无感知
  • 状态:Feature Flag REACTIVE_COMPACT,是 autoCompact 的补充

3. contextCollapse — 上下文折叠(实验性激进方案)

  • 触发:Feature Flag CONTEXT_COLLAPSE 开启
  • 做什么:比压缩更激进,让 Claude 通过 CtxInspectTool 主动决定哪些内容可以折叠丢弃
  • 设计理念:让 Claude 主动管理自己的记忆,类似人类"选择性遗忘不重要细节"

三者关系

正常运行中
  → token 慢慢增长
    → 接近阈值 → [autoCompact] 总结历史,继续跑

遇到 API 报错
  → max_tokens / prompt_too_long → [reactiveCompact] 静默压缩后重试

上下文太复杂(实验)
  → [contextCollapse] Claude 自主决定折叠哪些内容