Memoirs:教会智能体记忆(而不必在云端失去理智)

我用过的每一个对话式智能体——Claude、Cursor、各种 CLI 循环——都有着同样一个悄无声息的缺陷。它们醒来时对昨天毫无记忆。上下文窗口已经变得巨大,但它们在每个新会话中仍然从一张白纸开始。如果你试图通过保持一个持续的会话线程来绕过这一点,最终会得到更糟糕的东西:一个在每一轮都把整个聊天历史倾倒进上下文窗口的架构怪物。你付出了 40K token 的推理成本,而智能体仍然把最重要的事实埋在中间某处——看不见,被忽略。这就是 Lost in the Middle(迷失在中间)效应。它既昂贵,又根本不起作用。

针对这个问题有企业级方案。沉重的方案。云托管的向量数据库,在好的日子里网络往返时间以秒计。但我可不打算把我的架构笔记、我半遮半掩的凭据和我个人的约定发送给某个我无法掌控的外部 API。我想要一个本地优先的东西。从构造上就是私密的。一个真正理解我的环境变量、我的框架选择、我的风格——并把这些知识保留在身边的东西。

这就是后来的 memoirs。不是一个 RAG 封装层。那会太容易,也太无聊。我想要一个真正的记忆系统——一个能有机地遗忘并巩固真正重要内容的系统。

我把持久化层构建在大多数人低估的东西之上:SQLite。不只是一个扁平的 .db 文件。而是一个被强力加持的 SQLite 实例,搭配 sqlite-vec 用于稠密向量搜索,以及原生的 FTS5 用于带 BM25 评分的倒排索引词法搜索。

架构最终落定为六层,因为记忆系统不是一个存储盒——它是一座水处理厂:

  1. 原始日志:进水管。接收一切——完整的对话历史、嘈杂的 diff、原始输出。
  2. 提取:一个在本地运行的小型策展 LLM(目前是 Qwen 2.5 3B——快到察觉不出来),从噪声中过滤出信号:启发式规则、凭据、偏好、风格标记。
  3. 图谱:连接组织。记忆使用 Zettelkasten 风格的原则相互链接,在概念和会话之间寻找共享的语义节点。
  4. 双重索引:对 sqlite-vec 和 FTS5 进行原子化的并行写入。两个索引保持同步。
  5. 记忆引擎:策展层。记忆随时间获得和失去分数,具备完整的双时态支持——你可以查询系统在过去任意时刻所相信的内容。
  6. 表层:暴露层。HTTP + REST,更重要的是一个 22 端点的 MCP 服务器,它让任何智能体都能干净地查询系统,而无需了解其内部任何细节。

早期,我撞上了一堵墙。sentence-transformers 的嵌入以一种非常特定的方式失败:精确标识符召回。让 memoirs 检索一个特定的工具名或一个带版本号的依赖字符串,它返回的却是语义相邻的噪声。向量空间在意义上很出色。它们知道"汽车"接近"车辆"。但当一条记忆包含 “旧系统使用 psql-driver-v9” 时,嵌入并不会给这个精确字符串任何特殊权重。对于一个试图重建某个决策的智能体来说,这是一个致命的疏漏。

解决方案是把一个新东西与一个非常古老的东西结合起来:Reciprocal Rank Fusion(倒数排名融合,RRF)。我们并行运行两种搜索——一个通过 sqlite-vec 的稠密语义查询,以及一个通过带 BM25 评分的 FTS5 的精确匹配倒排索引查询。如果 BM25 精确找到了字符串,而语义搜索理解了周围的上下文,RRF 就会稳定分数,把正确的结果稳稳地推到第一位。结果是:p50 检索延迟降到了约 3.9 毫秒。在一个本地 SQLite 数据库上。这远远胜过大多数云向量存储。

如果一个系统不能遗忘,它就没有长期记忆。智能体有一种淹没在陈旧知识中的倾向。“昨天我在和 Docker API 较劲"紧接着就是"今天我们彻底放弃了 Docker,改用裸 OCI 调用。“如果记忆引擎以同等置信度呈现这两个事实,智能体就会做出错误的决定。每一次都是。

我翻回一本旧笔记本,实现了基于赫尔曼·艾宾浩斯遗忘曲线的衰减函数:$R(t) = e^{-\Delta t_h / (S \cdot 24)}$。新记忆随时间渐近地失去权重。但它们的强度分数——它们原始的信号质量——会在系统每次检索并积极确认那条记忆时被乘大。核心架构决策和深层偏好的衰减,比一次深夜会话中的调试战记要慢得多。

这所需的计算——巩固、语义冲突消解、图谱压缩——太昂贵了,无法实时运行。所以我求助于一个人类时刻在用、而软件几乎从不使用的机制:睡眠

我写了一个守护进程(sleep_consolidation.py),它监视 CPU 的低谷。它只在开发者和智能体都离开时运行——也就是锁空闲且系统空闲已超过 N 分钟的那些空闲窗口。在那个盲目的异步窗口里,本地的 Qwen 或 Phi 模型醒来,审查最近存储的画像,搜寻记忆版本之间的矛盾,并巩固或归档那些陈旧的部分。图谱会在下一个会话开始之前,在后台悄悄地被压缩。

在基准测试周期结束时,在像 LoCoMo 这样的记忆检索基准上取得强劲的 MRR 分数——而 RAM 开销仅为 231 MB,相比之下 LlamaIndex 或 Mem0 所消耗的要多得多——验证了这个假设。最好的智能体功能不是靠把数百万 token 发送给远程提供商而构建的。它们是靠深入理解基础索引、并在你自己的机器上强加无情的资源纪律而构建的。