Context Rot(上下文腐烂)
随着输入 token 增多,LLM 输出质量会持续下降——即便远没到上下文窗口上限。Chroma 实测 18 个前沿模型无一幸免。对编码 agent 而言,这是首要失败模式,比模型能力更关键。
什么是 Context Rot
Context Rot(上下文腐烂)指随着输入 token 增多,LLM 输出质量持续下降的现象——而且是在远没到上下文窗口上限时就开始下降。这个词由 Chroma 在 2025 年的技术报告中提出并系统验证:他们测了 18 个前沿模型(含 GPT-4.1、Claude 4、Gemini 2.5、Qwen3),没有一个例外,全都随输入变长而变差。
关键区分:Context Rot 不是上下文溢出。
| 上下文溢出 | Context Rot | |
|---|---|---|
| 触发 | 超过模型最大 token 上限 | 远未到上限就发生 |
| 表现 | 截断/拒绝,二元失败 | 渐进式质量下降 |
| 例子 | 200K 窗口塞 210K | 200K 窗口塞 50K 就开始退化 |
一个 200K 窗口的模型,可能在 50K token 时就出现明显退化。下降是连续的,不是悬崖式的。
为什么重要
很多团队的误区是「我选了 128K / 1M 上下文的模型,应该够用了」,然后塞满上下文却纳闷质量为什么变差。Context Rot 的核心洞察是:
容量是错误的指标,信噪比才决定输出质量。
对编码 agent 来说这尤其致命——Context Rot 是 agent 的首要失败模式,比模型能力、推理能力更关键。模型本身够聪明能解决问题,前提是上下文保持干净。但 agent 在搜索、探索、回溯过程中会不断累积噪声,这些噪声直接拖垮后续每一步的输出。
表现形式
Chroma 的研究揭示了几个非均匀退化的维度:
- needle-question 相似度:问题和目标信息的语义匹配度越低,越长的上下文越捞不出来(经典 NIAH 测的是字面匹配,掩盖了这个问题)
- 干扰项(distractors):上下文里有相似但无关的内容时,越长越容易被带偏
- lost-in-the-middle:信息放在长上下文中间时最容易被忽略,准确率可掉 30%+
- 噪声类型有别:互相抵消的操作(如成对的增删)比单纯的无关文本(如 print 语句)更严重地拖垮性能
社区的真实观察也印证这点:Claude Code 在多次 compaction 后会越来越差;与其让模型在长上下文里硬找,不如先让它总结、再基于总结提问、需要时再喂原文(RAG 式或简单 agent 循环)效果更好。
怎么缓解
Context Rot 是「上下文工程」存在的根本原因。常见手段:
- 控制信噪比:只放当前任务真正需要的内容,无关历史及时清掉
- RAG 检索:用 RAG 按需取相关片段,而不是一股脑全塞进去
- 总结压缩:长对话/长文档先总结,基于总结工作,需要细节时再拉原文
- 分而治之:把大任务拆成多个干净上下文的子任务(多 agent 编排实测比单 agent 提升显著)
- 主动清理:agent 探索产生的噪声(失败的尝试、无关的文件内容)用完就清,别让它留在上下文里腐烂
这些都属于 Context Engineering 的范畴。
对模型选型的启示
- 别只看上下文窗口大小:1M 窗口不等于能有效用满 1M。看的是模型在长上下文下的实际保持能力。
- 长上下文 ≠ 不需要 RAG:「长上下文会杀死 RAG」是误解。恰恰相反,Context Rot 证明了即便有大窗口,按需检索 + 控制信噪比仍是刚需。
- compaction 不是免费的:Claude Opus 4.5、GPT-5.1-Codex-Max 的 compaction 机制能延长工作时长,但每次压缩都可能损失信息,长任务质量仍会缓慢下滑。
常见误区
- 「上下文没满就没事」:错。退化在远未满时就开始,容量充足不代表质量稳定。
- 「换个更大窗口的模型就行」:错。Chroma 测的 18 个模型全部退化,换大窗口治标不治本,得做上下文工程。
- 「RAG 过时了」:错。Context Rot 反而强化了 RAG 和上下文工程的价值。
- 「把所有文档一次喂进去最省事」:短期省事,长期质量差。先总结后按需取原文通常更准。
真实案例
案例 1:Claude Code 多次 compaction 后遗忘早期决策
场景:用 Claude Code 做一个跨多文件的重构任务——迁移认证模块从 session 改为 JWT,涉及 10+ 文件、几百轮对话。任务跑到中后期触发多次 compaction。
表现:
- 前期约定的「所有 token 一律用
Bearer前缀」到后期开始出现不带前缀的代码 - 明确否决过的方案(如把 secret 写进 cookie)被重新提出
- 改一个文件时忘了另一个文件已经改过,产生重复修改或冲突
解决:把关键约定(命名规范、架构决策、已否决方案)写进 AGENTS.md 或单独的决策文件,让 agent 每次都能读到干净版本,而不是依赖 compaction 后的残缺记忆。详见 AGENTS.md。
案例 2:Cursor @codebase 在大型 monorepo 中召回质量下降
场景:50K+ 行的 monorepo,用 Cursor 的 @codebase 做跨包改动,比如「找到所有调用 PaymentService.charge 的地方并加一个幂等 key 参数」。
表现:
- 项目小时召回很准,项目一大就开始漏——明明有 8 处调用,只找到 5 处
- 召回的片段里混入大量相似但无关的代码(其他 Service 的方法、测试桩),干扰模型判断
- 回答越来越「看起来对但其实漏了」,比直接报错更危险
解决:大 monorepo 别指望 @codebase 一次搞定。先用 RAG 缩小范围到具体包/目录,或者手动 @ 指定关键文件,控制进入上下文的信噪比。项目越大,越要做 Context Engineering。
案例 3:ChatGPT 长文档分析越问越模糊
场景:团队把一份 200 页的产品需求文档(PRD)整体喂给 ChatGPT,让它回答「这个功能在哪些章节被提到、互相有没有矛盾」。
表现:
- 文档短(30 页内)时回答具体、能引用章节号
- 文档越长,回答越「正确但无用」——开始说「文档中提到了相关内容」「需要进一步确认」这类模糊话术
- 追问细节时模型会「编造」章节号或把不同章节的内容混到一起
解决:别整份喂。先让模型生成文档大纲和章节摘要,基于摘要定位相关章节,再把那几页原文拉进来回答。本质就是 RAG 思路——先索引后取原文,让上下文始终保持高信噪比。
延伸阅读
- 核心方法:Context Engineering——怎么给模型喂对上下文
- 检索:RAG / Fine-tuning vs RAG
- 项目约定:AGENTS.md——别把它写太长,否则也是一种 Context Rot
- 相关模型:Claude Opus 4.5 / Gemini 3 Pro