长期记忆架构与向量检索

让 Agent 跟踪多步任务 之后,我们手写的 my-claude-code 已经会拆任务、会提问、能恢复会话,但它还有个明显的短板:没有记性。

你上周告诉它「这个项目的 commit message 要加 feat/fix 这类前缀」,这周开个新会话,它交出来的提交信息又是光秃秃的一句话。由于上下文不共享,在不同的会话中,你教过它的偏好、约定、踩过的坑,全部清零。

解法前面两篇原理文章已经拆透了,而且正好是两条路线:Claude Code 自动记忆系统 走文件式,每条记忆是一个人能直接读的 markdown 文件;mem0 记忆库的实现思路 走向量式,把对话提炼成事实存进向量库,靠语义检索召回。

这一篇动手给 my-claude-code 加记忆,选的是文件式方案。

为什么不用 mem0

mem0 记忆库 是现成的记忆组件,装上就能用,看起来是更省事的选择。但对 Coding Agent场景,文件式几乎全面胜出。

原因主要有几点:

零依赖。文件式的全部家当就是 markdown 文件加文件系统。mem0 要跑通上一篇那套本地方案,得拖进来 torch、chromadb 好几个 GB 的依赖,首次运行还要下载 1.1GB 的 embedding 模型。给一个本地命令行工具加记忆,犯不上背这么重的包袱。

小量级用不上向量检索。个人 Coding Agent 的记忆是「项目约定、操作偏好」这类内容,最多也就几十条。

向量检索解决的是记忆库大到没法整个塞给模型的问题:几千上万条零散事实,只能先用 embedding 相似度粗筛出候选。而几十条记忆,把清单整个交给 LLM 让它挑就行了,LLM 是逐条读懂内容再挑,语义判断比向量空间里的相似度匹配更靠谱。

矛盾处理天然简单。mem0 的写入策略是 additive 只增不改。比如你先说「爱吃辣」,后来改口「不能吃辣」,两条互相矛盾的记忆会一直并存在库里,模型召回时拿不准该信哪条。

想消解矛盾,官方的回答是应用层自己负责(相关 issue 被标记为 not planned),得自己再写一套「LLM 审阅记忆库,判断该删谁改谁」的整理逻辑。

mem0 这样做,主要是为了让记忆的变动可追溯。但对于 Coding Agent 来说,对记忆的可追溯性的要求并不高,只要记忆能够及时更新、无歧义,准确反映最新的情况就可以了。

所以文件就是个很好的选择,只要用户改口,就能直接改写那个 md 文件,旧内容当场被覆盖,矛盾根本没有并存的机会。

人类可读可改。记忆出了问题,用户顺手就能改,而向量库里的东西,你只能借助检索接口窥探,不如纯文本那么简洁直观。

当然 mem0 不是白设计的。多用户隔离、海量零散事实的语义召回,这些产品化场景它仍然是正解,最后的总结里我们会做个完整对比。但给本地 Coding Agent 加记忆,文件式是更合适的选择。