跳到正文
阿里雲開發者社區 AI·· 3 小時前AI 評分62

Agent 記憶累積至互相衝突後:一次清理記錄

Agent 记忆攒到互相打架以后:一次清账记录

AI 導讀

作者以個人記帳工具項目復盤 Qoder 長期使用中累積的 Agent 上下文債務,並改用 Agent Context 管理記憶。他將問題分為時間、重複、衝突和執行四類,透過審計、修訂或刪除舊記憶、停用過時 Skill,以及人工裁決知識庫衝突來處理。作者指出後台可持續處理去重、衝突偵測和衰減,但內容是否失效及其適用條件仍須人工判斷;他未做對照測試,無法量化 Agent 引用舊做法的變化。

正文

2026-11-02 11

版权

版权声明:

本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《 阿里云开发者社区用户服务协议》和 《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写 侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

简介: 本文以个人小项目为例,揭示AI Agent长期使用中积累的“上下文债务”:时间失效、重复、冲突与执行四类问题。作者通过审计、分类、停用旧技能及切换专业记忆后端(Agent Context),实践了一套轻量可控的记忆治理方法,强调写入规范比存储优化更重要。

那天我让 Agent 跑一下测试,它先用了 pnpm test,失败后说“根据之前的记录,本项目使用 make check”,又跑了一遍。两条命令其实都对过:make check 是我半年前加的包装,后来嫌它慢,改回直接调测试框架,Makefile 里那一项早就删了。它记住了两个时期的说法,而且不知道哪个是现在的。

这是我一个人维护的副业项目,一个小型记账工具,前后端加一些定时任务,全部代码和部署都是我自己。我用 Qoder 写代码已经有一段时间,项目越做越久,Agent 需要记住的东西也越来越多:测试怎么跑,迁移脚本放在哪,哪些接口是给老版本客户端留的,部署前要先手动备份哪张表。那次之后我花了几个晚上,专门清理它记住的这些东西,下面是过程和一些想法。

一开始是自己攒的笔记

最早我没用任何记忆服务。项目根目录有一份给 Agent 读的说明文件,后来又写了个小脚本,每次会话结束把要点追加进一个本地 SQLite 文件,下次开始时按相似度取几条塞进提示里。规模小的时候这套东西很好用,我几乎忘了它的存在。

问题是慢慢出来的。系统从来不报错,只是回答越来越难解释:同一个问题,这周和上周得到的说法不一样;Agent 动手之前要读的材料越来越长;我开始习惯在它执行前多看一眼计划,确认它没拿旧做法。后来我借用“技术债务”的说法,把这类累积成本叫作上下文债务。技术债务的要点在利息,一次为了赶进度省掉的重构,会在之后每次改代码时多收一点;上下文也一样,一次省掉的整理或下线,会在后面的召回和执行里变成噪声和返工,一直收利息。

回头分类,我欠的大致有四种。

时间债务。内容自然失效,旧的还留在可召回范围里,比如那条 make check。它不报错,还因为早期被用得多,相似度排序时一点不吃亏。

重复债务。同一件事被记了好几遍,措辞略有不同。“部署前备份账单表”我在不同会话里说过好几次,脚本就存了好几条。重复内容挤占召回名额,还会放大表面上的“票数”,模型看到几条说法一致的记录,很难意识到它们其实出自同一次口头交代。

两条记录结论不一致,算冲突债务。有的是新旧之分,有的本来就各有适用范围,比如本地开发和线上环境的环境变量名就不一样。只存文本、不存来源和适用条件,没法裁决。

执行债务。我给 Agent 写过几个可复用的操作说明,其中一个是旧部署流程,在某段时间是对的,现在照做会漏掉一步数据迁移。这一类利息最高,它的后果是错误的动作,比错误的回答更难撤回。

利息怎么付,一个人的项目里也看得很清楚。最直接的是上下文变长:为了不漏掉什么,我的脚本越取越多,取得越多,重复和冲突越难被模型稳定处理,我又往说明文件里加更多“以这条为准”的补丁。然后是我自己的时间,每次执行前审一遍计划,习惯一旦养成就很难再省掉,哪怕后来它的计划都是对的。

更隐蔽的是不敢删。我不确定删掉某条旧记录会影响哪些任务,于是一直留着;留得越多,依赖越乱,下一次更不敢动。

自己清理的几轮尝试

第一轮是手工去重。我把 SQLite 里的记录导出来,一条条看,删掉明显重复的。看了几十条就意识到这个办法撑不了多久,因为新的重复每天都在进来。

第二轮是给每条记录加日期,召回时让“最新写入”优先。这条规则在我一个人的项目里比在团队里靠谱一些,但还是遇到了反例:有一次我在一个很久没合并的旧分支上工作,Agent 在那边记下的说法是旧的,写入时间却是最新的。

第三轮按使用频率清理,很久没被召回的就删。删到一半我停下来了,因为排在最冷那一端的,有一条是证书续期的步骤,一年只做一次。按命中率清理,最先被删掉的恰恰是这种平时用不到、需要时又没有替代品的东西。

三轮下来,清理这件事要两样东西:一个持续运行的后台,一个让我审计、做最终决定的入口。两样都自己写脚本的话,维护成本会超过那个项目本身。

换了一个记忆后端

后来我专门去查 Agent 记忆在去重、冲突和遗忘上都有哪些做法,读到一份 Agent Context 的帮助文档,里面的记忆整理、遗忘管理和知识冲突处理正好对得上我卡住的几处。接入我用的是 ctxdb setup --agent qoder,一条命令配好了凭证和 Hooks。

让我愿意试一试的,是文档里对记忆处理机制的描述:记忆按原子事实、实体、结构化图谱这样的层次组织,处理机制里明确列了去重、冲突和衰减三项。我自己的脚本只做了存和取,这三件事全靠我手工补。哪怕它只替我承担其中一部分,也比我在 SQLite 上继续堆规则划算。

接上以后,记忆改由 Hooks 自动写入,我原来那个 SQLite 脚本就停用了。我没有导入旧数据,打算让它从头积累,正好看看新的一套会不会重蹈覆辙。

几周以后,它确实又攒了不少东西。自动记录的话,写入一定比我审得快,换什么后端都一样。区别在于这回有了审计和整理的入口,后台也在持续做一部分我原来想自己写脚本做的事。

先审计:把它记住的东西摊开看

审计用的是 memory list,可以按类别筛:

ctxdb memory list
ctxdb memory search "测试命令"

--category 后面要接具体类别,我是先不带参数列一遍,看清有哪些类别再分批筛的。

我给每条记忆的处理只有四种结果:保留、改写、删除、待定。保留的不用动;改写的多半是条件缺失,比如环境变量名那两条,各自补上“本地”和“线上”之后就不再冲突;删除的是确认作废的旧说法;待定的是我一时判断不了、需要回去翻代码或部署记录的。待定那一栏我单独记在一个文本文件里,下次审计从这里开始。分四种结果的好处是逼着自己对每条都做决定,不至于看到一半觉得“好像都还行”就关掉。对拿不准的条目用 memory get 看详情,确认过时的用 memory update 改掉或 memory delete 删除,同一主题下一批明确作废的用 memory batch-delete 一次处理。

整理交给后台,判断留给自己

记忆每晚会自动整理一次,合并重复、更新属性,控制台上也有“立即整理”。我在那次集中审计之前先手动点了一次,想让重复的部分先由它合并,我只看剩下的。我没有逐条做前后对比,所以说不出它具体合并了什么,能确认的是审计时重复的条目明显少了,我的注意力可以放到冲突和过时上。

有一类问题整理解决不了。pnpm test 和 make check 那两条,在语义上并不互相矛盾,都是“本项目怎么跑测试”的合理答案,只是一个过时了。后台的冲突处理能发现说法不一致的地方,但哪条已经过时,往往要对照代码现状才判断得了,这部分还得我来。我在审计里删掉了旧的那条,又用 memory add 写了一条明确的“测试统一用 pnpm test,Makefile 里已无 check 目标”。

去重、找冲突、按时间衰减,工作量大,规则也相对明确,适合后台持续做;某条内容该降级还是该下线,跟业务风险有关,应该由我来定。

遗忘管理可以按自定义条件和召回频率自动遗忘。这里我犹豫了很久,结果只加了很保守的条件,按召回频率的那部分先没有依赖。原因还是那条证书续期步骤,以及类似的低频操作:数据恢复演练、年度对账脚本的参数。我更倾向于区分“衰减”和“失效”两种状态,衰减的降级保留,不再主动推送,但精确查询还能找到;确认失效或被替代的才删除。按频率遗忘适合前一种,但我还没想好怎么让系统知道哪些低频内容属于“关键但冷”。在想清楚之前,宁可多留一些冷数据。

另一个需要自己判断的是召回阈值。memory search 的 --threshold 默认是 0.4,清理之后记忆总量变少,同样的查询返回的条数也有变化。要不要调、调到多少,我目前保持默认,只在审计时用不同的阈值多搜几遍,看会不会漏掉一些措辞差得比较远的旧说法。

知识库里的冲突,走评审

除了私有记忆,我还建了一个只给自己用的知识库,放项目的设计记录和几份接口说明。设计记录天然会有冲突:早期的决定被后来的决定推翻,但旧文档我不想删,它解释了当时为什么那样写。

处理知识冲突用的是 kb review resolve-conflicts。我看中的是冲突不会被自动覆盖,新旧两份都留着,由我决定哪份当前有效、旧的是归档还是保留为背景。对一个人的项目来说,这一步看起来有点重,可正是“新的直接覆盖旧的”这条省事的规则,让我在 SQLite 那一版里吃过亏。

裁决时我给自己定的做法是:先看两份文档各自的生效条件,能写清楚分界的,两份都保留,在旧的那份开头补一句它已被哪份取代、从什么时候起;分界说不清的,回到代码里看当前实现,以代码为准,再改文档。这样做慢,但每次裁决都留下了理由。对 Agent 来说,它召回到旧文档时也能看到那句说明,不至于把它当成当前有效的依据。

旧 Skill 先停用,不删除

那份旧部署流程的操作说明,当时我已经放进了 Skill。处理它的时候我没用 skill delete,用的是 skill disable。停用后它不再被 Agent 加载,历史还在,以后如果要回头查某次部署为什么是那样做的,还有据可查。

停用之前我犹豫过要不要直接改那个 Skill,把漏掉的迁移步骤补进去。后来没这么做,因为新的部署流程和旧的差别不止那一步,在旧 Skill 上打补丁,等于让一份已经过时的说明继续冒充当前做法。新的流程我打算等它在几次真实部署里稳定下来,再单独写成一个新 Skill。这中间的空档,Agent 会按我当场交代的步骤做,慢一点,但每一步我都看得见。

一张给自己的台账

几轮清理之后,我把每类内容需要能回答的问题写成了一张表,下次审计照着过:

关注点 要回答的问题
来源 这条是我明确交代的,还是从对话里提炼的
时间 什么时候产生,多久没有被验证
适用范围 本地还是线上,哪个分支,哪个版本之后
依赖 有没有 Skill 或文档在依赖它
冲突状态 有没有另一条给出了不同说法
生命周期 有效、待复核、已停用

这些问题里,时间、冲突和重复后台能帮我发现,来源和适用范围大部分要靠写入时我自己写清楚。有了这张表,每次删东西都有依据,删错了也知道去哪找回来。

感受

这次清账让我看清,债务大部分是在写入那一刻进来的,存储层的问题反倒少。我随口说的一句话被当成长期事实;旧分支上的说法和主干上的混在一起;还有那份阶段性的操作说明,被我固化成了 Skill。后台整理能把利息压低一些,写入这一头只能我自己管。

审计的过程本身也有点不舒服。把几周积累下来的记忆一条条摊开看,等于在回看自己这段时间是怎么跟 Agent 说话的:很多约定我只说了一半,有些是在赶时间时随口一提,还有些前后矛盾,我自己都没意识到。以前这些东西藏在一个我从不打开的 SQLite 文件里,现在它们被列出来,有类别,可以搜。这让我对“自动记忆”多了一点警惕,它记下的东西忠实地反映了我交代得有多随意。

换到 Agent Context 以后,我审计时心里有底一些。能按类别列出全部记忆,能看到整理在持续进行,冲突不会悄悄被覆盖,执行类的内容能停用。至于 Agent 本身,我能感觉到的变化是它在动手前引用旧做法的情况少了,但我没有做过对照,说不出少了多少。

接下来

我给自己定了一个很小的起步动作:每个月抽一个晚上,按类别把记忆过一遍,先看和部署、数据迁移有关的两类,因为这两类一旦出错,后果是动作而非答案。每次只看最常被用到的二十条左右,召回越多,影响面越大,也越可能藏着被反复放大的过时说法。二十条只是一个能坚持下去的量,看几次之后再决定要不要扩大。

还有一件事和 Agent Context 的知识库有关。我打算把项目里那几份最常被问到的约定,比如测试、迁移、部署前检查,从记忆里挪出来,写成知识库里的短文档。记忆适合放我在工作中随口交代的事,可这几样是项目的规矩,需要有一个能整份更新、能看到版本的地方。

写入习惯也得改。凡是带条件的约定,我会自己用 memory add 写一条带条件的版本,不指望自动提炼替我保留“仅线上”“自某版本起”这类限定。

有两件事还没想清楚。那些关键但冷的内容到底放在哪里更合适?留在私有记忆里要防着被遗忘,挪进知识库又显得有点重,我倾向于后者,但还没动手。再就是 Skill 停用之后,依赖过它的那些记忆要不要一起处理。现在它们还在,偶尔会提到“按部署 Skill 的步骤”,而那个 Skill 已经不会被加载了。这类跨资产的引用关系我目前只能靠自己记,等积累多一些,可能得找个办法把它记到台账里。

來源:阿里雲開發者社區 AI · developer.aliyun.com