横向拆解Claude Code、Codex等六大Agent上下文压缩策略后,我们做了第 7 个
主流Agent上下文压缩方案差异巨大,我们如何设计出更优的云端多用户方案?核心内容:1. 横向剖析六大主流Agent的上下文压缩策略与设计哲学2. 总结第一代“撑不住才动手”方案的五大核心痛点3. 介绍为云端多用户场景设计的四级水平线+增量摘要方案

横向拆解六大 Agent 的上下文压缩策略,提炼通用配方,并面向云端多用户的 Agent 场景落地一套四级水平线方案。
1. 同一个问题,六种做法
上下文压缩,如今几乎成了所有 Agent 的标配。但有意思的是怎么做——把主流方案的时间线拉出来,你会发现分歧大得离谱。
| 产品 | 核心策略 | 一句话概括 |
|---|---|---|
| Claude Code | 五段流水线,按成本递增排列 | 便宜的本地操作先上,LLM 摘要兜底 |
| Codex CLI | 保留近期用户消息原文,其余全部替换为 handoff 摘要 | 用户说的话最准确,模型说的可以重写 |
| OpenCode | 时间戳标记隐藏 + 结构化摘要 + 回放最后一条用户消息 | 不真删,理论上可恢复 |
| Cline | /smol(/compact)生成摘要后在同一任务内接续 | 自动 + 手动双模式 |
| Cursor | 自动摘要 + 提示开新对话 + 历史可搜索 | 压缩后仍能回溯原始历史 |
| Amp | 不做递归压缩,用 /handoff 开新线程携带要点 | 长对话本身就是问题,换线程比压缩好 |
| MemGPT / Letta | 上下文 = RAM,历史 = 磁盘,Agent 自主换入换出 | 操作系统级的内存调度 |
六家产品,六种哲学。足以说明这件事没有显而易见的最优解,每一种选择背后都是取舍。
后面的内容分三块展开。先横向看看各家的具体做法和设计考量;然后从中提炼几条已经接近共识的原则;最后介绍我们在 MUR AI 上最终落地的方案——四级水平线 + 增量摘要,以及云端多用户场景下额外需要的几层设计。
MUR AI 是一个面向用研场景的云端多用户 Agent。跑在云端、服务多人这两个特点,让我们在压缩方案上比本地 CLI 工具多了好几层要考虑的事,第 6 节会细聊。
2. 反面教材:第一代“撑不住才动手”
要理解新方案为什么长成这样,得先看清它们想摆脱什么。第一代压缩的逻辑一句话能讲完:

直观,好实现。但用过的人都知道体验很差——下面五条是痛点的根源。后面你会发现,新方案各自的设计选择,本质上就是在重点解决其中某一两条。
悬崖式触发。
全量摘要丢细节。
Token 估算粗糙。
text.length / 3 估算 token。中英混合场景下误差 30-50%,以为安全实际已经溢出,或者以为该压了实际还早。不区分信息价值。
用户内容被一刀切。
第一代做法的根本问题:它把压缩当突发事件处理,而不是一种持续维护的能力。
3. 第二代:各家的做法
过去一年,几个主流 Agent 不约而同走向“分层 + 渐进”,但味道各不相同。逐个拆解一下。
Claude Code(Anthropic):五段流水线 + 结构化摘要
Claude Code 把上下文管理做成了一条严格按成本递增排列的流水线:
- Budget Reduction——调整工具输出的截断预算
- Snip——截短老的工具输出,留“做过什么”的摘要行
- Microcompact——对工具输出内容做局部内联压缩
- Context Collapse——对更久远的历史做粒度更细的折叠
- Auto-Compact——兜底,调 LLM 生成结构化摘要
前四步都是纯本地操作,零 API 调用。只有第五步才会请求 LLM。摘要本身是结构化的,固定包含九个章节。值得注意的细节是:它在压缩时刻意保持消息序列前缀稳定,让 Prompt Cache 命中率不会因为压缩而掉下来。
Claude Code 内部其实还有两条更激进的路径,思路都是“把脏活交给服务端,客户端一个字节都别改”:
- cached_microcompact:把“删掉旧 tool 结果”包装成 API 层的 cache_edits 指令。客户端发出去的 prompt 原封不动,服务端在已缓存的前缀上直接抠掉指定内容——字节没变,缓存不失效。
- apiMicrocompact:更彻底,直接调 Anthropic 的 context_management API,让服务端按 input_tokens 阈值自动裁剪旧的工具调用。
换句话说,前面那条本地五段流水线在很多场景下已经退居二线了。能让服务端做的就让服务端做,本地操作永远是兜底。
Codex CLI(OpenAI):近期用户消息优先保护
Codex 的策略相对简单——在约 95% 容量时触发,生成一份 handoff 摘要替换掉旧历史。重建后的上下文只剩三部分:

所有 assistant 回复和工具结果被物理删除,由摘要替代。近期约 20k token 内的用户消息原样保留,更早的用户消息则被蒸馏进摘要里——关键请求和约束会被保留下来。设计哲学是把压缩当作“同事间的工作交接”。
OpenCode:可逆隐藏 + 回放最后一条指令
OpenCode 的做法很有意思,它分两步走:
第一步 Prune(轻量,无 LLM 调用):每次成功响应后自动触发。往回遍历消息,跳过最近 2 轮用户对话,保护最近 40k token 的工具输出不动,把更老的工具输出用时间戳标记为“已压缩”。关键是——数据还在数据库里,只是模型看到的变成了占位符。
第二步 Summary(重量,调 LLM):只在 token 用量超过模型输入上限时触发。生成一份五段式结构化摘要,然后自动回放用户最后一条消息——模型不会从摘要继续,而是从你最近的指令继续。
Cline:自动 + 手动双模式
Cline 从 v3.25 开始有两种压缩模式:手动触发的 /smol 和自动触发的 Auto-Compact。如果开启了 Focus Chain(v3.25 默认开启),待办列表会穿越压缩存活下来,作为进度锚点。
Cursor:压缩 + 可回溯
Cursor 在上下文超出模型窗口时自动压缩旧消息,同时会提示用户“开一个带摘要的新对话”。2026 年他们加了个有意思的能力——Dynamic Context Discovery:把聊天历史变成可搜索的文件,即使压缩后 Agent 也能回头检索原始细节。据说在 A/B 测试里减少了 46.9% 的总 token 消耗。不过社区反馈里有个已知问题:压缩后模型有时会“忘掉”刚才的编辑,Cursor 团队确认这是高优 bug 在修。
Amp(Sourcegraph):不压缩,换线程
Amp 的立场很鲜明:递归摘要会导致性能逐步衰减,所以干脆不做。替代方案是 /handoff——把当前线程的要点打包进一个新线程。不过 2026 年的 Neo CLI 更新里,Amp 也加了 90% 窗口用量时的自动上下文管理——算是对纯手动路线的一个妥协。
MemGPT / Letta:上下文当 RAM
学术派的代表。直接按操作系统的内存层次来建模:
| 层级 | 类比 | 容量 | 访问方式 |
|---|---|---|---|
| Main Context | RAM | 模型上下文窗口大小 | 始终在 prompt 里 |
| Recall Memory | 交换分区 | 完整对话历史 | conversation_search |
| Archival Memory | 磁盘 | 无限(向量存储) | archival_memory_search |
关键区别是:换入换出由 Agent 自己决定,不是被动截断。代价是架构复杂度高,适合需要跨会话长期记忆的场景。
3.x 第二代的实施陷阱:滑窗式 stub 替换 = 每步缓存失效
第二代“分层渐进”如果实施得不对,会掉进另一个隐蔽的坑。
想象这样一种实现:你设了一个规则——“保留最近 N 条 tool 结果,更老的替换成 stub”。听起来很合理对吧?问题在于如果这个判断在 step-loop 里每一步都重算,那么每完成一个 step,就有 1 条原本被保留的旧 tool 结果滑出窗口、被替换成 stub。它的字节变了,从这个位置往后的整段 prompt 前缀对 Prompt Cache 就失效了,需要重新写入。

我们之前有一个真实的 Task:一个 4 轮、177 step、59 分钟的会话,烧了 $77.3,其中
83%($64.8)全是 cache_write
step nMsgs #stub lastStubIdx cacheRead cacheWrite
5 188 75 165 175958 295 ← 命中
6 190 75 165 176253 232 ← 命中
7 192 76 167 102795 74555 ← 炸了
8 194 77 169 102795 77238 ← 又炸
9 196 78 171 102795 77634 ← 还炸
...
58 294 101 273 102795 117088 ← 一直炸
看规律:#stub 每 step 加 1,lastStubIdx 每 step 加 2,cacheRead 永远卡在一个小数不动,cacheWrite 一路涨到约 12 万/step。从 step 7 起前缀缓存就死了,剩下 50 多个 step 每一步都在为“窗口又挪了一格”买单。
stub 决策必须单调推进
cache_edits API 天然满足这个性质。4. 从实践里提炼出的共识
方案放在一起看,细节差异很大,但有几条原则几乎人人认同:
分层渐进,不一刀切。
成本严格递增。
增量摘要优于全量摘要。
用真实 token,别估算。
usage.totalTokens,免费、精确、唯一可信。text.length / 3 只在内部排序时凑合用。用户消息有特权。
保护近端。
单调边界,绝不滑窗。
5. 我们的方案:四级水平线
把上面的原则落地,我们选了四级水平线。理解方式很简单——想象一台电脑的内存压力监视器:

四个 Tier 不是互斥而是累积——Tier 3 触发时会先做完 Tier 1 和 Tier 2 再做摘要。这意味着即使最坏情况,需要送给 LLM 的内容量也已经被前两步免费砍掉一大块。
Tier 0:什么都不做(< 60%)
上下文宽裕,模型注意力没散,最好的优化是不优化。
Tier 1:Snip——便宜的整理(60-80%)
到了 60% 开始预防性维护,没有 LLM 调用,纯字符串处理:截短老的工具输出,截短用户消息里的代码块。细节:保护区内的不动;某些工具享有豁免;用户的纯文本指令永远不动。这一级成本是零,但能挡住相当一部分增长。
Tier 2:Prune——更狠的释压(80-95%)
预防性维护不够了:Tier 1 已经截短的工具输出进一步替换成占位符;裁掉 assistant 旧文本;截断阈值整体下调。依然零 LLM 成本,依然不动保护区。
Tier 3:Summarize——兜底(≥ 95%)
只有 Tier 1 + Tier 2 都救不回来时才触发 LLM 摘要。做的是增量摘要:找出“上次摘要之后 ~ 保护区之前”的消息作为 delta,LLM 输入:上次摘要 + delta → 生成合并摘要,然后替换旧摘要,删除 delta 消息。第一次触发时相当于普通摘要;之后每次都是追加合并,避免语义漂移。摘要 prompt 用结构化输出,四段:进展 / 文件 / 待办 / 上下文。
6. 云端 Agent 还要多做三层
前面四级水平线解决的是“上下文里压什么、压多狠”。但 MUR AI 跑在云端、服务多用户——这意味着我们得处理几件 CLI 工具可以无视的事:用户关掉浏览器再回来,压缩状态不能丢;Pod 重启、流量漂移,跨进程的压缩决策必须一致;工具完整日志要支持事后审计和前端回取;sandbox 里的工具输出动辄几十 MB。这些靠水平线解决不了。
6.1 存储分离:完整日志落盘,对话里只留截断版
每次工具调用都可能吐出几万 token。直接塞进对话历史不行,全丢掉又损失调试和审计能力。我们的做法:完整输出落盘,对话里只保留截断版 + 一条回取路径。本质上是把“模型的工作记忆”和“用户的审计需求”解耦了。
6.2 工具差异化:不是所有工具都该同等对待
我们把工具分四个梯度:完全保护、微压缩豁免、白名单内可压、差异化存储预算。一段 grep 输出和一次 Skill 调用的信息密度不在一个量级,用统一阈值处理就是粗暴。
6.3 跨轮缓存:让压缩决策在重启后还算数
这是云端特有的问题。我们的解法叫 ReplacementCache——把每一轮的截断决策按 part ID 存进 Redis。效果:同一个 part 在整个会话里始终长一样,消息前缀稳定,Prompt Cache 友好;跨实例、跨重启无感。
6.4 多用户隔离
最后一层最朴素但绝对不能省。所有压缩状态按 (userId, sessionId) 二元组隔离。多租户场景下,“压错给谁”比“压错什么”严重得多。
这四层——存储分离、工具差异化、跨轮缓存、多用户隔离——是 CLI 工具都不必处理的。但云端 Agent 不一样:用户合上电脑去吃饭、明天回来,期待的是接着上次继续。
7. 几个关键决策背后的原因
设计过程中有几个看似细节但影响很大的选择。
为什么阈值是 60 / 80 / 95?
为什么先 Snip 再 Prune?
为什么要增量摘要?
为什么用真实 token?
text.length / 3 估算。直到有一次发现估算值显示 70%,实际 LLM 返回的 usage 已经 92%——再来一轮直接溢出。LLM API 每次都返回精确的 usage.totalTokens——免费、精确、唯一可信,没理由不用。为什么保留 compactionProtected 标记?
8. 红线:什么东西任何 Tier 都不能动
把红线列清楚很重要——压缩系统最大的事故不是压不够,而是压错东西。
| 内容 | 原因 |
|---|---|
| 保护区内的所有消息 | 模型短期连贯性的命脉 |
| 用户消息的纯文本部分 | 用户意图就是任务来源 |
| PROTECTED_TOOLS 的输出(Skill / Task) | 高度结构化的关键信息或会话级状态 |
| MICRO_COMPACTION_EXEMPT 的工具(Task / AskUserQuestion) | 保住对话流的关键节点 |
| 带 compactionProtected 标记的 Part | 业务侧明确指定的“必须保留” |
这些规则在每一级压缩里严格执行。不存在“为了救场破例”的情况。
9. 可观测性:让压缩被看见
压缩发生在背后,没有可观测性的话调起来全靠玄学。我们在 SSE 事件里加了这些信息:当前触发的 tier、当时的 token 使用率、Snip 截了几个 part、Prune 替换了几个 part、预估节省了多少 token、命中 ReplacementCache 的 part 数。前端可以据此渲染一个压缩面板,让压缩过程透明化。
10. 我们没做的事
为了避免过度工程,有几条路我们暂时没走,但保留了可能性:主动 cache-aware 调度、可逆隐藏、回放最后一条用户消息、用户消息一字不动、分层长期记忆。这些是下一步可能探索的方向。
11. 说点感性的
聊了这么多技术细节,最后跳出来说一件事。
Context 压缩的目标从来不是省 token。省钱是顺带的。它要解决的问题是保护模型的注意力。
200K 的上下文窗口听起来很大,但研究反复表明:上下文塞到 70% 以上,模型的中段失忆和指令漂移就会明显恶化。它不是真的“忘了”,是注意力被稀释、信号被噪声淹没。这就是 Context Rot。
所以一个合格的压缩系统应该是一个信号工程师——把无关紧要的工具输出降为占位符,让模型不用扫过它们;把老的 assistant 文本裁短,让最近的对话不被淹没;把历史合并成结构化摘要,让模型用事实思考而不是用文本回忆。
我们花力气做分级、做增量、做保护区,本质上是在回答一个问题:“这一轮对话里,模型应该把注意力放在什么上面?”
2026 年做 Agent 工程,这个问题绕不开。
附录:参考资料
- Anthropic — Effective context engineering for AI agents
- Anthropic — Compaction (Claude API)
- Justin3go — Shedding Hea vy Memories: Context Compaction in Codex, Claude Code, and OpenCode
- badlogic — Context Compaction Research: Claude Code, Codex CLI, OpenCode, Amp
- Letta — Agent Memory: How to Build Agents that Learn and Remember
- Mem0 — LLM Chat History Summarization Guide
- Microsoft — Compaction (Agent Framework)
- MemGPT — Engineering Semantic Memory through Adaptive Retention
- Cline / Cursor / Amp — 各家官方文档

-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名