Anchored Summary
压缩之后 AI 还知道发生过什么
一、原理:摘要怎么不丢失关键信息
2.4 章我们讲过 compaction——当 context 满了,opencode 用 LLM 把历史压缩成摘要。
但摘要本身有个根本问题——压缩必然丢信息。50 个 turn 的历史压缩成 1000 字摘要,丢了 99% 的细节。哪些细节该留、哪些可以丢?
这个问题分两种:
情况 A:第一次压缩
session 跑了 50 个 turn,第一次压缩。LLM 看完所有历史,写一段摘要。这段摘要要:
- 保留用户最初的需求
- 保留已做出的关键决策
- 保留产生的文件 / 代码 / 输出
- 保留未完成的待办
但不能保留:
- 探索过程中的所有细节
- 失败的尝试
- 中间状态的对话
这个判断需要理解上下文的"价值梯度"——什么对未来工作还有用、什么是过去工作的"过程"。
情况 B:再次压缩
session 已经跑了 100 个 turn,之前压缩过一次(生成了 summary A)。现在又满了,需要再压一次。
直觉做法是——丢掉 summary A,让 LLM 从所有 100 个 turn 重新写一段 summary。
但这有问题:
- 重新读 100 个 turn 很贵(token 成本高)
- 重新写一段可能跟 summary A 不一致——细节漂移
- 一些早期细节在 summary A 里精确("用户最初需求是 X"),重新提炼可能改成"用户大概是 X"——精度损失
更好的做法是——让 LLM 在 summary A 的基础上"增量更新"。前面的摘要保留,最近的新历史合并进去。这样不重复处理旧内容、不丢早期精度、token 消耗低。
这就是 anchored summary——锚定式摘要。每次压缩不从头开始,而是在前次锚点上"叠加更新"。
"anchored"是关键词——它意味着摘要有一个稳定的锚点,每次更新是相对锚点的"细微调整"。不是从头重写。
opencode 在 compaction 设计上采用了 anchored summary。我们看具体怎么做。
二、案例:opencode compaction.txt 的 anchored summary 设计
打开 packages/opencode/src/agent/prompt/compaction.txt:
You are an anchored context summarization assistant for coding sessions.
Summarize only the conversation history you are given. The newest turns may be kept verbatim outside your summary, so focus on the older context that still matters for continuing the work.
If the prompt includes a <previous-summary> block, treat it as the current anchored summary. Update it with the new history by preserving still-true details, removing stale details, and merging in new facts.
Always follow the exact output structure requested by the user prompt. Keep every section, preserve exact file paths and identifiers when known, and prefer terse bullets over paragraphs.
Do not answer the conversation itself. Do not mention that you are summarizing, compacting, or merging context. Respond in the same language as the conversation.
只有几百字,但每一句都是产品判断。
判断 1:第一句就是身份锚
"You are an anchored context summarization assistant for coding sessions."
这个身份告诉 LLM——你是个专门的摘要助手,不是普通的 chat agent。你的工作是处理上下文摘要,不是回答问题。
如果这个身份不明确,LLM 可能"看到对话历史想回答里面的问题"——而不是摘要。
判断 2:明确分工——"newest turns kept verbatim"
"The newest turns may be kept verbatim outside your summary, so focus on the older context that still matters for continuing the work."
告诉 LLM——最近的 turn 不归你管,那些会原样保留。你只处理较老的 history。
这避免 LLM "把最新的 turn 也压缩进 summary"——LLM 看到最新内容可能本能想总结它,但它已经被外部保留了,再总结就重复。
明确分工让 LLM 聚焦"较老的 history"——这是它该做的事。
判断 3:识别 previous-summary 块
"If the prompt includes a <previous-summary> block, treat it as the current anchored summary."
opencode 在调用 compaction agent 时,如果有上次的 summary,会用 <previous-summary> XML 标签包装传入。这一句告诉 LLM——这个块是"现有锚点"。
判断 4:增量更新而非重写
"Update it with the new history by preserving still-true details, removing stale details, and merging in new facts."
这是 anchored summary 的核心。3 个动作:
- preserve still-true details——还成立的细节保留
- remove stale details——过时的细节删
- merge in new facts——新事实合并进来
不是"重新写一份摘要"——是"对现有摘要做精确调整"。
这种增量更新有几个好处:
- 旧细节精度保留(不会被重新提炼丢失)
- 新事实精确融入
- token 消耗低(LLM 不用重新读所有历史)
判断 5:保留 exact identifiers
"Keep every section, preserve exact file paths and identifiers when known, and prefer terse bullets over paragraphs."
明确告诉 LLM——精确的标识符(文件路径、变量名、函数名、URL、数字)必须保留 exact。不能"按意思总结"。
为什么?因为这些是 LLM 未来工作的"接口"。如果摘要说"用户改了 auth 相关文件"——LLM 后续想再改 auth 不知道改哪个文件。如果摘要说"用户改了 src/auth/login.ts 第 145 行"——LLM 直接知道。
精确标识符的保留是 anchored summary 的质量底线。
判断 6:用 bullets 不用 paragraphs
"prefer terse bullets over paragraphs"
为什么?因为 bullets 让 LLM 更容易"识别条目、判断哪个还有效、哪个该删"。
如果摘要是连贯叙事——"用户最初想做 X,于是 LLM 做了 A 和 B,但 B 有问题改成了 C..."——更新时 LLM 要"理解叙事重新组织",容易出错。
如果摘要是 bullet list:
- User goal: ...
- Decisions made: ...
- Files changed: ...
- Outstanding: ...
更新时 LLM 直接定位哪个 bullet 该改——精确高效。
判断 7:不提摘要本身
"Do not mention that you are summarizing, compacting, or merging context."
明确告诉 LLM——别说"以下是摘要"、"我合并了一些内容"、"我精简了对话"。
为什么?因为这些"元话术"会让最终塞回去的 summary 显得突兀。LLM 拿到 summary 继续工作时,应该感觉"这就是我的工作记忆"——不应该感觉"系统给我塞了一段元描述"。
这就是 4.7 章会讲的隐性管理原则——LLM 不应该感受到自己"被系统管理"。
把这 7 个判断合起来,compaction 的设计就是个精细的产品工程。不是"让 LLM 写个摘要"那么简单——它涉及身份定位、分工边界、增量逻辑、精度保证、格式约束、语气控制。
每一个判断都对应一个"不这么做会出什么问题"。这种细致是 opencode 团队大量实验积累的结果。
三、设计启示:增量压缩 vs 重新生成
这一章的核心论点:长 session 的摘要必须是 anchored 而非 from-scratch——增量更新避免精度漂移。
设计 AI 产品的 compaction 时,下面几条原则有用:
1. 第一次压缩可以 from-scratch
session 第一次满了——没有 previous summary——LLM 从所有历史里提炼。这没问题。
2. 后续压缩必须 anchored
第二次及以后的压缩——基于上次 summary 增量更新。不要每次 from-scratch。
3. previous summary 用 XML 标签明确
<previous-summary>...</previous-summary> 让 LLM 知道这是"基线"。
4. 明确 3 个增量动作
让 prompt 说清楚:
- preserve(还成立的保留)
- remove(过时的删)
- merge(新的加)
LLM 看到具体动作指南更可能正确执行。
5. 强调 exact identifiers
文件路径、变量名、数字——必须 exact 保留。这是 summary 后续可用的关键。
如果 summary 都是"用户大概想做 X"——LLM 后续也只能"大概做 X"。精度逐 turn 衰减。
6. 用 bullet 不用 prose
让 summary 结构化——每项一个 bullet。
更新时 LLM 能精确 target 某个 bullet 而不是"重新组织叙事"。
7. 用模板规定 summary 结构
让 summary 有固定 section:
## User Goal
...
## Decisions Made
...
## Files Modified
...
## Outstanding Tasks
...
## Constraints
...
每次更新都在这些 section 内修改——保持结构稳定。LLM 学会"我看 summary 时按 section 找信息"——查找效率高。
8. 别让 LLM "意识到"自己在做摘要
prompt 里明确"不要提摘要"。让最终 summary 看起来就是"工作记忆"——不是元描述。
LLM 拿到 summary 继续工作时,应该自然地"基于已有信息继续"——不应该意识到"这是别人给我压缩的"。
最后一个观察。Anchored summary 这个设计模式不只用于 compaction——任何"长期演化的 AI 工作"都可以用。
比如:
- AI 帮你管理项目,每周写一份"项目状态"摘要——用 anchored 模式,每周更新前次摘要
- AI 帮你跟踪研究进展,每月写一份"研究笔记"——anchored 模式让笔记不会断层
- AI 帮你写日记,每天写一段——anchored 模式让日记之间有连贯性
凡是"AI 持续产出 + 长期演化"的场景——anchored summary 都适用。它的本质是让 AI 的产出有"时间连续性"——不是孤立的快照,是有历史的演化。
这是 AI 产品工程里一个被低估的设计模式——把它学会,你能解决一类"AI 长期工作" 的根本问题。
下一章 4.6 我们看另一类"系统层注入"——mid-conversation 在对话中段塞新指令。这是 mode 切换、上下文更新等场景的实现机制。