第 7 章:Context Compression
本章导读
- 核心问题:如何在不丢失证据链的前提下降低上下文成本。
- 关键词:Compression、Summary、Extractive Compression、Reference-based Compression、Compaction
- 学习产出:把完整 CI 日志压缩为保留失败测试、异常、关键栈帧和原始引用的高信号摘要。
1. 本章要解决什么问题
上一章讨论了 Context Selection:如何选择最有价值的信息。
但即使选择做得很好,长任务中上下文仍然会不断增长。
例如一个 Coding Agent 连续工作 30 分钟,可能产生:
- 多轮用户对话;
- 代码搜索结果;
- 多个文件内容;
- 多次测试输出;
- 多次失败日志;
- 修改计划;
- Git Diff;
- Agent 的阶段性结论。
这些内容如果全部保留,迟早会超过上下文预算。
这时就需要 Context Compression。
注意,压缩不是简单“总结一下”。
真正的 Context Compression 要回答:
哪些信息必须保真保留?
哪些信息可以摘要?
哪些信息只保留引用?
哪些信息可以丢弃?
压缩后如何保证 Agent 还能继续任务?
本章核心观点是:
Context Compression 是在保留任务连续性和决策价值的前提下,减少上下文 token 占用。
2. 对应 Anthropic 文章
本章主要对应:
- Effective Context Engineering for AI Agents
Anthropic 在长任务部分提到 Compaction:当上下文窗口接近限制时,将已有对话和任务历史压缩成高保真摘要,再用摘要开启新的上下文窗口。
它还提到 structured note-taking 和 sub-agent architectures。
这些方法本质上都是为了解决同一个问题:
长任务需要跨越单个 Context Window,但不能把所有历史都永久塞在上下文里。
3. Compression 与 Summary 的区别
很多人把 Compression 理解为 Summary。
但二者不同。
普通 Summary 追求“读起来简短”。
Context Compression 追求“让 Agent 能继续正确工作”。
例如一段测试失败日志,普通摘要可能是:
测试失败了,原因可能是订单状态校验有问题。
这不够。
面向 Agent 的压缩结果应该是:
失败测试:OrderServiceTest.shouldRejectInvalidState
失败断言:expected 400 but got 500
异常类型:IllegalStateException
关键栈帧:OrderStateMachine.validate(OrderStateMachine.java:88)
相关最近变更:OrderStatus 新增 PENDING_REVIEW,但状态机未允许该状态
建议下一步:读取 OrderStateMachine.java 和 OrderStatus.java
原始日志引用:ci://build/4312/logs/test#L882-L940
这才是高价值压缩。
它保留了:
- 事实;
- 证据;
- 定位信息;
- 下一步行动;
- 原始引用。
4. 常见压缩对象
4.1 Conversation Compression:对话压缩
长对话应该压缩为:
当前目标
用户已确认的约束
被否定的方案
关键决策
未解决问题
下一步计划
不应该保留所有寒暄、重复解释和过期假设。
示例
原始对话可能很长,压缩后应类似:
目标:为订单服务增加状态 PENDING_REVIEW。
用户约束:不能修改 public API;必须兼容旧状态;需要补充 service 层单元测试。
已确认:状态转换逻辑集中在 OrderStateMachine。
已否定:不采用数据库迁移方案。
下一步:修改状态机并补充 OrderServiceTest。
4.2 Tool Result Compression:工具结果压缩
工具结果最容易产生大量噪声。
例如:
- 完整日志;
- 查询结果;
- 搜索列表;
- 构建输出;
- API 返回 JSON;
- 大文件内容。
压缩工具结果时,应保留:
工具名
调用参数
结果状态
关键输出
错误信息
证据引用
建议下一步
测试日志压缩模板
命令:./mvnw test
结果:失败
失败模块:order-service
失败测试:OrderServiceTest.shouldRejectInvalidState
异常:IllegalStateException
关键栈帧:OrderStateMachine.java:88
可能原因:新增状态未加入状态机
原始输出引用:artifact://ci/4312/test-output.txt
4.3 Code Compression:代码压缩
代码不能简单摘要成“这个类处理订单”。
对 Coding Agent 来说,更有价值的是结构化代码摘要:
文件路径
类名
主要方法签名
关键字段
调用关系
与当前任务相关的代码片段
测试覆盖情况
例如:
OrderStateMachine.java
- validate(OrderStatus from, OrderStatus to): 校验状态转换,不允许未注册转换。
- allowedTransitions: Map<OrderStatus, Set<OrderStatus>>。
- 当前缺少 PENDING_REVIEW -> APPROVED。
相关测试:OrderServiceTest.shouldRejectInvalidState。
这样比完整文件短,但保留了 Agent 修改代码所需的核心信息。
4.4 Document Compression:文档压缩
企业文档往往很长。
压缩文档时应保留:
- 与当前任务相关的规则;
- 决策结论;
- 约束;
- 示例;
- 链接;
- 版本时间。
不应该保留整篇背景说明。
4.5 Memory Compression:记忆压缩
长期记忆也需要压缩。
例如多个历史任务都说明:
OrderStateMachine 经常因为新增状态漏配转换而导致测试失败。
可以合并为一条项目经验:
项目经验:修改 OrderStatus 时,必须同步检查 OrderStateMachine.allowedTransitions,并运行 OrderServiceTest。
来源:历史任务 #1021、#1188、#1340。
置信度:高。
这比保留三段历史对话更有价值。
5. 压缩策略
5.1 Rolling Summary
Rolling Summary 用于长对话或长任务。
每隔一段时间,将历史压缩为当前摘要。
历史对话 + 当前摘要
↓
新摘要
↓
替代旧历史
适合:
- 长会话;
- 多轮需求澄清;
- 长时间编码任务;
- 研究任务。
风险:摘要可能逐渐丢失细节。
缓解方式:保留关键证据引用。
5.2 Hierarchical Summary
分层摘要适合大型任务。
例如:
Step Summary
↓
Phase Summary
↓
Task Summary
↓
Project Memory
每一层保留不同粒度信息。
示例:
- Step Summary:一次工具调用结果;
- Phase Summary:Explore 阶段发现;
- Task Summary:整个任务结论;
- Project Memory:可复用经验。
5.3 Extractive Compression
抽取式压缩从原文中选择关键片段。
适合:
- 错误栈;
- 法规条款;
- 代码片段;
- 日志关键行。
优点是保真。
缺点是不够抽象。
5.4 Abstractive Compression
生成式压缩重新表达信息。
适合:
- 对话总结;
- 方案总结;
- 文档摘要;
- 多工具结果综合。
优点是简洁。
风险是可能引入幻觉。
因此关键事实必须保留引用。
5.5 Reference-based Compression
只保留引用,不保留全文。
例如:
完整日志:ci://build/4312/logs/test-output.txt
关键片段:L882-L940
适合大文件、大日志、大数据集。
Agent 需要时再通过工具读取。
6. 压缩质量标准
一个好的压缩结果应满足:
- 保留当前目标;
- 保留已确认事实;
- 保留关键约束;
- 保留失败和阻塞;
- 保留下一步行动;
- 保留原始来源引用;
- 不把假设写成事实;
- 不丢失用户明确要求。
可以用一个简单检查表:
如果只看压缩结果,Agent 能否继续任务?
如果结果出错,人类能否追溯原始证据?
如果上下文恢复,是否知道下一步要做什么?
如果答案是否定的,压缩就不合格。
7. 架构图 / 流程图
7.1 压缩流程
7.2 分层摘要
7.3 Compaction
8. Java / Spring Boot 落地方案
8.1 ContextCompressor
public interface ContextCompressor {
boolean supports(ContextChunk chunk);
ContextChunk compress(ContextChunk chunk, CompressionRequest request);
}
请求:
public record CompressionRequest(
String taskId,
AgentPhase phase,
int targetTokens,
boolean preserveReferences
) {}
8.2 类型化压缩器
public class ToolResultCompressor implements ContextCompressor {
@Override
public boolean supports(ContextChunk chunk) {
return chunk.type() == ContextType.TOOL_RESULT;
}
@Override
public ContextChunk compress(ContextChunk chunk, CompressionRequest request) {
// 提取命令、状态、错误、关键行、引用
return chunk;
}
}
常见实现:
ConversationCompressor
ToolResultCompressor
CodeContextCompressor
DocumentCompressor
MemoryCompressor
8.3 CompressionPolicy
public record CompressionPolicy(
int maxTokensBeforeCompression,
Map<ContextType, Integer> targetTokensByType,
boolean requireSourceReference,
boolean allowAbstractiveSummary
) {}
不同类型应有不同目标 token。
例如:
CI 完整日志 20000 tokens → 压缩到 1000 tokens
代码文件 5000 tokens → 压缩到 1500 tokens
对话历史 12000 tokens → 压缩到 2000 tokens
8.4 CompressionTrace
压缩也需要可观测:
public record CompressionTrace(
String originalChunkId,
String compressedChunkId,
int originalTokens,
int compressedTokens,
String strategy,
List<String> preservedReferences,
Instant createdAt
) {}
否则 Agent 失败时无法判断是否是压缩丢失关键信息。
9. 常见误区
9.1 把压缩等同于短摘要
短不代表有用。
压缩应保留决策价值。
9.2 丢失引用
没有引用的摘要很难验证。
关键事实必须可追溯。
9.3 把假设压缩成事实
例如 Agent 猜测“可能是状态机问题”,压缩后不能写成“原因是状态机问题”。
应写:
假设:可能是状态机漏配,需要验证。
9.4 所有内容用同一种压缩器
日志、代码、文档、对话需要不同策略。
9.5 只在爆窗时压缩
压缩应该是持续机制,而不是最后补救。
10. 贯穿案例:CI 失败分析 Agent
CI 日志是最典型的压缩对象。
完整日志可能包含依赖下载、编译输出、测试启动信息、数百个成功测试和最终失败栈。Agent 不需要看到全部内容。
对于 build #4312,日志压缩结果应保留:
Build: #4312
Command: ./mvnw test
Failed Test: OrderServiceTest.shouldApprovePendingReviewOrder
Assertion: expected HTTP 200 but got HTTP 500
Exception: IllegalStateException: transition PENDING_REVIEW -> APPROVED is not allowed
Key Frame: OrderStateMachine.validate(OrderStateMachine.java:88)
Raw Log Reference: ci://build/4312/log#L882-L940
Suggested Next Step: inspect OrderStateMachine.allowedTransitions
这个压缩结果比“测试失败,可能是状态机问题”更有价值,因为它保留了失败测试、异常、关键栈帧、原始引用和下一步行动。
这说明 Context Compression 不是简单摘要,而是在更短形式下保留任务连续性和证据链。
11. 本章小结
本章讨论了 Context Compression。
核心结论:
- Compression 的目标是保留任务连续性和决策价值,而不是简单变短。
- 不同上下文需要不同压缩策略。
- 工具结果、日志、代码和对话都应结构化压缩。
- 长任务需要 Rolling Summary、Hierarchical Summary 和 Compaction。
- 压缩结果必须保留证据引用,避免把假设变成事实。
一句话总结:
好的压缩不是让上下文更短,而是让上下文在更短的形式下仍然能驱动正确行动。
12. 实践任务
任务 1:压缩 CI 日志
找一段真实或模拟的测试失败日志,压缩为:
- 命令;
- 失败模块;
- 失败测试;
- 异常类型;
- 关键栈帧;
- 可能原因;
- 下一步;
- 原始引用。
任务 2:实现 ToolResultCompressor
在 Java 中实现一个 ToolResultCompressor,输入完整命令输出,返回结构化摘要。
任务 3:设计分层摘要表
设计数据库表保存:
step_summary
phase_summary
task_summary
memory_candidate
任务 4:压缩质量评估
给定压缩前后内容,回答:
是否保留了用户目标?
是否保留了关键事实?
是否保留了下一步?
是否保留了来源?
是否引入了新假设?