小节一:Context Compaction 到底压缩什么——先区分“历史细节”和“当前决策所需信息”

Context Compaction 最容易犯的第一个错误,就是把所有内容一视同仁。

但实际上,Context 里的不同信息生命周期完全不同。

第一类:绝对不能因为历史太长就随便删掉的内容

这类通常包括:

System Prompt
Safety Rules
Agent Rules
Project Rules
Current Goal
Current Task
Working Memory
用户明确的硬约束

例如:

禁止修改 tests/
不允许新增依赖
必须在 workspace 内操作
高风险 Shell 需要审批

这些不是“旧消息”。

它们是当前执行边界。

所以 Compaction 过程中不应该被当作普通历史压缩掉。

第二类:适合做 Summary 的内容

例如:

旧 Conversation
旧 Tool Result
已完成排查过程
已解决错误
历史计划

这类信息的细节不一定每轮都重要,但它们产生的结论仍然重要。

例如:

旧历史:
连续 7 个 Tool Call 才确认入口文件是 src/main.py

下一轮 Context 只需要:

已确认入口文件为 src/main.py

即可。

第三类:适合只保留最近几轮原文的内容

例如:

最近的用户补充
最近的 Tool Result
刚刚发生的错误
刚刚修改的代码片段

因为它们很可能影响下一步。

所以常见策略是:

Old Context
→ Summary

Recent Context
→ Keep Raw

第四类:可以从当前 Context 中直接移除的内容

例如:

重复 Tool Result
无关日志
已经解决且没有长期价值的错误细节
重复文件列表
低价值调试输出

注意,从当前 Context 移除,不代表从 Trace 删除。

这两层要分开。

一个实用的四层保留策略

先配合这张图看,会更容易把“保留什么、压缩什么、移出什么”理顺。左边是完整历史,中间是四层保留策略,右边则是压缩后真正送进后续推理的 Context。这样一对照,整个思路会非常直观。

图片[1]-小节一:Context Compaction 到底压缩什么——先区分“历史细节”和“当前决策所需信息”-大赛人网

这张图最核心的一点,是把 Compaction 从“少放一点内容”升级成了“按价值分层处理内容”。真正要保留的,是会继续影响决策的信息;真正可以压的,是那些表达冗长、但事实已经明确的历史内容。

可以先记:

P0:永远保留
System / Safety / Project Rules / Current Goal

P1:结构化保留
Working Memory / Current Task / Key Decisions

P2:压缩保留
Old Messages / Old Tool Results / Old Errors

P3:直接移出当前 Context
重复日志 / 无关输出 / 已解决低价值细节

这套分层非常适合放进 ContextBuilder。

为什么“摘要”比“截断”更稳?

这一点特别值得单独看图。很多同学一开始会觉得,既然上下文太长,那直接 messages[-20:] 不就行了吗?但问题恰恰出在这里:截断是机械删内容,压缩则是有策略地重建 Context,两者完全不是一回事。

图片[2]-小节一:Context Compaction 到底压缩什么——先区分“历史细节”和“当前决策所需信息”-大赛人网

从图里你会很清楚地看到,截断最容易丢掉系统规则、关键约束、早期事实和 Tool 链路;而 Summary 的目标,是把这些仍有价值的信息重新组织成更紧凑的表达。所以我们后面一直强调一句话:压缩的是表达,不是事实。

假设旧历史有:

用户要求:不要修改 tests/
Agent 已确认:入口是 src/main.py
Agent 已确认:数据库正常
Agent 已确认:requests 缺失

如果按时间截断:

只留最近 10 条

很可能删掉第一条规则。

如果做语义 Summary:

Project Constraints:
- 禁止修改 tests/

Historical Findings:
- 入口 src/main.py
- 数据库已排除
- requests 缺失是当前主要根因

关键语义仍然存在。

这就是 Compaction 真正的价值,保留“决策价值”,而不是保留“文本原样”。

一个最小压缩结构

如果你想先抓住一个“最小可用版本”,建议先看下面这张结构图。它把压缩前的长历史、compact_context() 的处理逻辑,以及压缩后的最小 Context 结构放在了一张图里,基本能把全章主线串起来。

图片[3]-小节一:Context Compaction 到底压缩什么——先区分“历史细节”和“当前决策所需信息”-大赛人网

这里特别要注意最后一块:Summary ≠ Working Memory。历史摘要负责保留过去发生过什么,而当前任务状态负责描述此刻正在做什么、下一步准备做什么。两者看起来都像“总结”,但职责完全不同。

from dataclasses import dataclass, field


@dataclass
class CompactSummary:
    key_facts: list[str] = field(
        default_factory=list
    )
    completed: list[str] = field(
        default_factory=list
    )
    resolved_errors: list[str] = field(
        default_factory=list
    )
    important_files: list[str] = field(
        default_factory=list
    )
    decisions: list[str] = field(
        default_factory=list
    )


if __name__ == "__main__":
    summary = CompactSummary(
        key_facts=[
            "项目入口为 src/main.py",
            "数据库配置正常",
        ],
        completed=[
            "已复现启动错误",
            "已检查 requirements.txt",
        ],
        resolved_errors=[
            "config.py 路径错误已解决",
        ],
        important_files=[
            "src/main.py",
            "requirements.txt",
        ],
        decisions=[
            "继续围绕 requests 缺失排查",
        ],
    )

    print(summary)

这比直接存一大段字符串更容易后续更新和测试。

Summary 不应该复制 Working Memory

这也是一个细节。

Working Memory 是:

现在最重要的认知

Historical Summary 是:

旧历史压缩后的背景

它们有重叠,但不应该完全重复。

例如:

Working Memory:
Next Action = 修改 requirements.txt 并验证

Historical Summary:
数据库方向已排除;入口已确认;曾发生 config.py 路径错误但已解决

这样各自有价值。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发
头像-大赛人网
欢迎您留下宝贵的见解!
提交
头像-大赛人网

昵称

取消
昵称

    请登录后查看评论内容