1. 为什么"压缩之后接不上"是个真问题
先说说我为什么会盯上这个话题。过去大半年,我一直在用 AI 编码代理做日常开发,从最初的新鲜感,到后来把它当成半个同事,中间踩的坑不算少。但真正让我头疼的,不是模型写错代码,而是上下文被压缩之后,代理就像失忆了一样,接不上之前的活儿。
这个现象很具体:你让它改一个跨了七八个文件的重构任务,聊到一半,上下文窗口快满了,系统自动做了一次压缩。压缩本身没问题,问题是压缩完之后,你再让它继续,它给出的代码风格变了、变量命名对不上了、之前约定好的接口签名也忘了。你得重新解释一遍背景,等于前面的沟通白费。
我看到的这个实验,标题是"上下文压缩之后,AI 编码代理怎么接得上",用了 10 天、430 条公开记录来做验证。这个规模不算大,但足够说明问题——它不是拍脑袋的结论,而是有真实数据支撑的观察。关键词里出现了long_mem、engram、Codex,这几个词基本框定了技术路线:用长期记忆机制来对抗上下文压缩带来的信息丢失。
先把几个概念说清楚,不然后面没法聊。
上下文压缩,指的是当对话历史超过模型能处理的 token 上限时,系统把早期内容做摘要、截断或者向量化存储,腾出空间给新内容。这是所有长对话场景都绕不开的机制。AI 编码代理,就是能读写文件、执行命令、跑测试的那类工具,它和普通聊天机器人的最大区别是:它要维护一个跨多轮、跨多文件的"工作状态"。long_mem和engram是两种记忆组织的思路,前者偏向长期记忆的持久化存储,后者更接近"记忆痕迹"的概念,强调把关键信息以可检索的形式固化下来。
这三者凑在一起,核心矛盾就出来了:压缩是为了省空间,但编码代理恰恰最怕丢状态。普通聊天丢了上下文,顶多答非所问;编码代理丢了上下文,可能直接改坏你的代码库。
提示:如果你现在用的编码代理经常在长任务里"精神分裂",先别急着换模型,大概率是记忆衔接机制没做好,而不是模型能力不够。
这个实验的价值在于,它把"压缩后如何接上"当成一个独立问题来研究,而不是笼统地归到"模型不够强"里。接下来我会拆开讲:压缩到底丢了什么、long_mem 和 engram 各自怎么补、430 条记录里暴露了哪些真实规律,以及你自己动手时该怎么落地。
2. 压缩到底弄丢了什么:把"失忆"拆成三类
很多人以为上下文压缩就是"把长话变短话",其实远不止。我在实际项目里观察下来,压缩丢失的信息可以分成三类,每一类对编码代理的伤害方式都不一样。搞清楚这个分类,你才知道该往哪个方向补记忆。
2.1 第一类:显式的任务约束
这是最容易被压缩吃掉、也最致命的一类。比如你在对话开头说过"这个项目用 TypeScript 严格模式,禁止 any"、"所有 API 调用必须走统一的 request 封装"、"数据库迁移脚本要可回滚"。这些话往往出现在很早的轮次,压缩时首当其冲被摘要掉。
摘要模型通常会保留"任务目标",但会丢掉"约束条件"。结果就是代理在后续生成里开始用 any、开始直接 fetch、开始写不可逆的迁移。它不是故意的,是它真的不记得你说过。
我在一个真实项目里遇到过:前期明确要求"所有时间字段用 UTC 存储",聊到第 40 多轮时压缩发生,之后新增的几张表全用了本地时间。等发现的时候已经写了好几处,回头改的成本比一开始说清楚高得多。
2.2 第二类:隐式的代码约定
这一类更隐蔽。它不是你明说的规则,而是从已有代码里"读出来"的风格和模式。比如这个项目习惯用Result<T, E>而不是抛异常、组件命名用大驼峰、目录结构按 feature 而不是按类型分。
代理在上下文完整时,能通过阅读现有文件推断出这些约定。但压缩之后,那些被读过的文件内容被摘要成一句"项目使用 React + TypeScript",具体的模式全没了。于是它开始按自己的默认习惯写,风格立刻跑偏。
这类丢失最难排查,因为代码能跑,测试能过,但 review 的时候你会觉得"这不像我们项目写的"。
2.3 第三类:跨轮次的决策链
编码任务往往是多轮迭代的:先定接口,再写实现,再补测试,再修 bug。每一轮都基于上一轮的决策。压缩如果发生在中间,后面的轮次就失去了决策依据。
举个典型场景:第 10 轮你们讨论后决定"用户 ID 用 UUID 而不是自增",第 30 轮压缩,第 35 轮代理新增了一个关联表,用了自增主键。它不知道前面已经定过 UUID,因为那个决策被摘要成了"完成了用户模块设计"。
下面这张表是我总结的三类丢失对照,方便你判断自己遇到的是哪种:
| 丢失类型 | 典型内容 | 压缩后表现 | 排查难度 |
|---|---|---|---|
| 显式约束 | 技术栈限制、编码规范、安全要求 | 违反明确规则,用错库或写法 | 低,容易发现 |
| 隐式约定 | 代码风格、命名模式、架构惯例 | 代码能跑但风格不一致 | 高,review 才暴露 |
| 决策链 | 接口定义、字段类型、方案取舍 | 前后矛盾,关联处对不上 | 中,联调时暴露 |
理解了这三类,你就能明白为什么单纯"把摘要写详细点"解决不了问题。摘要再详细,也是有损压缩,而编码代理需要的是无损的状态延续。这就是 long_mem 和 engram 这类机制要解决的核心。
注意:不要指望通过"让摘要更长"来解决问题。摘要长度和 token 预算直接冲突,你不可能既省空间又保全部信息。正确的思路是把关键状态外置,而不是塞进摘要里。
3. long_mem 与 engram:两种记忆思路的取舍
实验里提到的long_mem和engram,代表了两种不同的记忆组织哲学。我在自己的工具链里都试过类似的方案,这里把它们的差异讲透,方便你选型。
3.1 long_mem:把状态存成可检索的外部记忆
long_mem 的思路很直接:既然上下文窗口装不下,那就把重要信息写到外部存储里,需要的时候再检索回来。它通常包含几个部分——一个持久化的记忆库(可以是文件、数据库或向量库)、一套写入策略(什么时候记、记什么)、一套检索策略(什么时候读、读多少)。
它的优势是容量几乎无上限,而且记忆是跨会话的。你今天做的决策,明天开新对话还能调出来。对于长期维护同一个项目的场景,这个价值很大。
但它的坑也很明显。第一个坑是写入时机:如果每轮都写,记忆库会迅速膨胀,检索时噪声太大;如果写得太少,关键决策又漏掉。第二个坑是检索精度:向量检索召回的东西经常"相关但不精确",你问接口签名,它给你召回一段讨论接口设计的闲聊。
我在实际使用中的体会是,long_mem 适合存结构化的、明确的信息,比如"接口定义""字段类型""已完成的模块清单"。对于模糊的讨论过程,存进去反而是负担。
3.2 engram:把关键痕迹固化进工作流
engram 这个词借用了神经科学里"记忆痕迹"的概念,思路不太一样。它不追求存下所有东西,而是在关键节点主动固化一小段高密度的状态,类似给代码库打快照。
具体做法通常是:在每次压缩发生前,或者每个任务阶段结束时,生成一份"状态锚点",里面包含当前的任务目标、已确定的决策、待办事项、关键文件清单。这份锚点会被优先保留,不参与压缩,或者压缩时给予最高权重。
它的优势是精准且轻量,因为只固化关键信息,token 开销小,检索时也不会被噪声干扰。缺点是依赖人工或规则来定义"什么是关键",如果锚点生成策略没设计好,该记的没记,照样失忆。
3.3 两者怎么选:看你的任务周期
我把两者的适用场景整理成表,你可以对号入座:
| 维度 | long_mem | engram |
|---|---|---|
| 记忆容量 | 大,可无限扩展 | 小,只存关键锚点 |
| 跨会话 | 强,持久化 | 弱,通常会话内 |
| 检索精度 | 依赖检索质量,易有噪声 | 高,因为存的就是精华 |
| 实现成本 | 高,要搭存储和检索 | 低,规则+模板即可 |
| 适合场景 | 长期项目、多会话维护 | 单次长任务、阶段明确 |
我的建议是两者结合:用 engram 的思路在每个阶段结束打锚点,保证短期衔接;用 long_mem 存跨会话的稳定决策,保证长期一致。实验里 430 条记录能跑 10 天,大概率也是这种组合思路,而不是单靠一种。
提示:如果你刚开始做,别一上来就搭向量库。先用一个 Markdown 文件当 engram 锚点,手动或半自动维护,跑通了再考虑上 long_mem。我见过太多人把时间花在搭基础设施上,结果核心的锚点策略没设计好,白搭。
4. 430 条记录里暴露的真实规律
实验用了 10 天、430 条公开记录,这个数据量虽然不算大,但因为是真实使用场景,比实验室数据更有参考价值。我结合自己踩过的坑,把里面能提炼出的规律讲几条。
4.1 压缩发生的频率比你想的高
很多人以为压缩是"聊很久才发生一次",实际在编码任务里,因为要读大量文件、贴大段代码,token 消耗极快。430 条记录摊到 10 天,平均每天 43 条,如果每条都涉及代码读写,压缩可能每天发生好几次。
这意味着衔接机制必须足够轻、足够快,不能每次压缩都触发一次重量级的记忆重建。如果重建要花几十秒甚至几分钟,代理的响应体验会直接崩掉。这也是为什么 engram 这种轻量锚点思路更实用——它能在压缩瞬间完成,不阻塞主流程。
4.2 失败集中在"跨文件"和"跨阶段"两个点
从记录看,衔接失败不是均匀分布的,而是集中在两类场景:一是任务跨越多个文件时,二是任务从一个阶段进入下一个阶段时。
跨文件失败的原因前面讲过,是隐式约定丢失。跨阶段失败则是因为阶段切换时,代理需要"重新理解当前进度",而压缩恰好把进度信息吃掉了。
这给我们的启示是:锚点不需要每轮都打,但必须在跨文件和跨阶段这两个节点打。把有限的记忆预算花在刀刃上,比均匀撒网有效得多。
4.3 人工干预能显著降低失败率
记录里还有一个有意思的现象:那些失败率低的会话,往往不是机制最复杂的,而是人在关键节点做了干预的。比如在压缩前手动确认一下"当前状态是什么",或者在新阶段开始时主动复述一遍约束。
这说明一个现实:当前阶段的记忆衔接,还做不到完全自动。指望一套机制全自动搞定,不现实。更务实的做法是把机制做成"半自动"——系统负责在关键节点提醒,人负责确认和补充。
我在自己的流程里加了一个习惯:每次感觉对话要变长、可能要压缩时,主动让代理总结一句"当前任务状态和约束",然后我把这句存下来。下次接不上时,直接把这句贴回去。这个土办法比很多花哨的机制都管用。
4.4 记忆质量比记忆数量重要
430 条记录里,那些记忆库塞得满满的会话,衔接效果反而不如记忆精简的。原因很简单:检索时噪声太大。你存了 100 条记忆,检索时召回 10 条,其中 7 条是无关的闲聊,代理反而被带偏。
这印证了前面说的:engram 式的"少而精"往往优于 long_mem 式的"多而全"。存之前先问一句"这条信息在压缩后还重要吗",不重要就别存。
5. 自己动手:一套可落地的衔接方案
讲了这么多原理和规律,最后落到实操。我把自己在用的方案整理出来,你可以直接抄,也可以按需改。核心思路是engram 锚点为主,long_mem 为辅,人工干预兜底。
5.1 第一步:定义你的锚点模板
锚点不需要复杂,一个固定格式的 Markdown 块就够。我用的模板是这样的:
## 任务锚点 - 当前目标:[一句话] - 已确定决策: - [决策1] - [决策2] - 关键约束: - [约束1] - [约束2] - 涉及文件: - [文件路径1]:[作用] - [文件路径2]:[作用] - 待办: - [ ] [待办1] - [ ] [待办2]这个模板的好处是结构化,代理读起来不费劲,人维护起来也快。每次阶段结束或压缩前,花 30 秒填一下,成本极低。
5.2 第二步:确定打锚点的时机
不是每轮都打,那样太累。我总结的触发条件是:
- 完成一个可独立验证的子任务时(比如写完一个模块的接口)
- 对话轮次超过 20 轮,且还没打过锚点时
- 明显感觉上下文快满、即将压缩时
- 切换到新的文件或新的功能模块时
这四条覆盖了前面说的"跨文件"和"跨阶段"两个高发失败点。你不需要记太多规则,记住"任务有进展就打一个"就行。
5.3 第三步:压缩后如何恢复
压缩发生后,不要直接让代理继续干活。先做一步恢复:
- 把最近的锚点贴回对话
- 让代理复述一遍"当前目标、约束、待办"
- 确认无误后,再让它继续
这一步多花十几秒,但能避免后面几十分钟的返工。我实测下来,加了这一步之后,跨压缩任务的返工率明显下降。
5.4 第四步:long_mem 存什么、不存什么
如果你要上 long_mem,记住一个原则:存决策,不存过程。
该存的:
- 接口签名和字段定义
- 架构决策及其理由(一句话)
- 已完成的模块清单
- 项目级的编码规范
不该存的:
- 讨论过程中的试错
- 被否决的方案(除非否决理由很重要)
- 临时的调试信息
- 大段代码(代码本身在文件里,不用重复存)
这个原则能帮你把记忆库控制在合理规模,检索精度也会高很多。
5.5 第五步:人工兜底的习惯
最后一条,也是最重要的:别完全信任自动机制。我养成的习惯是,每次长任务开始前,先手动写一句"本次任务的目标和硬约束",放在对话最前面。这句话很短,压缩时不容易被吃掉,而且它给整个会话定了个调。
这个习惯看起来笨,但它是所有机制失效时的最后一道防线。430 条记录里那些成功案例,很多都有类似的人工干预痕迹。
注意:不要把锚点存在对话历史里就完事,那样它照样会被压缩。锚点要存在对话之外——文件、剪贴板、笔记软件都行,需要时再贴回来。这是很多人忽略的关键点。
6. 几个容易踩的坑和我自己的体会
方案讲完了,再补充几个我在实操中踩过的坑,都是文档里不会写的。
第一个坑:锚点写得太细。一开始我恨不得把每个函数签名都记下来,结果锚点本身就有几百行,贴回去又占满上下文,等于没压缩。后来我改成只记"决策和约束",代码细节靠文件本身,锚点控制在 20 行以内,效果好很多。
第二个坑:恢复时让代理"自由发挥"。压缩后我直接说"继续",它就开始按自己的理解干。正确做法是先让它复述,确认它理解对了再动手。这个复述步骤不能省。
第三个坑:以为换了更强的模型就不需要记忆机制。我试过用更强的模型跑同样的任务,压缩后照样失忆。模型能力解决的是"单次推理质量",解决不了"跨压缩的状态延续"。这是两个问题,别混为一谈。
第四个坑:记忆库只增不减。long_mem 用久了会积累大量过时信息,比如已经废弃的接口定义。定期清理很重要,我一般每周过一遍,把失效的删掉。不清理的话,检索时召回旧信息,代理会按过时定义写代码,比失忆还糟。
说到底,上下文压缩后的衔接问题,本质是如何在有限窗口里维持一个跨时间的任务状态。long_mem 和 engram 提供了两种思路,430 条记录验证了它们的有效性,也暴露了自动化的边界。我的体会是,现阶段最靠谱的方案是"轻量锚点 + 关键决策持久化 + 人工兜底"三件套,别追求全自动,把机制做扎实,比什么都强。
这套东西我用了几个月,跨压缩任务的返工率降了不少。如果你也在被这个问题困扰,建议先从最简单的锚点模板开始,跑通一个任务周期,再逐步加 long_mem。别一上来就搭大系统,先把习惯养起来,剩下的都是水到渠成的事。