Lazy loaded image
Agent丨长上下文场景下如何做摘要压缩:既控制上下文长度,又避免关键信息丢失和语义扭曲?
Words 6144Read Time 16 min
2026-5-13
type
Post
status
Published
date
May 13, 2026
slug
agent38
summary
tags
Agent
category
icon
password
在 Agent、RAG、AI Coding 这类长任务里,“摘要压缩”不是简单地把聊天记录变短,而是一个状态保真问题
把原始上下文中的任务目标、约束、决策、证据、未完成事项、工具结果和错误状态,压缩成一个可继续推理的“任务状态快照”。
如果只让模型自由写一段自然语言摘要,很容易出现三类问题:
关键信息丢失:忘掉用户约束、失败的命令、已做过的方案。语义扭曲:把“待验证”写成“已完成”,把“假设”写成“事实”。上下文污染:摘要里保留了大量已经过时、无关、重复的工具输出。
成熟系统通常不会把“摘要压缩”当作一个孤立 Prompt,而是把它放在上下文工程 Context Engineering里设计。Anthropic 明确把 context engineering 定义为在推理时动态维护上下文状态,而不是只写 system prompt;他们也指出长上下文会消耗模型有限注意力,context 需要被当作有限资源管理。(Anthropic)

一、为什么长上下文仍然需要摘要压缩?

很多人会问:现在模型都有 128K、200K、甚至更长上下文了,为什么还要摘要?
原因有三个。
第一,长上下文不等于高质量上下文。Lost in the Middle 研究发现,模型在长输入中对位于开头和结尾的信息利用效果通常更好,而对中间关键信息的使用会显著退化。(arXiv) Chroma 的 Context Rot 报告也指出,随着输入 token 增加,模型表现会出现退化,尤其当问题与“针”信息的语义相似度下降时更明显。(Chroma)
第二,Agent 的上下文增长不是线性的“聊天增长”,而是由工具调用放大的。一次 grep、一次文件读取、一次测试日志、一次网页抓取都可能把数千 token 塞进上下文。Claude Code 的上下文窗口说明里也把指令、文件读取、工具结果、hook、subagent 返回摘要等都纳入了上下文窗口管理。(Claude Code)
第三,长上下文会带来成本和延迟问题。OpenCode 社区关于自动 compaction 的 issue 就明确提到:本地模型每轮会重新处理完整历史,随着对话增长,prefill 延迟会急剧上升,因此希望在接近阈值时把历史摘要为 compact representation,并保留最近 turns。这里需要注意,我检索到的是 OpenCode 社区需求/issue,而不是把它当作已稳定发布的官方能力。(GitHub)

二、成熟系统如何做:Claude、OpenHands、LangGraph、OpenClaw、RAG

1. Claude / Claude Code:Compaction 是长任务的第一道闸

Anthropic 在 Claude API 文档中把 server-side compaction 定义为:当长对话接近上下文窗口限制时,自动总结较旧上下文,并用 compaction block 继续对话。它的目的不只是省 token,还包括让活跃上下文保持聚焦和高性能。(Claude Platform)
Claude Code 的文档也说明:/compact 会把 conversation history 替换为结构化摘要;系统提示、项目根目录 CLAUDE.md、auto memory 等会重新注入,而某些 path-scoped rules、嵌套 CLAUDE.md、skill bodies 有不同的保留/重新注入规则。(Claude Code)
这给我们的启发是:
摘要压缩不应该“一刀切”压缩所有内容。
系统指令、项目规则、长期记忆、最近消息、工具 schema、文件片段,应该有不同生命周期。

2. OpenHands:Condenser 抽象非常值得参考

OpenHands SDK 的 Condenser 系统专门负责 conversation history compression,包括历史压缩、阈值检测、摘要生成、View 管理。官方架构文档把核心组件拆成 CondenserBaseRollingCondenserLLMSummarizingCondenserPipelineCondenserViewCondensation 等。(OpenHands Docs)
OpenHands 的 LLMSummarizingCondenser 默认会在历史超过限制时,保留前面的关键事件,例如 system prompt 和初始用户消息,同时把被丢弃的中间事件替换成 LLM 生成的 summary。官方示例中 keep_first=2max_size=10 就体现了这种“保留头部 + 摘要中部 + 保留尾部”的 rolling 设计。(OpenHands Docs)
从源码看,OpenHands 的 condenser 不是简单判断“超过长度就摘要”,而是区分 REQUESTTOKENSEVENTS 三类触发原因;同时支持 soft/hard condensation,并在 hard reset 失败时逐步缩短 event 字符串重试。(GitHub)
可以抽象成下面这个设计:

3. LangGraph / LangMem:摘要节点是图中的 pre-model hook

LangChain 的短期记忆文档把长对话处理归为几类:trim messages、delete messages、summarize messages、custom strategies;其中 summarize 是把早期消息替换成摘要,而不是永久丢掉所有历史。(LangChain 文档)
LangMem 的 SummarizationNode 更接近生产工程里的“摘要节点”:当消息超过 token 阈值时,把较旧消息摘要成一个 summary message,再与剩余消息组合成 [summary_message] + remaining_messages。它还支持 running_summary,只处理尚未摘要过的新消息,并可基于已有摘要继续更新。(langchain-ai.github.io)
抽象成 LangGraph 风格:

4. OpenClaw:明确区分 Context 和 Memory

OpenClaw 文档把 context 定义为“本轮发送给模型的所有内容”,包括 system prompt、conversation history、tool calls/results、attachments 等;同时强调 context 不等于 memory,memory 可以存盘后再加载,context 是当前窗口里的内容。(OpenClaw)
OpenClaw 的 compaction 文档还提到:当会话接近上下文限制时,会把较旧消息压缩成 compact entry,最近消息保持原样;完整历史仍保存在磁盘上,compaction 只改变下一轮模型可见内容。它还特别说明,拆分历史时会保持 assistant tool calls 与对应 toolResult 的配对,避免把工具调用链拆断。(OpenClaw)
这点非常关键:
摘要压缩应该只改变“模型输入视图”,不要破坏“原始事件日志”。
原始日志必须 append-only 保存,摘要只是一个派生视图。

5. RAG:Query-aware contextual compression

RAG 里的摘要压缩和 Agent 对话压缩稍有不同。RAG 更强调“面向当前 query 压缩检索结果”。LangChain 的 contextual compression 思路是:先召回一批文档,再用 compressor 根据 query 抽取相关内容或过滤掉无关文档。这样初始检索阶段可以偏召回,压缩阶段负责精度。(LangChain)
RAG 场景不要把文档做成“通用摘要”后直接喂给模型,因为通用摘要很容易丢掉当前问题需要的细节。更推荐:

三、推荐架构:四层上下文压缩系统

一个生产级长上下文摘要压缩系统,建议分成四层:
第一层:Active Context,本轮真正发给模型的上下文。
第二层:Rolling Summary,滚动摘要,承载历史任务状态。
第三层:Retrievable Memory,可检索记忆,例如向量库、关键词索引、文件索引。
第四层:Raw Event Log,完整原始事件日志,永不直接丢弃。
图解如下:
核心原则是:
Main LLM 不直接看完整历史。
Context Builder 每轮根据 token budget 组装“最小高信号上下文”。
Summary 只是 Active Context 的一部分,Raw Event Log 才是事实源。

四、摘要应该压缩什么?不应该压缩什么?

摘要压缩最容易犯的错误是:按时间顺序总结“发生了什么”。这对人类读者友好,但对 Agent 继续任务并不够。
更好的摘要结构是“任务状态快照”。
这里的重点不是 YAML,而是结构化槽位。自由文本摘要容易把“事实、假设、计划、结论”混在一起;结构化摘要则强迫模型区分它们。

五、压缩流程:不要压最近,也不要压系统目标

推荐使用“头尾保留,中间压缩”的 rolling compaction。
为什么不压最近消息?
因为最近消息通常包含当前推理链、用户刚刚纠正的信息、刚失败的工具调用、下一步操作意图。如果压掉最近消息,Agent 很容易“断片”。
为什么保留头部?
因为头部通常包含系统指令、初始任务目标、用户原始约束。OpenHands 的 keep_first 设计、Claude Code 对系统提示和项目 memory 的重新注入,都是这个思路。(OpenHands Docs)

六、Token Budget Planner:先分预算,再决定压缩

不要等上下文爆了才压缩。应该在每轮调用前做预算规划。
一个可落地的预算模型:
系统提示与工具 schema:20%
当前用户问题与最近对话:25%
rolling summary:10%
RAG / 文件片段 / 工具观察:35%
安全余量:10%
伪代码:
这里有一个工程细节:输出 token 也要预留。很多系统只算输入,结果输入塞满后模型没有足够输出空间,导致回答截断或者工具调用 JSON 不完整。

七、如何避免关键信息丢失?

1. 摘要前先做信息分级

把历史事件分成四类:
必须原文保留:系统指令、用户硬约束、最近几轮、当前错误栈、关键代码 diff、工具调用参数。
结构化摘要:已完成步骤、设计决策、已验证结论、失败尝试。
仅保留引用:长日志、完整文件、大段检索文档。
可以丢弃:重复确认、无效寒暄、过时中间输出、已被后续结果覆盖的信息。

2. 工具调用必须成对保留或成对摘要

如果有这样的消息序列:
不能只保留 tool_call,不保留 tool_result;也不能只保留“测试失败”,不保留失败位置。OpenClaw 文档特别强调 compaction 分块时要保持 tool calls 与 toolResult 配对,这正是为了避免 Agent 后续无法理解工具观察。(OpenClaw)
推荐摘要为:

3. 每条关键事实都要带 provenance

不要写:
已经确认数据库连接正常。
要写:
这样后续如果出现冲突,Agent 可以回到 raw event log 或 memory 里重新检查。

4. 对“用户纠正”做高优先级保留

用户纠正是最容易在摘要中丢失的内容,例如:
不要用 Redis,用 Postgres。
我刚才说错了,应该是 gRPC,不是 REST。
这不是 bug,是预期行为。
建议把 correction 单独放进摘要槽位:

5. 摘要后做自检,而不是直接替换

摘要生成后,最好再跑一个 verifier:
生产系统里可以先用规则校验:是否存在 source、是否存在 next_steps、是否包含最新用户消息、是否保留失败状态。高风险任务再用 LLM verifier。

八、如何避免语义扭曲?

语义扭曲通常来自三个地方:
第一,摘要模型为了流畅性自动补全因果关系。
第二,摘要把多个时间点的信息合并,丢掉“先后顺序”。
第三,摘要没有区分事实、计划、假设和结论。
解决方式是“状态机化”。
推荐在摘要 schema 中显式区分:
不要让摘要模型写成:
我们决定使用方案 A,因为它性能最好。
除非原始事件里真的有性能验证。否则应该写:

九、Agent 场景和 RAG 场景的压缩差异

Agent 压缩:保任务状态

Agent 需要继续执行,因此摘要要保留:
目标、约束、计划、已完成步骤、失败尝试、工具结果、当前阻塞、下一步。

RAG 压缩:保证据片段

RAG 需要回答问题,因此摘要要保留:
与 query 相关的原文证据、出处、段落、时间、版本、冲突信息。
RAG 中最危险的是“摘要替代证据”。如果答案需要引用法规、合同、论文、代码,最好保留原文 span,而不是只保留模型摘要。
RAG 压缩更推荐这种形式:
优先级通常是:
原文片段 extractive compression > 带引用的结构化摘要 > 无引用自然语言摘要。

十、一个生产级摘要压缩 Prompt 模板

这个 Prompt 的重点是:约束摘要模型不要发挥,不要润色,不要自动归因

十一、压缩触发策略:不要等 95% 才压

建议使用三种触发器:
经验上,不要等到 90% 甚至 95% 才压缩,因为摘要本身也需要上下文空间。Claude API compaction 的官方描述是“接近配置阈值时自动总结”,而不是等到完全爆窗。(Claude Platform)
更稳妥的策略:
60%:开始提示/后台准备摘要候选。
70%:自动 rolling summary。
85%:强制 compaction,只保留最近原文和关键状态。
95%:危险区,只能 hard reset,容易丢信息。

十二、摘要压缩与长期记忆要配合

摘要压缩解决的是“当前会话继续跑”的问题,不等于长期记忆。
Anthropic 在 context engineering 文章中把长任务技术拆成 compaction、structured note-taking、multi-agent architectures。Compaction 负责压缩当前长会话;structured note-taking 把关键状态写到上下文窗口之外;sub-agent 则用独立上下文完成局部任务,只把精炼结果返回主 Agent。(Anthropic)
所以生产系统建议:
长期记忆适合存:
用户偏好、项目规则、稳定 API 约定、业务实体、已确认结论、长期 TODO。
Rolling summary 适合存:
当前任务进度、临时计划、当前错误、最近决策、下一步操作。
Raw artifact store 适合存:
完整日志、完整文件、完整网页、完整工具输出、测试报告。

十三、评估摘要压缩效果

不要只看压缩率。压缩率高不代表 Agent 还能继续完成任务。
建议指标:

1. Compression Ratio

通常希望历史区域能压到 10%~30%,但不能为了低 token 牺牲任务状态。

2. Key Fact Recall

从原始 trace 中人工或自动标注关键事实,看摘要是否保留:

3. Constraint Preservation

检查用户硬约束是否全部保留:

4. Continuation Success Rate

把同一条长任务 trace 分成两组:
A 组:不压缩,完整上下文继续。
B 组:压缩后继续。
比较后续任务成功率、工具调用成功率、错误恢复率。

5. Semantic Drift Rate

让 verifier 判断摘要是否出现:
把未完成写成已完成。
把失败尝试写成成功。
把假设写成事实。
把旧约束覆盖新约束。
删除关键错误信息。

十四、落地建议:一个可直接实现的最小版本

最小可用版本可以这样做:
这个版本已经具备几个关键能力:
保留完整 raw events。
只压缩中间历史。
保留最近消息。
支持 running summary。
工具调用边界对齐。
摘要后校验。
每轮动态构建上下文。

十五、常见错误总结

错误 1:把摘要写成文章

自然语言摘要适合人看,不适合 Agent 继续执行。应该写状态快照。

错误 2:删除原始历史

压缩只应该改变模型视图,不能删除 raw log。OpenClaw 的设计也强调完整历史仍在磁盘上,compaction 只改变下一轮模型看到的内容。(OpenClaw)

错误 3:压缩最近上下文

最近几轮通常最重要,应该原文保留。

错误 4:无引用摘要

没有 source/event/file/span 的摘要,后续无法验证,冲突时也无法回溯。

错误 5:RAG 中用摘要替代证据

RAG 最好保留 extractive spans。摘要可以辅助组织,但不能替代关键原文证据。

结论

长上下文场景下的摘要压缩,本质是一个上下文状态管理系统,不是一个“总结聊天记录”的小功能。
成熟方案的共同点是:
Claude / Claude Code 强调 compaction、结构化保留和重新注入。
OpenHands 把它抽象成 Condenser,支持阈值检测、rolling history、soft/hard condensation。
LangGraph / LangMem 把摘要作为图中的 pre-model 节点,用 running summary 替换旧消息。
OpenClaw 明确区分 context 和 memory,并保留完整历史,只改变模型可见视图。
RAG 系统更适合 query-aware contextual compression,优先保留相关原文证据。
最终可落地的设计原则是:
原始日志不丢,模型视图可压;系统目标和最近上下文不压;中间历史结构化摘要;关键事实带引用;工具调用保持原子性;摘要后必须校验。
这样才能在控制上下文长度的同时,最大限度避免关键信息丢失和语义扭曲。
回到首页