第 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. 压缩质量标准

一个好的压缩结果应满足:

  1. 保留当前目标;
  2. 保留已确认事实;
  3. 保留关键约束;
  4. 保留失败和阻塞;
  5. 保留下一步行动;
  6. 保留原始来源引用;
  7. 不把假设写成事实;
  8. 不丢失用户明确要求。

可以用一个简单检查表:

如果只看压缩结果,Agent 能否继续任务?
如果结果出错,人类能否追溯原始证据?
如果上下文恢复,是否知道下一步要做什么?

如果答案是否定的,压缩就不合格。

7. 架构图 / 流程图

Context Compression 流程
图 7-1 Context Compression 流程 这张图强调不同类型上下文需要使用不同压缩器,并保留证据、决策和原始引用。 Mermaid 源文件

7.1 压缩流程

压缩流程
图 7-2 压缩流程 Mermaid 源文件

7.2 分层摘要

分层摘要
图 7-3 分层摘要 Mermaid 源文件

7.3 Compaction

Compaction
图 7-4 Compaction Mermaid 源文件

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。

核心结论:

  1. Compression 的目标是保留任务连续性和决策价值,而不是简单变短。
  2. 不同上下文需要不同压缩策略。
  3. 工具结果、日志、代码和对话都应结构化压缩。
  4. 长任务需要 Rolling Summary、Hierarchical Summary 和 Compaction。
  5. 压缩结果必须保留证据引用,避免把假设变成事实。

一句话总结:

好的压缩不是让上下文更短,而是让上下文在更短的形式下仍然能驱动正确行动。

12. 实践任务

任务 1:压缩 CI 日志

找一段真实或模拟的测试失败日志,压缩为:

  • 命令;
  • 失败模块;
  • 失败测试;
  • 异常类型;
  • 关键栈帧;
  • 可能原因;
  • 下一步;
  • 原始引用。

任务 2:实现 ToolResultCompressor

在 Java 中实现一个 ToolResultCompressor,输入完整命令输出,返回结构化摘要。

任务 3:设计分层摘要表

设计数据库表保存:

step_summary
phase_summary
task_summary
memory_candidate

任务 4:压缩质量评估

给定压缩前后内容,回答:

是否保留了用户目标?
是否保留了关键事实?
是否保留了下一步?
是否保留了来源?
是否引入了新假设?

results matching ""

    No results matching ""