ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AI编码代理上下文压缩后如何保持连续性:long_mem与engram实践

AI编码代理上下文压缩后如何保持连续性:long_mem与engram实践 1. 这个实验到底在折腾什么先交代背景。我日常重度依赖 AI 编码代理也就是那种能自己读代码、改文件、跑命令、看报错的工具。用得多了就会撞上一个绕不开的墙上下文窗口是有限的。一个稍大的项目光是把相关文件、历史对话、工具调用结果塞进去token 数就爆了。于是几乎所有代理都会做一件事——上下文压缩把旧的对话摘要化、把冗余的工具输出裁掉腾出空间给新内容。问题就出在这。压缩之后代理经常断片。上一轮它明明已经定位到某个函数有问题压缩一轮回来它又开始从头翻文件之前已经确认过的接口约定它转头就忘更离谱的是它会把摘要里的一句话当成事实然后基于这个二手信息做出错误修改。这不是模型笨是压缩把连续性给切断了。所以我做了这个实验10 天430 条公开记录专门观察上下文压缩之后AI 编码代理到底靠什么接得上。核心关注三个东西long_mem长期记忆机制、engram记忆痕迹/记忆印迹这个概念在代理里的落地、以及Codex这类代理在压缩后的行为表现。说白了我想搞清楚一件事——压缩不可避免那怎么让代理在压缩之后还能记得我是谁、我在干嘛、我干到哪了。这篇东西适合两类人看。一类是天天用编码代理、被断片折磨过的开发者另一类是想自己搭代理、正在设计记忆模块的工程师。我会把 10 天里踩的坑、观察到的规律、以及最后跑通的一套接续方案原原本本讲清楚。不讲虚的全是能直接抄的操作。先说结论方向免得你看到一半觉得跑题压缩本身不是问题问题是压缩时没有保留接续锚点。只要在压缩策略里显式维护几类锚点信息代理的连续性会有肉眼可见的提升。下面拆开讲。2. 为什么压缩之后代理会断片2.1 上下文压缩到底压掉了什么很多人以为压缩就是把长文本变短其实远不止。一个编码代理的上下文里大致有这么几类内容系统指令代理的身份、行为规范、工具定义。这部分通常不会被压。任务目标用户最初让它干什么比如修复登录接口的 500 错误。探索轨迹它读了哪些文件、搜了什么关键词、跑了什么命令。中间结论它推断出的假设比如怀疑是 token 校验失败。工具原始输出文件内容、命令回显、报错堆栈。已执行的修改改了哪些文件、改了什么。压缩策略一般会优先砍掉工具原始输出因为这部分最占地方。然后对探索轨迹做摘要。问题在于中间结论和已执行的修改经常被混在摘要里一压缩就模糊了。代理回来看到一段之前排查了登录相关问题它根本不知道排查到哪一步、哪些已经排除了。我在这 430 条记录里做过统计压缩后代理重复劳动的比例相当高。所谓重复劳动就是它重新去读一个它上一轮已经读过的文件或者重新验证一个已经确认过的结论。这个比例在压缩策略比较激进的时候能到三成以上。2.2 接得上的本质是状态可恢复打个比方。上下文压缩就像给一个正在做手术的医生做记忆清除然后让他继续手术。你光告诉他病人在做阑尾切除没用他需要知道切到哪一层了、止血钳夹在哪、下一步该干嘛。这些就是状态。代理的状态包括几个层面任务状态目标是什么当前处于哪个子步骤。认知状态已经确认了哪些事实排除了哪些假设。操作状态已经改了哪些文件工作区现在是什么样。意图状态下一步打算干什么为什么这么打算。压缩如果只保留任务状态的模糊描述丢掉后三个代理就会断片。long_mem 和 engram 这两个概念本质上就是在解决状态怎么持久化、怎么在压缩后重新加载的问题。注意不要把 long_mem 理解成把所有历史都存下来。存下来没用关键是存什么、怎么索引、什么时候取回来。存一堆原始日志压缩后照样接不上因为代理不知道该看哪一条。2.3 Codex 这类代理的压缩行为观察Codex 系的代理在压缩上有个特点它倾向于保留最近若干轮的完整上下文对更早的内容做摘要。这个策略在短任务里没问题但在长任务里会出岔子。因为一个长任务的关键锚点往往出现在很早的时候——比如用户第一句话里定义的约束条件。我记录过一个典型案例。任务开始时用户说不要动数据库 schema。这句话在压缩后变成了摘要里的用户有一些限制条件。代理后来为了修 bug直接改了一个 migration 文件。这就是典型的锚点丢失。所以后面我调整了策略把关键约束单独拎出来不参与压缩。这个改动之后类似的越界修改基本消失了。3. long_mem 与 engram记忆机制怎么落地3.1 long_mem 不是数据库是取回策略一开始我也走了弯路以为 long_mem 就是搞个向量库把历史对话都 embed 进去需要的时候检索。实测下来纯向量检索在编码场景里效果一般。原因是代码相关的记忆有很强的结构性不是语义相似就能取对的。举个例子。代理在改auth.py的时候它需要的记忆可能是这个项目里 token 校验统一走verify_token()而不是某次对话里提到过 token。向量检索很容易把后者捞出来但前者才是真正有用的。前者是一种规则性记忆后者是事件性记忆。我的做法是把 long_mem 分成三层层级存什么取回方式是否参与压缩约束层用户硬性要求、项目规范每轮强制注入否事实层已确认的代码事实、接口约定按文件/模块索引否事件层探索轨迹、工具输出向量检索 时间衰减是约束层和事实层不参与压缩每轮都重新注入。事件层可以压因为它只是过程记录丢了影响不大。这个分层是这次实验里最关键的收获之一。3.2 engram 的启发给记忆留痕迹engram 这个词原本是神经科学里的概念指记忆在神经组织里留下的物理痕迹。借用到代理上我的理解是每一次重要的认知变化都应该在某个地方留下一个轻量的标记而不是只存在于对话流里。具体怎么做我在代理的工作目录里维护了一个engram.md文件代理每完成一个认知跃迁就追加一行。什么叫认知跃迁比如确认了某个 bug 的根因排除了某个假设确定了某个接口的调用方式发现了一个之前不知道的约束这个文件不参与上下文压缩因为它本身就在磁盘上代理随时可以读。压缩后代理第一件事就是读engram.md相当于回忆。实测这个简单的机制效果出奇地好。因为它把易失的对话记忆变成了持久的文件记忆。对话可以被压缩文件不会。3.3 为什么不用现成的记忆框架有人会问市面上不是有各种 agent memory 框架吗为什么不直接用。我试过几个主要问题是它们太重、太通用。通用框架为了适配所有场景会存一大堆东西检索逻辑也很复杂。在编码这个垂直场景里我需要的其实很朴素几个不参与压缩的锚点文件 一个按模块索引的事实库。自己搭的好处是可控。我知道每一轮注入了什么、压缩时砍了什么、取回时捞了什么。调试起来一目了然。通用框架出问题的时候你根本不知道是检索错了还是存储错了。实操心得记忆机制越简单越好。能用文件解决的别上数据库能用规则解决的别上模型。复杂度是连续性的敌人。4. 10 天实验的具体做法4.1 实验设置与记录方式实验周期 10 天全程用同一个编码代理跑真实任务任务类型覆盖bug 修复、功能新增、重构、写测试。每一条记录包含任务描述、压缩发生的时间点、压缩前后的代理行为、是否出现断片、断片的具体表现。430 条记录里压缩触发次数大概在 200 次左右平均每个任务触发 2 到 3 次。断片被定义为以下任一情况重复读取已读过的文件重新验证已确认的结论违反已声明的约束忘记已执行的修改导致重复修改或冲突记录方式很土就是一个 markdown 表格每行一条。但正是这种土办法让我能事后统计出规律。4.2 压缩策略的三次迭代第一版默认压缩。就是代理自带的策略什么都不改。结果断片率最高重复劳动多约束违反也时有发生。第二版加锚点文件。引入engram.md和constraints.md前者记认知跃迁后者记硬性约束。压缩后强制先读这两个文件。断片率明显下降尤其是约束违反几乎消失。第三版事实层索引。把已确认的代码事实按模块整理成facts/目录每个模块一个文件。代理在动某个模块前先读对应的事实文件。这一版之后重复探索基本被压到很低。三次迭代的对比大致是这样版本断片率重复劳动约束违反主要改动v1高多偶发无v2中中几乎无加锚点文件v3低少无加事实层索引4.3 关键参数什么时候触发压缩压缩触发时机很关键。触发太早上下文还没充分利用就压了浪费触发太晚压缩时信息已经太杂摘要质量差。我的经验是在上下文用到七成左右时触发。这个比例留了足够空间给压缩后的新内容又不至于让压缩前的信息太乱。具体阈值要看模型窗口大小窗口越大可以适当晚一点压。另外压缩前先落盘。也就是在触发压缩之前强制代理把当前的认知跃迁写进engram.md。这一步很关键相当于睡前记日记。不落盘就压等于把记忆直接抹了。5. 实操搭一套压缩后能接上的方案5.1 目录结构设计先给一套可以直接抄的目录结构。放在项目根目录下跟代码一起版本管理.agent-mem/ constraints.md # 硬性约束不参与压缩 engram.md # 认知跃迁记录不参与压缩 facts/ auth.md # auth 模块的已确认事实 api.md # api 模块的已确认事实 ... state.json # 当前任务状态快照constraints.md里写什么用户明确说的限制、项目本身的规范。比如不要改 schema所有接口必须走统一错误处理。这些每轮注入。engram.md是追加式的只增不改。格式简单到极致- [时间] 确认登录 500 的根因是 token 过期未刷新 - [时间] 排除数据库连接问题 - [时间] 确定 verify_token 在 auth/utils.py:42facts/按模块分文件每个文件记录这个模块的稳定事实。代理动这个模块前先读。state.json记录当前任务干到哪了是个结构化快照。5.2 压缩前后的钩子怎么写如果你用的是可编程的代理框架可以在压缩前后挂钩子。伪代码大概是这样def before_compress(context): # 强制落盘认知跃迁 engram_entries extract_cognitive_shifts(context) append_to_engram(engram_entries) # 更新任务状态快照 update_state_snapshot(context) # 标记哪些内容可以压 return mark_compressible(context) def after_compress(compressed_context): # 重新注入不参与压缩的锚点 anchors load_anchors() # constraints engram facts return inject_anchors(compressed_context, anchors)核心就两点压缩前落盘压缩后注入。这两步做好连续性就有保障。注意extract_cognitive_shifts这一步如果让模型来做要给它明确的指令告诉它什么叫认知跃迁。否则它会把一堆无关紧要的东西也写进去engram.md很快就变成垃圾场。5.3 事实层的维护规则事实层最容易腐化。因为代码在变事实也会过时。我的规则是只记录稳定的事实易变的别记每条事实标注来源比如文件路径和行号事实被推翻时不删除标记为失效保留历史第三条很重要。因为代理有时候需要知道这个结论曾经成立过但现在不成立了这能帮它理解代码的演进。维护事实层不需要额外工具就是让代理在确认一个事实后顺手写一行。写多了它会形成习惯。6. 踩过的坑与排查技巧6.1 锚点文件本身被压缩了这是最坑的一个。我一开始把engram.md的内容直接塞进上下文结果它也被压缩策略盯上了因为它在上下文里表现为一段长文本。压缩后锚点就没了。解决办法是把锚点标记为不可压缩。不同框架标记方式不同有的支持给消息打标签有的需要把锚点放在系统指令区。如果框架不支持那就压缩后重新读文件注入别指望它留在上下文里。6.2 事实层和实际代码不一致出现过好几次事实文件里写着接口 A 返回 200实际代码已经改成返回 201 了。代理信了事实文件改错了地方。根因是事实层没有跟代码同步。我的补救办法是在事实条目里带上代码位置代理用之前先核对一下那个位置。如果对不上就重新确认。这多了一步但避免了基于过时信息做决策。6.3 压缩摘要把假设当成了事实摘要模型有时候会把代理怀疑是 X写成问题是 X。这个偏差很致命因为代理回来会把假设当结论用。对策是在落盘时区分假设和结论。engram.md里明确标注[假设]和[确认]。压缩摘要里如果出现未标注的断言代理要警惕。6.4 常见问题速查表现象可能原因排查方向压缩后重复读同一文件探索轨迹没落盘检查 engram 是否记录了已读文件违反用户约束约束没进锚点层检查 constraints.md 是否每轮注入基于过时信息修改事实层未同步核对事实条目的代码位置把假设当结论摘要混淆了假设与确认落盘时强制标注状态锚点丢失锚点被当普通上下文压缩标记锚点为不可压缩6.5 一个反直觉的发现实验里有个发现挺反直觉压缩得越狠代理反而越依赖锚点文件。因为上下文里啥都没有了它只能去读文件。这其实是好事说明锚点机制在极端情况下也能兜住。但如果锚点文件本身质量差压缩狠了就是灾难。所以锚点质量比压缩策略更重要。与其纠结怎么压不如先把锚点做扎实。7. 关于 Codex 使用中的一些实际观察热词里 Codex 相关的词特别多安装、配置、登录、模型支持这些。我在实验里也大量用了 Codex 系的代理顺手记几个实际观察跟上下文连续性也有关。Codex 这类代理在长任务里的表现很大程度上取决于它的记忆管理。默认配置下它对早期上下文的保留比较弱长任务容易断。我的做法是配合前面那套锚点机制用效果会好很多。另外模型选择会影响压缩后的恢复能力。有些模型在摘要理解上更强压缩后能更好地利用摘要里的信息有些模型则容易过度依赖最近几轮。这个没有绝对好坏得按任务类型试。我一般会在长任务里选摘要理解更稳的模型短任务随便。还有一点Codex 的工具调用输出往往很长这是压缩的主要对象。如果能把工具输出结构化比如只保留关键字段而不是整段回显压缩压力会小很多连续性也更好。这个优化我强烈建议做。实操心得别指望代理自己管好记忆。它没有我可能会忘的自觉。记忆管理是使用者的责任你得主动帮它把该记的记下来。8. 最后分享几个能立刻用上的技巧第一个技巧给代理一个开工前先读记忆的固定动作。不管是新任务还是压缩后恢复第一件事就是读constraints.md和engram.md。这个动作写进系统指令里形成条件反射。第二个技巧压缩前问代理一句你现在确认了什么。让它自己总结当前认知然后落盘。这比事后从对话里提取要准得多因为代理自己最清楚它确认了什么。第三个技巧事实层按模块分文件别搞一个大文件。大文件读起来慢而且容易过时。分模块之后代理动哪个模块读哪个文件精准且轻量。第四个技巧定期清理 engram。虽然我说只增不改但太长的 engram 也会拖慢读取。我的做法是每隔一段时间把已经稳定的事实提升到 facts 层然后从 engram 里归档掉。engram 保持精简只留最近的认知跃迁。这套东西我跑了 10 天430 条记录从最初的频繁断片到后来的基本连续中间改了三版。核心就一句话压缩不可避免但记忆可以外置。把该记的记到磁盘上压缩就只是压缩上下文压不掉连续性。
返回列表