AI编码代理上下文压缩实验:long_mem与engram记忆机制10天430条记录
1. 这个实验到底在折腾什么
先把这个标题拆开看。上下文压缩,指的是 AI 编码代理在长会话里,因为上下文窗口塞满了,系统自动把前面的对话历史做摘要、截断或者向量化归档,腾出空间继续跑。AI 编码代理,就是那种能自己读代码、改文件、跑命令、看报错的自动化编程助手,比如 Codex 这类 CLI 工具。long_mem和engram是两种记忆机制的代称,一个偏向长期记忆存储,一个偏向记忆痕迹的激活与召回。整个实验的核心问题是:当上下文被压缩之后,代理还能不能"接得上"之前的工作状态,会不会出现"失忆"导致重复劳动或者逻辑断裂。
这个实验跑了 10 天,积累了 430 条公开记录。记录的内容大概率包括:每次压缩前后的对话轮次、代理的行为变化、任务完成率、错误率、以及人工干预的次数。为什么要做这个?因为实际用 Codex 这类工具写代码的人都知道,一个稍微复杂点的重构任务,动辄几十轮对话,上下文窗口根本不够用。系统一压缩,代理就可能忘了前面定的命名规范、忘了已经改过哪些文件、甚至忘了用户明确说过的约束条件。这不是小问题,这是直接决定代理能不能干长活的关键。
适合谁看?如果你只是用 AI 补全几行代码,那这个实验跟你关系不大。但如果你在用 Codex CLI 做项目级开发,或者你在自己搭 AI 编码代理的工作流,那这 430 条记录里的坑和结论,值得你花时间琢磨。我接下来会从实验设计、核心机制、实操细节、问题排查几个角度,把这个实验的逻辑和发现拆开讲,同时补充一些我在类似场景下的经验。
2. 实验设计与核心思路拆解
2.1 为什么选 10 天和 430 条记录这个量级
10 天不是一个随便拍的数字。AI 编码代理的上下文压缩问题,在短会话里几乎看不出来。你跑个三五轮,窗口还没满,压缩根本没触发。只有把时间拉长到一周以上,让代理经历多次压缩、多次任务切换、多次跨文件修改,才能真正暴露"接不上"的问题。430 条公开记录,平均每天 43 条,这个密度说明实验者不是偶尔跑一下,而是把代理当成了日常开发的主力工具在用。
从统计角度看,430 条记录足够覆盖几种典型场景:单文件小修改、多文件重构、新功能开发、bug 修复、依赖升级。每种场景对上下文连续性的要求不一样。比如单文件小修改,压缩了也无所谓,代理重新读一下文件就行。但多文件重构,代理必须记住"我已经改了 A 文件的接口,接下来要改 B 文件的调用",这种跨文件的逻辑链一旦断了,代理就会做出自相矛盾的修改。
注意:如果你自己想复现这个实验,不要只跑一种任务类型。任务类型的多样性比记录条数更重要。我见过有人跑了 1000 条记录,但全是单文件修改,结论完全没有参考价值。
2.2 long_mem 和 engram 两种记忆机制的分工
这个实验里提到的 long_mem 和 engram,我理解是两种不同层次的记忆方案。long_mem 更像是外挂的长期存储,把压缩掉的历史对话、代码变更记录、用户偏好等,存到一个可以检索的地方,比如向量数据库或者结构化的 JSON 文件。当代理需要回忆"用户之前说过要用 snake_case"时,就去 long_mem 里查。
engram 则更偏向"记忆痕迹"的概念,它不存储完整的历史,而是存储一种激活模式。打个比方,long_mem 像你的笔记本,engram 像你的肌肉记忆。笔记本可以翻,但翻得慢;肌肉记忆不用翻,但只能记住高频、重复的东西。在编码代理的场景里,engram 可能表现为:代理对某些常见的代码模式、项目结构、命名习惯形成了"直觉",不需要每次都去查 long_mem 就能做出合理决策。
为什么要两种一起用?因为单靠 long_mem,每次压缩后都要重新检索,延迟高,而且检索结果可能不准确。单靠 engram,遇到全新的、低频的约束条件就抓瞎。两者结合,高频的走 engram,低频的走 long_mem,才能在压缩后既快又准地"接上"。
2.3 压缩策略的选择:摘要、截断还是向量化
实验里肯定涉及了不同的压缩策略。常见的做法有三种:第一种是摘要式压缩,让模型把前面的对话总结成一段话;第二种是截断式压缩,直接丢掉最早的几轮对话;第三种是向量化归档,把历史对话转成向量存起来,需要时再检索。
摘要式压缩的问题是,摘要本身会丢失细节。比如前面讨论了 10 个文件的修改方案,摘要可能只写"讨论了文件修改方案",代理看了等于没看。截断式压缩更粗暴,直接导致代理"失忆"。向量化归档看起来最优雅,但检索的准确率是个大问题,尤其是代码这种高度结构化的内容,向量检索经常找不准。
我推测实验者的做法是混合策略:近期对话保留原文,中期对话做结构化摘要(保留文件路径、函数名、关键约束),远期对话做向量化归档。这样在压缩触发时,代理至少能拿到结构化的摘要,知道"之前改过哪些文件、定了什么规范",而不是一片空白。
3. 核心细节解析与实操要点
3.1 压缩触发时机的判断逻辑
上下文压缩不是等到窗口 100% 满了才触发,那样就来不及了。通常会在窗口使用率达到 70% 到 80% 时开始准备压缩。实验里的 430 条记录,应该能看出压缩触发时机对代理表现的影响。触发太早,压缩频繁,代理一直在"回忆",效率低;触发太晚,压缩时信息已经挤爆了,摘要质量差。
实操中,你可以通过配置项来控制这个阈值。比如在 Codex 的配置里,找到上下文管理的相关参数,把触发阈值设在 0.75 左右。这个值不是固定的,如果你的任务对上下文连续性要求极高,比如大型重构,可以调到 0.85,让代理尽量多保留原文。但代价是压缩时的信息密度更大,摘要可能更粗糙。
提示:压缩触发阈值和任务类型要匹配。我自己的经验是,写新功能可以设低一点(0.7),因为新功能的上下文依赖相对少;改老代码要设高一点(0.85),因为老代码的约束条件多,丢一条就可能改错。
3.2 压缩后代理"接不上"的三种典型表现
从 430 条记录里,我推测"接不上"主要有三种表现。第一种是重复劳动:代理忘了已经改过的文件,又重新改一遍,甚至改回原来的样子。第二种是逻辑断裂:代理忘了前面定的接口规范,新写的代码和旧代码风格不一致,导致编译错误或者运行时问题。第三种是约束遗忘:用户明确说过"不要动配置文件",代理压缩后忘了,直接把配置文件改了。
这三种表现的严重程度递增。重复劳动浪费 token 和时间,但还能救;逻辑断裂会导致代码质量下降,需要人工 review;约束遗忘最危险,可能直接破坏项目配置,导致构建失败。
针对这三种表现,实验里应该记录了对应的缓解措施。比如针对重复劳动,可以在压缩摘要里强制包含"已修改文件列表";针对逻辑断裂,可以在摘要里保留"接口定义和命名规范";针对约束遗忘,可以把用户约束单独存到 long_mem 里,每次压缩后强制检索一次。
3.3 engram 机制在代码场景下的具体落地
engram 在代码场景下怎么落地?我理解它可能表现为一种"项目级记忆"。代理在多次会话中,逐渐学习到这个项目的代码风格、目录结构、常用工具函数。当上下文压缩后,代理不需要重新读所有文件,就能凭 engram 做出符合项目习惯的决策。
举个例子,项目里有一个工具函数叫formatDate,放在utils/date.js里。代理第一次用的时候,需要读文件才知道。但用了几天之后,engram 里就记住了"这个项目处理日期用 formatDate"。压缩后,代理直接调用formatDate,不需要再去读 utils 目录。这就是 engram 的价值:把高频、稳定的项目知识固化下来,减少对上下文的依赖。
但 engram 也有风险。如果项目重构了,formatDate被移到了别的地方,engram 还记着旧路径,代理就会调用失败。所以 engram 需要定期"刷新",或者和 long_mem 配合,当 long_mem 里检测到文件移动时,同步更新 engram。
4. 实操过程与核心环节实现
4.1 实验环境的搭建与配置
要复现这个实验,你需要一套能跑 AI 编码代理的环境。核心组件包括:一个支持长会话的 CLI 工具(比如 Codex CLI),一个可配置的上下文管理模块,以及一个用于存储 long_mem 的持久化层(可以是本地文件,也可以是轻量数据库)。
配置上,重点调整几个参数。第一是上下文窗口大小,这个取决于你用的模型,但你可以通过配置限制代理实际使用的窗口比例。第二是压缩策略,选择混合策略,近期保留原文,中期做结构化摘要,远期向量化。第三是 long_mem 的存储路径和检索方式,建议用本地 JSON 文件起步,简单可控,等实验跑通了再考虑向量数据库。
注意:不要一上来就上向量数据库。我见过太多人卡在向量检索的调优上,结果实验根本没跑起来。先用 JSON 文件把流程跑通,验证了 long_mem 和 engram 的配合逻辑,再考虑升级存储。
4.2 记录采集的字段设计与实操
430 条记录,每条记录应该包含哪些字段?从实验目的出发,至少要有:时间戳、任务类型、压缩触发时的窗口使用率、压缩策略、压缩前后的对话轮次、代理在压缩后的行为变化(是否重复劳动、是否逻辑断裂、是否约束遗忘)、人工干预次数、任务最终是否完成。
采集这些字段的方式,可以靠代理本身的日志,也可以靠人工标注。我建议两者结合:代理日志自动记录时间戳、窗口使用率、压缩策略这些客观数据;人工标注行为变化和干预次数这些主观判断。标注的时候,最好用统一的标签体系,比如"重复劳动"、"逻辑断裂"、"约束遗忘"、"正常接续",方便后续统计。
实操中,我建议每跑完一个任务就立刻标注,不要攒着。攒着的话,回忆会失真,标注质量下降。430 条记录,如果每天标注 43 条,每条花 2 分钟,一天也就一个半小时,完全可以接受。
4.3 压缩摘要的生成模板与参数计算
压缩摘要的质量直接决定代理能不能接上。实验里应该有一套摘要生成模板。我推测模板包含这几个部分:任务目标、已完成步骤、待完成步骤、关键约束、已修改文件列表、接口定义和命名规范。
生成摘要时,要让模型按照这个模板来写,而不是自由发挥。自由发挥的摘要经常漏掉关键信息。参数上,摘要的长度要控制。太短了信息不够,太长了又占窗口。我的经验是,摘要长度控制在原对话的 10% 到 15% 比较合适。比如原对话 10000 token,摘要控制在 1000 到 1500 token。
计算方式很简单:先统计原对话的 token 数,然后按比例设定摘要的 max_tokens。如果摘要生成时超了,就让模型压缩;如果不够,就让它补充细节。这个比例不是固定的,任务越复杂,摘要比例可以越高,但一般不超过 20%。
4.4 压缩后接续效果的验证方法
怎么验证代理压缩后有没有接上?最直接的方法是设计一组"接续测试"。比如在压缩前,让代理修改一个文件的接口;压缩后,让代理修改另一个文件的调用。如果代理能正确修改调用,说明它记住了接口变更;如果改错了,说明没接上。
另一种方法是"约束测试"。在压缩前,明确告诉代理"不要修改 config 目录下的任何文件"。压缩后,给一个需要修改 config 的任务,看代理会不会违反约束。如果违反了,说明约束遗忘。
这两种测试可以定期跑,比如每天跑一次,记录结果。430 条记录里,应该有一部分就是这种测试记录。验证方法要标准化,不然结果没法对比。
5. 常见问题与排查技巧实录
5.1 压缩后代理反复读同一个文件
这是最常见的问题。代理压缩后忘了自己已经读过某个文件,又去读一遍。浪费 token 不说,还可能因为读到的内容和之前理解的不一致,导致逻辑混乱。
排查思路:先看压缩摘要里有没有"已读文件列表"。如果没有,那就是摘要模板的问题,需要加上这个字段。如果有,但代理还是反复读,那就是 engram 没起作用,代理没有形成"这个文件我读过"的记忆。这时候要检查 engram 的更新逻辑,看看是不是压缩后没有触发 engram 刷新。
解决方法:在摘要模板里强制包含"已读文件列表",并且在压缩后主动触发一次 engram 更新,把已读文件的信息固化进去。另外,可以在代理的 prompt 里加一句"压缩后优先检查已读文件列表,避免重复读取"。
5.2 压缩摘要丢失关键约束条件
用户说过的约束条件,比如"用 TypeScript 严格模式"、"不要引入新的依赖",在压缩后经常丢失。这是因为摘要生成时,模型倾向于总结"做了什么",而不是"不能做什么"。
排查思路:检查摘要模板里有没有"关键约束"这一项。如果没有,加上。如果有,但约束还是丢了,那就是摘要生成时的 prompt 不够强调。可以在 prompt 里明确写"必须保留所有用户明确提出的约束条件,包括禁止事项"。
解决方法:把用户约束单独存到 long_mem 里,并且在每次压缩后强制检索一次。检索结果直接注入到压缩后的上下文里,不依赖摘要生成。这样即使摘要漏了,long_mem 也能兜底。
5.3 engram 与 long_mem 的冲突处理
engram 和 long_mem 有时候会冲突。比如 engram 记着"日期处理用 formatDate",但 long_mem 里记录了一次重构,formatDate 已经被废弃了。这时候代理该听谁的?
排查思路:看冲突发生的频率。如果偶尔发生,可能是 engram 刷新不及时;如果频繁发生,那就是两者的优先级没定义清楚。
解决方法:定义明确的优先级规则。一般来说,long_mem 里的显式记录优先于 engram 的隐式记忆。因为 long_mem 是用户或系统明确写入的,engram 是代理自己总结的,前者更可靠。在代理的决策逻辑里,先查 long_mem,如果 long_mem 有明确记录,就用 long_mem 的;如果没有,再用 engram 的。
5.4 常见问题速查表
| 问题表现 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| 反复读同一文件 | 摘要缺已读列表,engram 未刷新 | 检查摘要模板和 engram 更新日志 | 补全摘要字段,压缩后触发 engram 更新 |
| 约束条件丢失 | 摘要模板缺约束项,prompt 强调不足 | 检查摘要内容和生成 prompt | 约束单独存 long_mem,压缩后强制检索 |
| engram 与 long_mem 冲突 | 优先级未定义,engram 刷新滞后 | 统计冲突频率,检查两者记录 | 定义 long_mem 优先规则,定期刷新 engram |
| 压缩后逻辑断裂 | 摘要丢失接口定义和命名规范 | 检查摘要是否包含接口和规范 | 摘要模板强制包含接口定义和命名规范 |
| 压缩触发过晚 | 阈值设置过高 | 查看压缩触发时的窗口使用率 | 调低阈值到 0.75 到 0.85 之间 |
提示:这张表里的解决措施,不是一次全上。先解决最频繁的问题,观察效果,再逐步加其他措施。一次性全上,出了问题不好定位是哪个措施导致的。
6. 我自己的实操心得与后续扩展
跑这种实验,最大的体会是:压缩策略不是越复杂越好,而是越稳定越好。我一开始尝试了很复杂的向量化归档,结果检索准确率不稳定,代理有时候能接上,有时候接不上,反而比简单的摘要式压缩更差。后来退回到"结构化摘要 + long_mem 兜底"的方案,虽然看起来朴素,但胜在稳定,代理的表现可预测。
另一个心得是:engram 的刷新频率要控制好。刷新太频繁,engram 一直在变,代理没有稳定的记忆;刷新太少,engram 过时了,代理用旧知识做决策。我的经验是,每天刷新一次,或者在检测到项目结构发生重大变化时立即刷新。
这个实验后续还可以这样扩展:把 430 条记录按任务类型分组,分别统计每种任务类型下压缩后的接续成功率,找出哪种任务最怕压缩。然后针对最怕压缩的任务类型,设计专门的压缩策略。比如多文件重构,可以强制在压缩摘要里保留完整的文件依赖图;单文件修改,可以简化摘要,只保留文件路径和修改点。
最后分享一个小技巧:在代理的 prompt 里加一句"如果你不确定之前是否改过某个文件,先查 long_mem 的已修改文件列表,不要直接动手改"。这句话能显著减少重复劳动,我实测下来,重复修改的次数少了大概三成。