上下文压缩最佳实践:AI 编程工具如何避免聊久后失忆
上下文压缩最佳实践:为什么 AI 编程工具聊久了会“失忆”,以及怎么避免
你用 AI 编程工具时,可能遇到过这些情况:
刚开始它理解得很好,聊久了以后突然开始跑偏;刚才还知道项目结构,过几轮又重新读文件;你明明强调过“不要修改代码”,压缩上下文后它却忘了;一次长任务做到一半,模型突然总结前文,然后继续工作。
这些现象背后,其实都和一个核心问题有关:
上下文太长了,客户端必须压缩。
无论是 OpenCode,还是 Claude Code,本质都一样:模型本身不会永久记住对话。客户端需要在每次请求前,把历史消息、工具结果、项目规则、摘要和当前任务重新组装成上下文。当内容太多时,就必须裁剪、摘要、折叠或重建。
上下文压缩不是一个边缘功能,而是 AI 编程工具能否稳定完成长任务的关键能力。
一、为什么必须压缩上下文?
AI 编程工具的一次任务里,上下文增长非常快。
一次普通代码分析可能包含:
系统提示
项目规则文件
历史对话
目录结构
读取过的源码
grep 搜索结果
bash 命令输出
测试日志
工具调用记录
模型中间解释
用户补充要求
这些内容都会占 token。
刚开始上下文还小,模型能看到完整现场。随着工具调用越来越多,文件内容和日志不断进入消息历史,上下文会快速膨胀。
如果不压缩,就会出现三个问题:
1. 请求超过模型上下文窗口
2. 输入 token 成本越来越高
3. 重要信息被海量工具输出淹没
所以成熟的 AI 编程客户端一定要做上下文压缩。
二、OpenCode 的压缩思路:摘要旧历史,保留最近上下文
OpenCode 的压缩逻辑比较清晰,可以理解为经典的“头部摘要 + 尾部保留”。
它会估算当前请求的上下文大小:
system + messages + tools
当上下文接近模型窗口上限时,就把历史拆成两段:
head:较早的历史,拿去总结
recent:最近的消息,原样保留
然后让模型生成一个结构化 summary,再把它作为后续对话的 checkpoint。
压缩后的上下文大概是:
conversation checkpoint summary
+ recent messages
+ 当前用户新输入
这种方式的优点是简单、直观、容易实现。
但它也有风险:如果 summary 写得不好,早期关键约束就会丢。
比如用户一开始说过:
这次只做架构分析,不要修改代码。
如果压缩摘要没有保留这句话,后面模型就可能忘掉这个限制。
所以 OpenCode 这种策略的关键在于:
摘要必须保留用户意图、关键决策、已读文件、工具结果、未完成任务和禁止事项。
三、Claude Code 的压缩思路:多层压缩,不只做摘要
Claude Code 的压缩体系更复杂。
它不是等上下文爆了以后简单总结,而是在真正请求模型前做多层处理:
1. 找最近一次 compact boundary
2. 只取 compact boundary 之后的活跃消息
3. 控制工具结果大小
4. 做 microcompact / snip / context collapse
5. 判断是否需要 auto compact
6. 生成 summary
7. 重新注入必要附件和状态
这比 OpenCode 更重,但也更适合真实长任务。
Claude Code 压缩后的上下文不是只有 summary,还会补回一些后续继续工作必须的信息:
compact boundary
summary message
最近保留消息
已读文件相关附件
plan mode 状态
已调用 skill 信息
MCP instructions
deferred tools 信息
session hooks 结果
也就是说,它不只是“总结历史”,还会努力恢复一个能继续工作的现场。
这点非常重要。
好的上下文压缩,不是把历史压短就完了,而是要保证:
模型压缩后还能继续干活
四、压缩最容易丢什么?
结合 OpenCode 和 Claude Code 的实现,可以总结出上下文压缩最容易丢的几类信息。
第一类是用户约束。
例如:
只分析,不改代码
不要运行耗时命令
不要删除文件
必须用中文回答
不要动 generated 目录
这类信息如果没有进入规则文件,也没有被 summary 明确保留,压缩后很容易丢。
第二类是任务状态。
例如:
已经读过哪些文件
当前分析到哪一步
哪些问题已经排除
下一步准备验证什么
压缩摘要如果只写结论,不写过程,模型后面可能会重复读文件、重复搜索、重复执行命令。
第三类是错误和修复路径。
例如:
刚才 npm test 失败
失败原因不是业务逻辑,而是缺少环境变量
用户已经明确说不要再纠结这个测试
如果这些没保留,模型可能会再次掉进同一个坑。
第四类是文件细节。
摘要可以保留文件名和结论,但很难保留完整代码细节。
所以压缩后模型重新读文件是正常现象。
第五类是工具输出里的关键片段。
比如长日志里只有一行关键错误:
Caused by: Table vip_member not found
如果压缩时只写“测试失败”,后续模型就失去了定位依据。
五、上下文压缩的最佳实践
下面这些实践,既适用于 AI 工具开发者,也适用于重度使用 AI 编程工具的人。
1. 不要把所有东西都放进规则文件
规则文件,比如 CLAUDE.md、AGENTS.md、项目 memory,应该只放每轮都值得提醒模型的稳定规则。
适合放:
项目使用 pnpm
测试命令是 pnpm test
不要修改 generated 目录
提交前必须运行 lint
回答必须使用中文
只允许改业务模块,不要改框架模块
不适合放:
完整业务说明
数据库全部表结构
历史需求文档
几千行接口说明
长篇排查日志
原因很简单:规则文件如果每轮都进入上下文,就会持续消耗 token,还会挤压真正任务上下文。
大文档应该让模型按需读取,而不是每轮自动注入。
2. 把关键约束写得短而硬
长任务里,最容易被压缩丢掉的是用户约束。
所以关键约束要写得短、明确、可摘要。
不推荐:
我们现在先看看这个项目吧,暂时应该不用动代码,主要还是希望你看看结构,除非特别有必要再说。
推荐:
约束:本轮只分析,不修改任何文件。
短句更容易被模型识别,也更容易进入压缩摘要。
3. 长任务开始时,先让模型建立任务清单
在复杂任务开始时,可以明确要求:
先列出你要阅读的文件和分析步骤,不要直接改代码。
这样上下文里会出现一个结构化计划。
后续即使压缩,summary 也更容易保留:
当前任务目标
已完成步骤
未完成步骤
下一步
这比散乱对话更抗压缩。
4. 每个阶段结束,让模型主动总结
长任务不要等系统自动压缩。
你可以阶段性要求:
总结当前进展:
1. 用户目标
2. 已读文件
3. 关键发现
4. 已排除问题
5. 当前结论
6. 下一步
7. 不能违反的约束
这其实是在帮未来的压缩机制准备高质量材料。
OpenCode 和 Claude Code 都依赖 summary 继续工作。你主动生成阶段总结,相当于给后续压缩提供锚点。
5. 工具输出要提炼,不要让日志淹没上下文
运行命令后,如果输出很长,最好让模型提取关键信息:
从这段日志里提取真正关键的错误行、堆栈位置和可能原因,不要全文复述。
工具输出是上下文膨胀的大头。
尤其是:
测试日志
构建日志
grep 大量结果
大型 JSON
长 SQL
如果不控制,模型后面看到的是一堆噪音。
好的实践是让模型把工具结果转成结构化结论:
命令:pnpm test
结果:失败
失败用例:UserService should create user
关键错误:Cannot find table vip_member
位置:UserRepository.java:42
判断:测试数据库 schema 未初始化
下一步:检查 migration 或 test setup
这种形式比几千行日志更有价值。
6. 文件内容不要长期依赖记忆,需要时重新读取
很多人会觉得:
模型刚才不是读过这个文件了吗,为什么又读?
其实这很正常。
文件内容通常很长,压缩后不可能完整保留。摘要最多保留:
读过 VipService.java,发现它承担过多职责。
但不会保留完整代码。
所以对关键代码细节,正确方式不是要求模型永远记住,而是接受它必要时重新读取。
更好的提示是:
如果需要确认代码细节,请重新读取对应文件,不要凭摘要猜。
这比模型基于残缺记忆乱推理更可靠。
7. 压缩摘要必须保留“下一步”
很多摘要只写了过去发生了什么,却没写下一步。这会导致压缩后模型继续工作时失去方向。
一个高质量压缩摘要必须包含:
当前正在做什么
刚刚做到哪一步
下一步应该做什么
为什么做这一步
用户有没有特别约束
例如:
当前正在分析 Claude Code 的上下文压缩机制。已经确认 autoCompact 会在 query 前触发,compactConversation 会生成 summary 并用 compact boundary 替换活跃上下文。下一步是对比 OpenCode 和 Claude Code 的压缩策略,并整理最佳实践。用户要求结合对话过程,写成通俗易懂的文章。
这种摘要能直接恢复工作现场。
8. 摘要里必须保留“用户原话”
OpenCode 的压缩提示里有一个很重要的点:要求列出所有非工具类用户消息。
这个设计很有价值。
因为用户原话往往包含意图变化和约束。
例如:
“试试求实,陈述事实”
“克隆下来的代码不要删”
“内容压缩到现有的一半”
“开始是不是应该有一些常见问题的钩子”
这些话看似零散,但决定了后续回答风格和任务边界。
最佳实践是:压缩摘要里要保留关键用户原话,尤其是最近几轮。
9. 区分“活跃上下文”和“历史记录”
Claude Code 里有一个非常重要的设计:UI/日志里可以保留完整历史,但模型请求只取 compact boundary 之后的活跃上下文。
这说明要区分两件事:
历史记录:给人看、用于恢复、可追溯
活跃上下文:真正发给模型、影响回答
用户经常误解:
我界面上还能看到,所以模型应该也能看到。
不一定。
压缩之后,模型看到的可能只是摘要和最近几轮。
所以,如果某个旧信息很关键,最好重新放进当前活跃上下文:
提醒:之前约束仍然有效,本轮只分析不改代码。
10. Prompt Cache 不是压缩的替代品
服务端 prompt cache 可以降低重复前缀的处理成本,但不能替代上下文压缩。
因为 prompt cache 解决的是:
重复前缀如何更便宜、更快
压缩解决的是:
上下文太长,模型窗口放不下
即使命中 cache,内容依然占上下文窗口。规则文件太大、工具列表太大、历史消息太大,仍然会挤压有效空间。
所以不要以为有 prompt cache,就可以无限塞上下文。
六、一次长对话应该怎么组织?
结合前面的实践,一个比较稳的长任务流程可以是这样:
1. 开始任务
明确目标、约束、输出形式
2. 建立计划
让模型列阅读路径和分析步骤
3. 分阶段执行
每阶段只读必要文件和命令结果
4. 阶段总结
总结已读文件、关键结论、问题、下一步
5. 继续下一阶段
用阶段总结承接,而不是依赖零散历史
6. 自动压缩发生后
检查摘要是否保留关键约束和当前任务
7. 必要时补充提醒
重新声明不能丢的规则
这套流程能显著降低长任务跑偏概率。
七、上下文压缩的理想摘要模板
可以直接使用这个模板:
请压缩当前上下文,保留后续继续工作必需的信息:
1. 用户目标
2. 用户明确约束
3. 当前任务进度
4. 已读取/修改/分析的文件
5. 关键发现
6. 已排除的问题
7. 工具执行结果中的关键错误或结论
8. 当前正在处理的问题
9. 下一步应该做什么
10. 最近几轮用户原话
如果是代码任务,可以加上:
11. 已修改文件和改动点
12. 尚未验证的风险
13. 需要重新读取确认的文件
这个模板的目的不是让摘要好看,而是让模型压缩后还能继续干活。
八、给 AI 工具开发者的建议
如果你在做自己的 AI 编程工具,可以参考 OpenCode 和 Claude Code 的经验。
基础版可以学 OpenCode:
超过阈值
-> 拆分 head/recent
-> head 生成 summary
-> recent 原样保留
-> summary + recent 继续
进阶版可以学 Claude Code:
压缩前先处理工具结果
保留最近消息
生成 compact boundary
重注入必要文件/规则/工具信息
保留用户约束和下一步
区分 UI 历史和模型活跃上下文
监控 cache hit/miss
最重要的是,不要只追求“压得短”。
真正好的压缩目标是:
压缩后仍然能继续准确工作
最后总结
上下文压缩不是简单省 token,而是在有限窗口里保留任务连续性。
OpenCode 给了我们一个清晰模型:
旧历史总结,最近消息保留。
Claude Code 给了我们一个更工程化的模型:
多层压缩,边界切分,摘要替代,必要上下文重注入。
对使用者来说,最佳实践是:
规则文件短而稳定
关键约束明确重复
长任务阶段性总结
工具输出提炼成结论
重要文件必要时重新读取
压缩摘要必须保留当前任务和下一步
对工具开发者来说,最重要的判断是:
压缩不是为了让历史变短,而是为了让模型在失去完整历史后,依然知道自己是谁、用户要什么、已经做了什么、下一步该做什么。
这就是上下文压缩的核心。