Claude Code 跑在 200K token 的窗口上,但逆向分析显示它大约用到 167K 就开始”扔东西”了;Codex CLI 的阈值几乎一模一样。两个互为对手的编码智能体,在”什么时候忘、忘掉什么”这件事上收敛到了同一个数字——这不是巧合,而是同一个根本约束逼出来的同一类答案。
上一篇(Claude Code 和 Codex 还在用 RAG 吗?)讲的是 agent 怎么把信息装进上下文;这一篇讲反面:窗口装满之后,agent 怎么决定把什么扔出去。这个问题有个更熟悉的名字——内存管理。
主线一句话:Agent 的上下文淘汰,本质是把操作系统的内存层级搬进了对话历史:所有策略都是三个动词(丢弃、压缩、外置)的组合,外加一张”什么必须活下来”的根集表;而最新的学术证据正在提醒行业——最便宜的那个动词,常常不输最贵的那个。
1. 根本约束:窗口不是装不下,是装多了会变笨
先回答一个自然的反问:为什么不直接把窗口做大? Claude 系列已经有 1M token 的窗口变体了,把 200K 换成 1M,问题不就消失了?
不会消失,因为限制上下文使用的从来不只是容量,还有注意力质量。两条证据:
“Lost in the Middle”(arXiv:2307.03172)系统测量了模型在长上下文中取信息的能力,发现了著名的 U 型曲线:关键信息放在开头或结尾时表现最好,放在中间时显著下滑——即使是专为长上下文设计的模型也一样。窗口越长,“中间”越大,被吞掉的信息越多。
Anthropic 的工程博客《Effective context engineering for AI agents》把这个现象命名为 context rot(上下文腐烂),并给出了一个值得记住的心智模型:模型有一个注意力预算(attention budget),每多一个 token 都在消耗它,收益边际递减。他们的设计准则是一句话:找到”最小的高信号 token 集合”,使期望结果的概率最大化。
所以问题的正确形式不是”怎么装下更多”,而是”怎么让留在窗口里的每个 token 都值得留”。这就是淘汰策略存在的理由——它不是容量不够时的无奈之举,而是维持智能水位的主动手段。
2. 统一框架:三个动词,一张根集表
把所有工具的所有机制摆在一起看,你会发现它们都是三个原子操作的组合:
| 动词 | 操作 | 信息损失 | 成本 |
|---|---|---|---|
| 丢弃(Evict) | 直接删掉旧消息/旧工具输出 | 完全丢失 | 零(不调用模型) |
| 压缩(Compress) | 用 LLM 把历史摘要成短文本 | 有损,细节没了 | 一次昂贵的 LLM 调用 |
| 外置(Externalize) | 把内容写到窗口外(磁盘/记忆文件),留个指针 | 零损失,但要重新读取才可见 | 便宜,代价延迟到未来 |
以及一个所有策略都绕不开的问题:什么必须活下来? 系统提示词、用户的核心意图、未完成的任务、刚改过的文件——这些是不能被淘汰的”根”。如果你写过垃圾回收器(GC),会发现这个结构完全同构:三个动词是回收策略,根集(root set)决定什么对象永远不被回收。
这个类比不是我发明的。MemGPT(arXiv:2310.08560)在 2023 年就把它推到了极致:论文标题就叫 Towards LLMs as Operating Systems,提出”虚拟上下文管理”——像 OS 用分页在快慢存储间搬数据、给进程制造”内存无限大”的错觉一样,在有限窗口和外部存储之间分层调度信息,给 agent 制造”上下文无限长”的错觉。今天 Claude Code 和 Codex 的所有机制,都可以读作这篇论文思想的工程化变体。
下面逐个解剖。
3. Claude Code:一条”能不调模型就不调”的分层管道
Claude Code 的上下文管理不是单一机制,而是一条按成本递增排列的管道。根据社区对其源码的逆向分析(Decode Claude、另一份深挖),每轮迭代开始时依次执行:
graph LR
A[Snip<br/>丢弃整条消息] --> B[Microcompact<br/>掩码可重算的工具结果]
B --> C[Context Collapse<br/>LLM 摘要选定片段]
C --> D[Autocompact<br/>LLM 摘要全部历史]
style A fill:#d4edda
style B fill:#d4edda
style C fill:#fff3cd
style D fill:#f8d7da
绿色的两级不调用模型,黄色调一次(局部),红色调一次(全局)。整条管道的设计哲学一句话:能用零成本的丢弃解决,绝不动用昂贵的摘要。
3.1 Microcompact:赌”可重算”的丢弃
管道里最有味道的一级是 microcompact。它盯着一类特殊消息:工具输出——Read 读进来的文件、Bash 跑出来的日志、Grep 的搜索结果。这类内容有个共同特征:随时可以重新算出来。文件还在磁盘上,命令还能再跑一遍,把 5000 token 的原文留在窗口里纯属浪费。
于是 Claude Code 把较老的工具输出掩码掉,只留一个”内容已存盘,路径在此”的引用;最近若干条工具结果保持完整(所谓 hot tail),保证模型手头的推理不断档。适用的工具覆盖 Read、Bash、Grep、Glob、WebSearch、WebFetch、Edit、Write(逆向分析)。
这一步没有任何模型调用,却常常回收掉窗口里最大的一块——在编码任务里,工具输出通常占上下文的大头,而且是信息密度最低的大头。
3.2 Autocompact:最后手段与触发数字
当便宜手段都不够用,才轮到全量摘要(autocompact):让模型读完整段历史,产出一份结构化总结,替换掉原文。触发阈值不是拍脑袋的,对源码的逆向分析给出了公式:
200K 窗口代入,大约 167K 触发。两个减项各有含义:20K 是给”生成摘要本身”留的输出空间——如果窗口塞到只剩 500 token 才想起摘要,模型连总结都写不下;13K 是提前量,因为检查发生在每次 API 调用之前,要为下一轮的增量留余地。还有一个防御性常量:连续 3 次 autocompact 失败就停手(MAX_CONSECUTIVE_AUTOCOMPACT_FAILURES = 3),防止死循环烧 API。
需要说明:这些数字来自社区逆向,不同版本会漂移(另一份对比研究记录的触发点是约 95% 容量)。数字会变,结构不变:预留输出空间 + 提前量,在窗口八九成满时动手。
3.3 根集表:什么在 compaction 后活下来
摘要发生后,窗口里剩什么?官方文档给了一张精确的幸存者名单——这就是前面说的”根集”:
| 机制 | compaction 之后 |
|---|---|
| 系统提示词、output style | 不动(本来就不在消息历史里) |
| 项目根 CLAUDE.md、无路径限定的规则 | 从磁盘重新注入 |
| 自动记忆(auto memory) | 从磁盘重新注入 |
带 paths: 的路径规则 | 丢失,直到再次读到匹配文件 |
| 子目录里的嵌套 CLAUDE.md | 丢失,直到再次读到该目录文件 |
| 已调用的 skill 正文 | 重新注入,单个上限 5000 token、总量 25000,最旧的先丢 |
| 对话本身 | 被一份结构化摘要替换 |
注意”从磁盘重新注入”这四个字——这是外置动词的用法:CLAUDE.md 和记忆文件的真身在窗口外的文件系统里,窗口里的只是投影,所以摘要摧毁不了它们。想让一条信息在 agent 的记忆里永生,不要把它说在对话里,要把它写在磁盘上。 这是使用任何带 compaction 的 agent 时最实用的一条推论。
除了被动等待,用户还有三个主动手段:/compact 加聚焦指令(如 /compact focus on the auth bug)控制摘要保留什么;/clear 在切换任务时直接清空;把大规模检索派给子智能体——它在独立窗口里烧掉几万 token,只把几百 token 的结论带回来。子智能体本质上是淘汰策略的第四种形态:隔离,从一开始就不让垃圾进主窗口。
4. Codex CLI:同样的骨架,不同的取舍
OpenAI Codex CLI 面对同一个问题,给出了结构相同、细节分岔的答案(依据 Codex Knowledge Base 的架构分析与 openai/codex 源码):
触发公式几乎一字不差。 effective_window = context_window − min(max_output_tokens, 20000),再减 13000 得阈值——200K 窗口同样落在约 167K。两个没有互相抄源码的团队算出同一个数,说明这不是设计品味,是约束的解:输出预留 + 摘要空间 + 提前量,给定同样的窗口就得出同样的答案。
两条摘要路径,其中一条是黑盒。 用 OpenAI 官方模型时,Codex 调用服务端接口 POST /v1/responses/compact,返回一个 AES 加密的不透明压缩块——客户端看不到摘要内容,服务端解密后直接喂给下一轮模型。用第三方模型时走本地路径:抽取最近的用户消息(约 20K token 上限),用一段摘要提示词让模型总结。这个”不透明 blob”设计与 OpenAI Responses API 的 compaction 一脉相承:官方文档明说 compaction item “不供人类解读”。对比之下,Claude Code 的摘要是明文,用户可以审查甚至用 /compact 指令干预——透明可控 vs 黑盒省心,是两家在同一骨架上最大的哲学分岔。
摘要后的修复动作。 compaction 之后 Codex 会注入一段引导消息,并自动重读最近编辑过的至多 5 个文件(每个文件有 token 上限)——这是对”摘要丢失了文件细节”的主动补偿:与其祈祷摘要写全了,不如把最关键的状态(刚改过的代码)从磁盘上原样拉回来。又是外置动词:磁盘是比摘要更可靠的记忆。
兜底是纯丢弃。 如果摘要之后空间仍不够,Codex 退化为 head trimming——从最老的消息开始硬砍。配置上只能把触发阈值调低不能调高(超过窗口 90% 的设置会被静默忽略),且目前无法完全关闭自动 compaction。
把两家放进一张表:
| 维度 | Claude Code | Codex CLI |
|---|---|---|
| 便宜的前置手段 | Snip + microcompact(掩码工具输出) | 无独立层,依赖摘要 |
| 摘要触发 | ~167K / 200K(公式推导) | ~167K / 200K(公式几乎相同) |
| 摘要形式 | 明文结构化总结,可审查可干预 | 官方模型走加密黑盒,本地模型走明文 |
| 摘要后修复 | 从磁盘重注入 CLAUDE.md/记忆/skill | 自动重读最近编辑的文件 |
| 兜底 | 连续失败 3 次即停 | head trimming 硬砍 |
| 用户控制 | /compact 可加聚焦指令、/clear | 阈值只能调低,不能关闭 |
顺带一提生态里的另两种性格,坐标系就完整了:OpenCode 把”修剪”(prune)做成与 compaction 分离的独立机制——保护最近 40K token 的工具输出,只删更老的;而 Sourcegraph Amp 干脆拒绝自动化,只提供手动的 handoff/fork 工具,哲学是”对话就该短而聚焦”。从全自动黑盒(Codex)到全手动(Amp),中间是分层自动化(Claude Code)——没有谁对谁错,是对”用户该不该感知遗忘”这个问题的不同回答。
5. API 层:淘汰策略正在变成基础设施
上面讲的都是 CLI 工具内置的策略。2025 年之后,两家把这套能力下沉到了 API,变成任何 agent 开发者都能直接调用的原语——这个动向值得单独一节,因为它说明上下文淘汰已经从”应用技巧”变成了”平台基础设施”。
Anthropic 的 context editing 提供了声明式的淘汰配置。clear_tool_uses_20250919 策略就是 API 化的 microcompact:超过 trigger 阈值后,自动清掉最老的工具结果,保留最近 keep 条,单次至少清出 clear_at_least token,exclude_tools 列表里的工具豁免。被清掉的内容替换为占位文本,而发起调用的 tool_use 块保留——模型知道”我调过这个工具、参数是什么”,只是结果不在了。
两个设计细节暴露了真实的工程约束。第一,清理发生在 prompt cache 查找之后——随意从历史中间删内容会打碎缓存前缀,让后续每次调用都全价重算;服务端在缓存命中后再清理,鱼与熊掌兼得。第二,文档建议 clear_at_least 不要低于 5000 token,原因同样是缓存:为几百 token 破坏一次缓存前缀,经济上是亏的。淘汰策略不能只看信息论,还得看账单。
配套的 memory tool(memory_20250818)补上外置动词:给模型一个 /memories 目录的文件操作接口,而且当 context editing 快要清理时,模型会收到系统警告,可以抢在遗忘前把重要信息写进记忆文件——这是 MemGPT”模型自主管理自己的内存分页”思想的第一个大厂官方实现。Anthropic 报告的内部评测数字:100 轮网页搜索任务上,单用 context editing 提升 29%,加上 memory tool 提升 39%,token 消耗降低 84%(注意是厂商自测,非独立复现)。
OpenAI 这边,Responses API 的 compaction 用 context_management + compact_threshold 一个参数开启服务端自动摘要。哲学分岔延续到了 API 层:Anthropic 给你一堆旋钮(trigger/keep/exclude),OpenAI 给你一个开关和一个黑盒。
6. 学术界的冷水:最贵的动词,常常不是最好的
到这里,一个自然的推断是:摘要比丢弃”聪明”,所以效果应该更好,丢弃只是穷人的替代品。学术证据恰恰打脸了这个直觉。
《The Complexity Trap》(arXiv:2508.21433,NeurIPS 2025 DL4Code workshop,JetBrains Research)做了一个行业早该做的对照实验:在 SWE-bench Verified 上,用同一个 SWE-agent 框架,比较”LLM 摘要”和”简单观察掩码”(把老的工具输出替换成占位符,只保留最近 M 条)两种策略,跨五种模型配置。结果:
- 观察掩码把成本砍半,解题率不输、有时还略胜 LLM 摘要。 最好的配置(Qwen3-Coder 480B)下,掩码策略每实例 1.29(降 52.7%),也低于 LLM 摘要的 $0.64;
- 摘要不仅要花一次模型调用,还可能引入错误——摘要写偏了,agent 就带着被污染的记忆继续干活;
- 混合策略(掩码为主、偶尔摘要)能再挤出 7-11% 的成本。
论文标题就是结论:迷信复杂机制是个陷阱。回头看 Claude Code 的管道设计——“能不调模型就不调模型”、把 microcompact 排在 autocompact 前面——工程直觉和论文结论指向了同一个地方。 这也解释了为什么观察掩码有效的前提值得记住:工具输出可重算(文件还在磁盘上),所以丢弃的信息损失是虚的;而对话里用户说过的意图不可重算,该摘要还得摘要。动词的选择跟着信息的可恢复性走。
那摘要这个动词还有没有救?ACON(arXiv:2510.00615,ICML 2026)给出的方向是:别用一段固定的摘要提示词打天下,而是把”压缩指南”本身当成可优化对象——分析 agent 在哪些任务上因为压缩丢了关键信息而失败,用自然语言迭代修订压缩指南(全程不动模型权重)。结果:峰值 token 降 26-54%,而且小模型在长任务上的表现最多提升 46%——压缩不仅省钱,还通过去掉干扰信息让模型变准了。这与 context rot 的机制严丝合缝:淘汰做得好,本身就是性能优化。
站远一点看,这个领域已经成了正式学科。《A Survey of Context Engineering for Large Language Models》系统分析了 1400+ 篇论文,把上下文工程拆成检索/生成、处理、管理三大组件——本文讲的一切属于第三格;《Context Engineering 2.0》则把这件事的历史拉长到 1990 年代的人机交互研究,论证”让机器理解我们的处境”是个演化了三十年的老问题,LLM 只是它最新的载体。
7. 往下看一层:KV cache 淘汰,同一道题的另一次出现
如果你读过我之前写的 KV cache 内存那篇,现在可以把两层接起来了——因为同一道淘汰题,在栈的更深一层早就被解过一遍。
推理引擎里,KV cache 随序列长度线性膨胀,显存装不下,就得决定扔掉哪些 token 的 KV。两篇代表作的答案:
StreamingLLM(arXiv:2309.17453,ICLR 2024)发现了 attention sink 现象:模型把不成比例的注意力压在最初几个 token 上,跟语义无关;只要保住开头几个 token 的 KV + 一个近期滑动窗口,4M token 的流式生成都不崩。H2O(arXiv:2306.14048,NeurIPS 2023)则发现注意力分数高度集中在一小撮 heavy hitter token 上,动态保留”重要的 + 最近的”,显存大降而精度几乎不掉。
现在把两层的策略并排放:
| KV cache 层(token 级) | Agent 层(消息级) | |
|---|---|---|
| 永远保住开头 | attention sink(前几个 token) | 系统提示词、CLAUDE.md 重注入 |
| 永远保住最近 | 滑动窗口 | microcompact 的 hot tail、OpenCode 保护最近 40K |
| 按重要性保中间 | H2O 的 heavy hitters | 摘要提取的”关键决策/未完任务” |
同构不是巧合。两层面对的是同一个数学结构:一个只增不减的序列、一个固定预算、一个”未来会用到什么”的预测问题——和 CPU cache 的 LRU、操作系统的页面置换是同一族。所有解都收敛到”头 + 近期 + 重要性采样”这个形状,因为这就是该问题在信息不完备下的通解形状。区别只在操作粒度(token vs 消息)和重要性信号(注意力分数 vs LLM 判断/启发式规则)。
这层同构给了我们一个预判:KV cache 淘汰这几年从静态规则(StreamingLLM)进化到动态学习(各种可学习淘汰策略),agent 层的上下文淘汰大概率会走同一条路——今天的固定阈值和手写规则,会被 ACON 那样”从失败中学习淘汰什么”的自适应机制逐步替代。
8. 带走的判断模型:设计淘汰策略的四个问题
如果你在构建自己的 agent,不要从”抄哪家的 compaction”开始,从这四个问题开始:
- 这条信息可重算吗? 可以(文件、命令输出、搜索结果)→ 用丢弃/掩码,零成本,信息损失是虚的。不可以(用户意图、中间决策)→ 才考虑摘要。这是《The Complexity Trap》的核心教训。
- 这条信息需要跨越多少轮? 几轮内 → 留在窗口;整个会话 → 摘要携带;跨会话 → 必须外置到磁盘/记忆文件,窗口里的一切都是易失的。
- 你的淘汰动作对 prompt cache 友好吗? 从历史中间随意删改会打碎缓存前缀;要么像 Anthropic 那样在缓存命中后清理,要么攒够量再动手(≥5000 token)。先算账,再淘汰。
- 用户需要感知遗忘吗? 需要审查与干预 → 明文摘要 + 手动命令(Claude Code/Amp 路线);追求无感 → 服务端黑盒(Codex 路线)。这是产品问题,不是技术问题。
回到开头那个 167K。两家竞争对手在触发阈值上算出同一个数字,在管道结构上收敛到同样的”便宜手段优先”,这不是谁抄谁——是窗口有限 + 注意力衰减 + 缓存经济学这三条约束共同决定的解空间本来就窄。三十年前操作系统研究者在页面置换上、五年前推理引擎工程师在 KV cache 上、今天 agent 框架作者在对话历史上,解的都是这道题。上下文淘汰没有银弹,但有通解的形状:根集不动,近期全留,可重算的丢弃,不可重算的压缩,要长活的外置。 下次你看到任何一个新 agent 框架的”创新记忆机制”,拿这个形状去套——十有八九,它只是给这五个短语换了名字。
参考来源
工程实践(官方文档与源码分析)
- Explore the context window — Claude Code 官方文档 — compaction 幸存者表、
/compact//clear/子智能体的一手说明 - Effective context engineering for AI agents — Anthropic 工程博客 — attention budget 与 context rot 的提出
- Context editing — Claude Platform 文档 / Memory tool 文档 — API 层淘汰原语与 29%/39%/84% 评测数字
- Compaction — OpenAI API 文档 — Responses API 的服务端摘要
- Inside Claude Code’s Compaction System — Decode Claude / Context Compaction Deep Dive — 分层管道与阈值公式的逆向分析
- Codex CLI Context Compaction: Architecture — Codex Knowledge Base — Codex 阈值公式、加密 blob、文件重读机制
- Context Compaction Research: Claude Code, Codex CLI, OpenCode, Amp — Mario Zechner — 四工具横向对比
- openai/codex issue #11716 — 自动 compaction 无法关闭的讨论
arXiv 论文
- Lost in the Middle: How Language Models Use Long Contexts — U 型注意力曲线
- MemGPT: Towards LLMs as Operating Systems — 虚拟上下文管理的奠基
- Efficient Streaming Language Models with Attention Sinks(ICLR 2024)— attention sink 与 StreamingLLM
- H₂O: Heavy-Hitter Oracle for Efficient Generative Inference(NeurIPS 2023)— KV cache 的重要性淘汰
- The Complexity Trap: Simple Observation Masking Is as Efficient as LLM Summarization for Agent Context Management(NeurIPS 2025 DL4Code)— 掩码 vs 摘要的对照实验
- ACON: Optimizing Context Compression for Long-horizon LLM Agents(ICML 2026)— 可优化的压缩指南
- A Survey of Context Engineering for Large Language Models — 1400+ 篇论文的领域分类
- Context Engineering 2.0: The Context of Context Engineering — 上下文工程的三十年史观