#0127 AI Agent 的记忆不该只有一层——我造了个双层记忆架构

type
Post
status
Published
date
Feb 21, 2026
slug
30ea745569bb810fa316fd77d5face88
summary
你的 AI Agent 能记住东西吗? 大概率能。一个 Markdown 文件,写上"用户喜欢简洁回复"、"时区 GMT+8",每次对话塞进 system prompt。搞定。
tags
思考
工具
健康
金钱
category
投资
icon
password

AI Agent 的记忆不该只有一层——我造了个双层记忆架构

你的 AI Agent 能记住东西吗?
大概率能。一个 Markdown 文件,写上"用户喜欢简洁回复"、"时区 GMT+8",每次对话塞进 system prompt。搞定。
但你有没有想过:当这个文件从 20 行膨胀到 200 行,从 200 行逼近上千条记忆时,会发生什么?
答案是:要么 token 爆炸,要么你开始删记忆。
这篇文章讲的就是我怎么从一个"纯文本记忆文件"起步,踩了一堆坑,最终做出一个双层记忆架构的。不是论文,是工程笔记。

纯文本记忆的四个死穴

我最初的记忆方案极其简单:一个 MEMORY.md 文件,每条记忆一行,标上优先级(P0 核心 / P1 阶段性 / P2 临时),200 行上限。
一开始很爽。Agent 启动时读这个文件,所有核心信息一目了然。不需要数据库,不需要 API,不需要任何工具调用。
但两周后,问题来了:
1. 装不下
系统运行一段时间后,记忆条目疯涨。每天的决策、偏好、事件、教训……200 行根本不够。你开始做淘汰:P2 超 30 天删,P1 超 90 天删。但有些 P1 记忆删了之后,Agent 遇到相关问题时就"失忆"了。
2. 搜不到
当你需要找"上次体检的结果是什么"时,关键词搜索只能匹配字面。文件里写的是"左肺下叶磨玻璃结节 6mm",你搜"体检结果"?搜不到。搜"肺"?混在一堆无关内容里。
3. 全量加载浪费 token
每次对话都把 200 行记忆塞进 prompt,不管这次对话需不需要。问个天气预报,也要先读完你的财务偏好、健康目标、系统架构。白白烧钱。
4. 手动维护是噩梦
记忆该加哪些?该删哪些?该怎么分类?全靠人工判断。做了两周我就知道,这事不能手动。

人脑的启示:不是所有记忆都在工作台上

想一下你自己的大脑是怎么处理记忆的。
"我叫什么名字"——你不需要回忆,直接就知道。这是工作记忆,前额叶里随时在线的东西。容量很小(心理学说大约 7±2 个组块),但访问延迟为零。
"去年 8 月那次体检的具体数值"——你需要想一下,可能还要翻记录。这是长期记忆,海马体存储的东西。容量几乎无限,但需要触发才能提取。
"今天中午吃了什么"——这是短期记忆,明天可能就忘了。
人脑不会把所有记忆都放在工作记忆里——那样会过载。但核心身份(我是谁、我在哪、我的目标)永远不需要"搜索"。
AI Agent 的记忆系统应该一模一样。

双层架构:工作记忆 + 长期记忆

我的方案是两层:

第一层:纯文本文件(工作记忆)

还是那个 Markdown 文件,200 行上限,P0/P1/P2 优先级。每次对话自动注入 system prompt。
这一层存什么?只存 Agent 必须随时知道的东西
  • 核心身份:用户是谁、时区、设备
  • 系统架构:当前版本、Agent 配置
  • 关键偏好:沟通风格、禁忌、规则
  • 当前策略:正在执行的计划
特点:零延迟、零工具调用、容量有限。Agent 一醒来就"记得"所有核心信息,不需要做任何动作。

第二层:SQLite + 向量嵌入(长期记忆)

一个本地 SQLite 数据库,每条记忆存储文本内容、分类、优先级、创建时间,以及一个 1024 维的向量嵌入(用 Voyage AI 生成)。
这一层存什么?所有值得记住但不需要时刻挂在脑子里的东西
  • 历史决策及其背景
  • 过去的体检结果、财务记录
  • 系统变更的具体细节
  • 教训和经验
特点:容量无限、语义搜索、需要工具调用才能访问。Agent 遇到具体问题时,主动搜索这个数据库。

辅助层:每日日志(短期记忆)

每天一个 Markdown 文件,记录当天发生的事。这是"今天的记忆",还没有被整理和归档。每天晚上自动提取精华写入 SQLite,每周压缩归档。

为什么必须两层?

这个问题我被问了不下三次,所以展开说清楚。
"只用纯文本文件不行吗?"
不行。200 行上限意味着你要不断淘汰旧记忆。而且没有语义搜索——你搜不到你不知道具体措辞的信息。手动维护到第三周你就会放弃。
"那全用 SQLite 不行吗?"
也不行。想象一下:每次对话开始,Agent 第一件事是调用 memory_search("我是谁") memory_search("用户偏好") memory_search("系统架构")……多了三轮工具调用,还可能搜漏关键信息。更重要的是,P0 级的核心身份信息(用户名、时区、核心规则)必须零延迟可用——你不能让 Agent "回忆"自己是谁。
两层的本质是什么?
第一层保证"永远记得",第二层保证"总能找到"。一个负责广度(核心信息覆盖),一个负责深度(历史信息检索)。

技术选型:为什么不用现成方案?

市面上有不少 AI 记忆方案,我都看了。
Mem0:Python 生态的记忆框架,功能挺好。但我的系统是 Node.js 技术栈,引入 Python 依赖意味着多一套运行时、多一套部署、多一套调试。不值得。
Letta(原 MemGPT):更重量级,本质上是一个完整的 Agent 框架,自带记忆管理。但我已经有了自己的 Agent 架构,用 Letta 等于推倒重来,而且两套框架打架的问题会很头疼。
Pinecone / Weaviate / Qdrant:独立向量数据库。功能强大,但都是 SaaS 或需要独立部署。我的原则是"本地优先、零 SaaS 依赖"。记忆这种核心数据,不能放在别人的服务器上。
最终选择:SQLite + Voyage AI 嵌入
SQLite 是零依赖本地存储,一个文件搞定,不需要数据库服务。Voyage AI 提供向量嵌入 API,1024 维精度,免费额度够用很久。嵌入在写入时生成,之后搜索全靠本地余弦相似度计算,不再依赖网络。
这个组合满足我的所有需求:本地优先、零 SaaS 运行时依赖、Node.js 原生、语义搜索。

四个核心组件

记忆引擎

核心 CRUD + 语义搜索。写入时自动调用嵌入 API 生成向量,搜索时计算余弦相似度返回 Top N。支持按分类(fact/event/decision/preference)和优先级(P0/P1/P2)过滤。

每日同步

每天晚上自动扫描当天的日志文件,提取所有要点(bullet points),根据内容自动判断分类和优先级:健康相关的标 fact,决策相关的标 decision,系统变更标 event。写入前先做去重检查,避免重复。

每晚整理

每天深夜自动运行:
  • P2 记忆超过 30 天 → 淘汰
  • P1 记忆超过 90 天 → 淘汰
  • 相似度 > 0.85 的记忆 → 合并去重
  • 新记忆与 P0 记忆矛盾 → 不覆盖,标记并报告
最后这条很重要:P0 是圣经,不能被自动覆盖。如果系统检测到新信息和 P0 矛盾,它会停下来问人,而不是默默改掉核心记忆。

迁移工具

一次性把旧的纯文本记忆导入 SQLite。解析每一行的优先级标签、分类、日期,生成嵌入向量,写入数据库。迁移了 67 条记忆,零数据丢失。

语义搜索到底好在哪?

举个真实例子。
我的系统记录了用户的睡眠偏好,其中有这么几条:
  • "23:00 前入睡,睡眠目标 7.5 小时"
  • "一般起床时间 ~9:00"
  • "在床时间比实际入睡更重要,手表检测不准"
如果用关键词搜索"睡觉"——一条都搜不到。因为没有一条包含"睡觉"二字。
但用语义搜索"用户的作息习惯"?三条全出来,按相关度排序。因为向量嵌入理解的是语义,不是字面。
这在实际使用中差别巨大。Agent 不需要知道记忆的精确措辞,只需要描述想找什么。

踩坑实录:Demo 跑通 ≠ 上线

这才是我最想分享的部分。
我花了两天搭好整套系统:引擎写好了、数据迁移了、搜索验证了。一切正常。然后我就去忙别的了。
一周后我才发现,这套系统根本没在用。

坑一:升级杀死了搜索

系统做了一次版本升级。升级会重新打包 JS 文件,文件名里的 hash 后缀变了。但旧的服务进程没有被完全杀掉——它还在内存里运行,引用着已经被删除的旧文件。
表现是什么?语义搜索工具报错:"Cannot find module xxx-JQDuyylL.js"。
我第一反应是:完了,SQLite 出 bug 了。花了半小时排查数据库,一切正常。又花了半小时查嵌入 API,也正常。
最后才发现:磁盘上的新文件叫 xxx-CIjpkmRY.js,但进程内存里还在找 xxx-JQDuyylL.js。杀掉旧进程,重启,搜索立刻恢复。
教训:升级后必须验证所有旧进程已被替换。

坑二:最后一公里没接好

数据迁移了 67 条记忆到 SQLite。很好。但然后呢?
每天产生的新记忆——对话中的决策、新发现的偏好、系统变更——全都只写进了日志文件和纯文本记忆,没有一条进入 SQLite
也就是说,SQLite 里永远是迁移那天的快照。新发生的事?搜不到。因为根本没写进去。
没挂每晚整理的定时任务。没接每日同步的写入流程。迁移完就收工了。
教训:数据流的"最后一公里"比"前面的路"更容易被遗忘。

坑三:僵尸进程吃资源

在排查坑一的时候,我顺手查了进程列表。发现了一个惊人的事实:系统里有两个服务进程在跑。一个是升级前的老进程,一个是升级后的新进程。
老进程占了 78% CPU。跑了两天多没人发现。
不仅浪费资源,还导致行为不一致——有时候请求落到新进程上一切正常,有时候落到老进程上就报错。
教训:进程健康检查不是可选的,是必须的。

总结

建工具和用工具是两件完全不同的事。
  • 引擎写好了 ≠ 数据在流动
  • 迁移完了 ≠ 新数据在写入
  • Demo 跑通了 ≠ 生产环境正常
  • 升级成功了 ≠ 旧进程已退出
真正的"上线"需要四个保证:定时任务挂好、写入流程接入、异常监控到位、升级后验证。少一个都算没上线。

完整的记忆生命周期

最终的系统长这样:
白天:
对话和事件产生,写入当天的日志文件。这是"短期记忆"。
每天 22
自动同步脚本扫描今天的日志,提取要点,判断分类和优先级,写入 SQLite。短期记忆变成长期记忆。
每天 23
整理脚本检查所有记忆:淘汰过期的、合并重复的、标记矛盾的。保持数据库干净。
每周日:
把超过 7 天的日志压缩:精华提取到纯文本工作记忆,原文移到归档目录。
每月 15 号:
月度大扫除:合并重复记忆、消除矛盾条目、审查优先级、回归测试确保搜索质量没退化。
这个流水线保证了:
  • 新记忆当天就能被搜索到
  • 过期记忆自动淘汰不占空间
  • 核心工作记忆始终精简
  • 矛盾和异常有人兜底

给想做类似系统的人

几个建议:
从小开始。先用纯文本文件。等你真的感受到搜不到、装不下的痛苦时,再加第二层。过早优化是万恶之源。
本地优先。记忆是最私密的数据。放在第三方 SaaS 上?想想就觉得不对。SQLite 一个文件搞定,备份就是复制文件。
自动化写入。手动维护记忆的耐心最多撑两周。必须自动化:自动提取、自动写入、自动整理。
P0 不可自动覆盖。核心身份信息是基石。任何自动化流程都不应该默默修改 P0 记忆。检测到矛盾就停下来问人。
先跑通再说。别花三天设计完美架构,花一天做个能跑的版本。然后观察它在真实使用中哪里出问题,再迭代。

记忆是 AI Agent 从"工具"变成"助手"的关键一步。没有记忆的 Agent 每次对话都是初次见面。有了记忆,它才能理解上下文、追踪变化、在长期关系中持续进化。
但记忆不是堆数据。它需要结构、需要层次、需要生命周期管理。就像人脑一样——不是记住所有东西,而是在对的时间记住对的东西。
Loading...

© xiyu 2013-2026