背景
构建 Agent 过程中,都会遇到上下文窗口不够用的问题。
很多人觉得这不是事儿——现在模型上下文窗口动辄 128K、200K,塞就完了。但我们做 Agent 的人都知道,窗口大不等于能用得久。原因很直接:
- 成本:token 是按量计费的,200K 上下文跑一轮可能就要几毛钱,一天跑几百轮,月底账单能吓死人
- 延迟:上下文越长,首 token 延迟越高,用户体验直线下降
- 噪声:塞进去的信息越多,模型越容易”走神”,关键指令被淹没在历史对话的海洋里
所以上下文压缩不是”要不要做”的问题,而是”怎么做好”的问题。
这篇文章从工程视角系统拆解 Agent 上下文压缩这件事——不是简单做摘要,而是一套完整的长任务状态治理体系。
一、核心矛盾:信息保真 vs 窗口有限
1.1 一个形象的比喻
想象你是一个项目经理,工位上只有一张 1 米宽的桌子。桌上要放:当前任务的资料、历史沟通记录、团队通讯录、公司规章制度、客户反馈……
桌子就这么大,你不可能把所有东西都摊开。你的做法是:
- 手边放当前正在处理的文件
- 抽屉里放最近几天的沟通记录
- 文件柜存历史项目档案
- 脑子里记着关键决策和客户偏好
桌子就是你的上下文窗口,你的信息管理策略就是上下文压缩。
1.2 Agent 面临同样的困境
Agent 的”桌子”就是模型的上下文窗口,需要同时容纳:
| 信息类型 | 说明 | 示例 |
|---|---|---|
| 系统指令 | 角色定义、行为约束、工具描述 | “你是一个代码助手,不要执行删除操作” |
| 用户目标 | 当前任务、约束条件、截止时间 | “在今天 5 点前完成用户模块重构” |
| 对话历史 | 多轮交互的上下文 | 用户之前的提问和纠正 |
| 工具结果 | API 返回、文件内容、搜索结果 | 数据库查询结果、文件读取内容 |
| 中间状态 | 已完成的步骤、待办事项、中间结论 | “已完成身份验证模块,还差权限校验” |
| 外部知识 | RAG 召回的文档片段、参考资料 | 相关技术文档、历史 case |
当任务只有 2-3 轮对话时,全部塞进去毫无压力。但当任务进行到 50 轮、100 轮时,这些信息的总量会远超窗口限制。
核心矛盾就在这里:长任务需要的信息量 > 模型能装下的信息量。
压缩的本质,就是在有限的窗口里,最大化当前决策所需的信息密度。
二、分层架构:不是所有信息都值得同等对待
最直觉的做法是”全塞进去”——把历史对话、工具结果、外部知识一股脑丢给模型。这种做法的结局我们在前面已经说了:成本高、延迟大、噪声多。
正确的思路是分层管理——不同信息有不同的保留策略,就像项目经理不会把所有文件都摊在桌上一样。
2.1 四层信息架构
1 | ┌───────────────────────────────────────────────────────┐ |
每层的”温度”不同,访问频率不同,保留策略也不同。热区信息每次都完整进模型,冷区信息只带一个索引,冻结区信息需要显式召回。
2.2 层间流转
信息不是静态分层的,它会随着任务推进从热变冷:
1 | 第 1 轮对话 → 在热区(原文保留) |
这个流转过程是自动的、渐进的,不是一刀切地截断。
三、压缩的五种武器
有了分层架构,接下来要解决具体问题:怎么压?针对不同类型的信息,有不同的压缩策略。
3.1 对话摘要:把过程变成结论
最基础的压缩手段——对早期对话生成摘要。
但这里有个常见误区:摘要不是”缩短版的对话”,而是提取决策链。
差的摘要:
1 | 用户问了怎么实现登录功能,助手建议使用 JWT, |
好的摘要:
1 | ## 已完成决策 |
区别在哪?差的摘要在讲故事,好的摘要在保存状态。Agent 拿着好的摘要能直接继续工作,拿着差的摘要还得重新理解一遍来龙去脉。
摘要的原则:记结论,不记过程;记决策,不记讨论。
3.2 工具结果裁剪:只留有用的部分
Agent 调用工具后,返回结果往往很长。一个数据库查询可能返回几千行,一个文件读取可能上万字。全部塞进上下文,一两轮就把窗口吃光了。
工具结果压缩的核心思路是结构化裁剪:
1 | 原始返回(5000 行) |
举个例子,Agent 查了一下用户表:
1 | SELECT * FROM users WHERE create_time > '2026-01-01'; |
压缩后不是把 3000 条记录全塞进去,而是:
1 | 查询结果:共 3000 条用户记录 |
从 5000 行压缩到不到 20 行,关键信息没丢。
3.3 重复检测:同样的内容不出现两次
Agent 在处理文件时,经常多次读取同一个文件。如果不做处理,同一个文件的内容会在上下文里出现好几次。
解决方式很直觉:去重。
1 | 第 3 轮:读取了 src/auth.ts 的内容(完整展示) |
这看起来简单,但在实际工程中非常有效。据 Claude Code 的实现(参考本博客之前的文章 Context 管理:四级压缩与无限对话的秘密),仅去重这一步就能节省大量 token。
3.4 状态提取:把”隐性知识”变成”显性清单”
这是最容易被忽略、但最关键的压缩手段。
在多轮任务中,有很多关键约束是散落在对话各处的:
- 第 2 轮用户提到”数据库用的是 PostgreSQL”
- 第 5 轮确认了”接口格式用 REST,不用 GraphQL”
- 第 8 轮说了”部署环境是 K8s,注意内存限制”
这些信息如果不显式提取出来,一旦被摘要化就可能丢失。而它们是硬约束,丢了就会导致后续工作做错方向。
所以压缩前要做一件事:状态提取。
1 | ┌─────────────────────────────────────────┐ |
摘要负责背景,状态面板负责保存关键约束。 两者分离,才能保证压缩后不丢关键信息。
3.5 渐进式折叠:不要一刀切
最后要强调的是,压缩不应该是一次性完成的,而是渐进式的。
1 | 上下文用量 60% → 不做压缩,原文保留 |
这就像手机存储空间管理:空间充足时什么照片都留着;空间紧张时先删视频(占空间大但信息密度低);快满时才删不重要的截图。
越紧急,压缩越激进,但状态面板始终保留。
四、最难的问题:压缩后怎么知道没丢关键信息?
前面的策略都是”怎么压”,但真正的工程难题是”怎么保证压完之后信息没丢”。
4.1 状态验证机制
每次压缩完成后,不应该直接把压缩结果丢给模型,而是要做一次状态验证:
1 | 压缩前:提取关键约束清单 |
验证什么呢?主要是检查:
- 用户目标是否还在
- 硬约束(数据库类型、部署环境等)有没有丢
- 未完成的步骤清单是否完整
- 有没有跟原始记录矛盾的地方
4.2 冲突检测
比丢信息更隐蔽的问题是摘要跟原始记录矛盾。
举个真实场景:
1 | 第 3 轮:用户说"用 MySQL" |
这时候 Agent 拿着错误摘要继续干活,后面所有关于数据库的操作都会做错。
更糟糕的是,错误会累积。第 20 轮的摘要基于第 15 轮的摘要,如果第 15 轮就错了,第 20 轮会继续错下去,形成错误链。
解决方案:
- 原始记录优先:当摘要和原始记录冲突时,以原始记录为准
- 用户确认优先:用户明确确认过的信息,优先级最高
- 标记冲突来源:发现矛盾时,标记是哪一轮的摘要出了问题
- 触发重压缩:发现错误摘要后,不是简单修补,而是重新压缩
核心原则:宁可让系统慢一点重新压缩,也不能让错误摘要继续进入后续链路。
五、可回放性:压缩不能只存结果
很多系统只做”向前看”的压缩——保留当前状态就够了。但实际工程中,向后看同样重要。
5.1 为什么需要回放?
考虑这些场景:
- 任务执行到一半出了错,需要回溯是哪一步出了问题
- 用户说”你刚才那个方案不对,退回到之前的版本”
- Agent 做了一个决策,但结果不好,需要复盘决策过程
如果压缩后只留了一个摘要,这些场景全玩不转。
5.2 Checkpoint 机制
正确的做法是每一步都保存一个轻量级的 checkpoint:
1 | { |
注意这里的关键设计:
- 工具调用的原始结果不在 checkpoint 里,而是存了一个引用 ID(
result_ref) - output_summary 是这步的摘要,不是完整输出
- state_delta 记录了这一步对全局状态的改变
这样做的好处是:需要回放时,可以通过引用 ID 拉回原始数据;正常执行时,只占很少的 token。
六、与 RAG 的配合
上下文压缩和 RAG 经常被放在一起讨论,但它们解决的问题不同:
| 上下文压缩 | RAG | |
|---|---|---|
| 解决什么 | 控制进入模型的信息量 | 提供模型不具备的外部知识 |
| 核心动作 | 裁剪、摘要、分层 | 检索、重排、注入 |
| 信息来源 | 对话本身的历史 | 外部知识库 |
但在实际系统中,两者必须配合工作。
6.1 配合模式
1 | 用户提问 |
关键点在于:RAG 召回的结果也要经过压缩处理。如果召回了 20 个片段,每个 2000 token,那就是 40K token,可能直接把窗口吃满。
6.2 保留可追踪性
压缩后还有一个容易丢的东西:引用来源。
RAG 注入的信息如果没有标注来源,模型可能会出现幻觉——把检索到的知识说成是自己”知道”的,用户也无法验证信息的可靠性。
正确做法是在压缩后的片段上保留元数据:
1 | [来源:技术文档 v2.3,第 5 章,2026-03-15 更新,权限:内部公开] |
压缩的是内容,不是来源。 每条注入的信息都应该能追溯到原始出处。
七、评估体系:怎么衡量压缩效果?
做了这么多优化,怎么知道效果好不好?
7.1 不能只看 Token 压缩率
最常见的错误指标是”Token 减少了多少”。压缩 90% 但任务失败了,那不叫优化,叫丢信息。
7.2 正确的评估维度
| 维度 | 说明 | 怎么测 |
|---|---|---|
| 任务成功率 | 压缩后 Agent 能不能正确完成任务 | 端到端测试 |
| 约束保留率 | 用户提出的约束条件在压缩后还剩多少 | 标注测试集 + 人工检查 |
| 答案一致性 | 同一个问题,压缩前后的回答是否一致 | 对比测试 |
| 延迟变化 | 压缩前后的首 token 延迟对比 | 性能监控 |
| 成本变化 | 压缩前后的 token 消耗对比 | 成本统计 |
7.3 重点测试的边界场景
常规对话的压缩效果通常不错,真正拉开差距的是边界场景:
- 长对话(50 轮以上):压缩系统能不能保持状态连贯
- 工具调用失败:失败信息是否被正确保留(而不是被压缩掉)
- 用户纠正:用户说”不对,应该是 XXX”,这个纠正有没有被保留
- 约束变更:用户中途改了需求,压缩后是否反映了最新需求
- 冲突场景:摘要和原始记录矛盾时,系统怎么处理
边界场景才是压缩系统的试金石。
八、工程实践:一个完整的压缩流水线
把前面的内容串起来,一个完整的 Agent 上下文压缩系统长这样:
1 | 每轮对话开始时: |
这套流水线在每轮对话时都会执行,但大部分步骤是轻量级的——只有上下文用量超过阈值时才会触发重度压缩。
九、总结
上下文压缩,不是简单的”做摘要”,而是一套完整的长任务状态治理体系。
核心要点:
- 分层管理:热区原文保留、温区摘要化、冷区按需召回、冻结区持久化
- 多种武器:对话摘要、工具裁剪、重复检测、状态提取、渐进折叠
- 状态优先:摘要负责背景,状态面板负责保存关键约束,两者分离
- 安全网:状态验证 + 冲突检测 + 原始记录优先
- 可回放:每步保存 checkpoint,能回溯、能还原
- 配合 RAG:压缩控制信息量,RAG 提供外部证据,引用来源不能丢
- 全面评估:不看压缩率,看任务成功率、约束保留率、答案一致性
能压的是过程、是冗余、是重复。不能压的是决策、是约束、是用户意图。把这条线划清楚,压缩系统就不会出大问题。
系列文章导航:
- Context 管理:四级压缩与无限对话的秘密 — Claude Code 的具体压缩实现
- Agent 记忆系统设计规范 — 冻结区(外部记忆)的设计详解
- Multi-Agent 系统设计原理 — 多 Agent 协作下的上下文管理