返回 AI编程

上下文压缩最佳实践: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.mdAGENTS.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 给了我们一个更工程化的模型:

多层压缩,边界切分,摘要替代,必要上下文重注入。

对使用者来说,最佳实践是:

规则文件短而稳定
关键约束明确重复
长任务阶段性总结
工具输出提炼成结论
重要文件必要时重新读取
压缩摘要必须保留当前任务和下一步

对工具开发者来说,最重要的判断是:

压缩不是为了让历史变短,而是为了让模型在失去完整历史后,依然知道自己是谁、用户要什么、已经做了什么、下一步该做什么。

这就是上下文压缩的核心。