Agent 的记忆不是聊天记录,而是一套信息检索系统
LLM 无状态,记住你不能靠拉长聊天历史。长期记忆要外置于会话:MEM0 用三个存储、每轮抽取,检索时把向量、关键词、实体分加权,只取最相关的几十条。
核心论点 · 点时间戳可跳到原声
长期记忆不是聊天记录拉长
LLM 本身是无状态机器,两个连续 prompt 之间没有任何残留。普通 agent 的「记性」只是把整段对话历史随每个新请求重发给模型,这叫 conversational memory;一旦开新会话,所有上下文清零。因此真正的长期记忆必须另建一个独立服务,跨会话保存用户偏好、agent 学到的知识和流程,并允许同一个用户在多 agent 间共享同一份记忆。判断标准很简单:它不能和 session 存在同一个存储里。这段给后文定下「外置记忆系统」的前提。
实体单独成库,检索才有捷径
MEM0 的信息架构是三件套。主存储是一个向量库,容纳每条「记忆」本体(一句话或一小段)及其元数据:创建/更新日期、归属用户还是 agent、内容 hash、词形还原版本等。第二个是实体库,也是向量库,但每个点是一个人/地名等实体,实体元数据里链接一到多条主记忆——提到巴黎就会牵出所有跟巴黎有关的记忆。第三个是 SQLite,不存回忆,只记两条流水:所有变更历史的日志,以及最近 10 条消息。前两个库负责语义关联,SQLite 负责支撑短期上下文和审计。
记忆靠 LLM 抽取,不全量照存
Ingestion 在 agent 每回合结束后跑,MEM0 的默认方式是 infer=true:把本回合消息喂给一个专门做「记忆提取」的 LLM,prompt 里带上 user summary、相关旧记忆和最近消息,让模型输出结构化 JSON,里面是要存档的独立记忆。另有 procedural 模式和 infer=false 两种:前者复述整段操作过程,作者认为已不常用;后者把消息向量化后直接入库,太粗糙。抽取式才是区分「记住关键信息」和「留存全文」的分水岭。
没最近消息,抽取会丢代词
抽取 prompt 的上下文不是只有当前消息。管线先把本轮消息拼成一个整体嵌入,去主向量库里找相关旧记忆;同时从 SQLite 调出最近 10 条消息。这样当用户说「它很棒」「他这方面很擅长」时,上一两句中的先行词才能被 LLM 找到。没有这个模块,抽取模型会丢失指代,抽出来的记忆就是「it is great」这样没法用的废记录。此外 prompt 还会列出对话日期和当前日期。要点:单条消息不足以成为记忆,记忆要放在最近的对话流里才成立。
检索先捞多,再重排裁少
Retrieval 无论走 agent 显式工具调用还是每回合自动注入,管线相同。query 先嵌入向量库做相似度搜索,但这里有个反直觉设计:第一轮不直接返回 top K。MEM0 会先取 max(top_k×4, 60) 条候选,再在这个粗池上做重排。作者举例:你要 top K=10,向量搜索实际会拉回 60 条。原因是后续还有两轮不同维度的打分,如果一开始只找回 10 条,重排模型没有足够的池子可挑。据作者判断,MEM0 默认没有 query rewriting,想要更好效果应在 harness 端自己加改写。
实体关联越少,检索加分越高
三道评分之一是 entity boost。系统先从 query 里抽实体,去实体库检索;命中的实体各自链接到若干主记忆。如果当初只有 2 条记忆绑在这实体上,很可能正是当前要找的,加分靠近 0.5;如果绑了 1000 条,这个实体几乎没有区分度,加分趋近 0。换句话说,实体加分奖励的是「冷门但精确」的线索,惩罚大而全的泛主题。这解释了为什么用「巴黎」搜索时,系统不会返回百科式的巴黎常识,而是把你存在库里的那条「最爱巴黎玛黑区」顶到前面。
检索排序不能只信 embedding
最终分数是把每条候选的三项证据相加:向量相似度归一到 0-1,BM25 关键词词形重叠归一到 0-1,实体加分只有 0-0.5,理论上限正好 2.5。对粗池里每条记忆都算这个总和,再除以 2.5,得到 0 到 1 的最终分数,按它切出真正的 top K 返回给 agent。BM25 输入的关键词版本在写入时就算好存了词形还原结果,所以重排时不用现场做 NLP。三分加权说明 MEM0 默认信任语义、关键词、实体三种信号叠加,而不是只靠 embedding 一把梭。
跑通全套用不到大模型
这套架构最容易被低估的一点:开销最大的「记忆提取」反而简单,1B 到 12B 参数的模型就足够,再小除非自己微调;作者示例推荐 Qwen 3 8B。Embedding 模型可以在 Hugging Face 按 feature extraction 任务筛选,再对照专门的 embedding benchmark 看多语或医学等细分榜单。如果长期做,建议针对自己的场景微调抽取模型。MEM0 的嵌入默认用闭源模型,但每个环节都能换成本地开源组件——这也是它适合做个人记忆底座的原因。
原话 · 已逐字校验
So remember that LLMs are stateless machines. In other words, when you send the prompt to your LLM, your LLM is going to process it and give you a completion. If you send another prompt to it after that, it will not remember anything about the previous prompt or the previous completion.
记住:LLM 是无状态机器。换句话说,你发一个 prompt,LLM 处理完就给出结果;之后你发另一个 prompt,它对之前的 prompt 和刚才的输出没有任何记忆。
And before I forget, just remember that for long form memory to work, you're going to have to make it external to your conversation.
趁我还没忘:长期记忆要能工作,你必须让它外置于对话。
So um the ingestion part is going to be executed or it's going to be run after every agent turn.
摄取这一步在 agent 每一回合结束后执行。
And this one right here is very useful to identify and to figure out what pronouns mean for example in that particular new message.
这个机制非常有用:它能识别并弄清代词在新消息里指什么。
So if you set a top k of 10, you're going to actually get 60 messages from this vector search.
如果你把 top k 设为 10,向量搜索实际上会拉回 60 条消息。
the score is going to be higher if there are less memories associated to that particular entity.
实体关联的记忆越少,这项加分就越高。
I would recommend that you add a query rewriting system right here on the side of your harness.
我会建议你在 harness 这一侧加一个查询改写系统。
数字与实体
| 初步候选池大小 | max(top_k×4, 60) 条 | 20:18 |
| top_k=10 时的实际召回 | 60 条候选 | 20:18 |
| SQLite 保留的最近消息数 | 10 条 | 12:12 |
| 记忆提取推荐模型规模 | 1B-12B 参数 | 26:22 |
| 检索最高总分(归一化前) | 2.5 分 | 24:20 |
| 实体 boost 的加分范围 | 0-0.5 分 | 24:20 |
术语
- BM25BM25 算法
- 稀疏关键词检索打分,按词项重叠给出相关分;配合向量检索做重排。
- query rewriting查询改写
- 检索前把用户原始问题改写得更利于召回;MEM0 默认没有这一步。
- lemmatized version词形还原版本
- 把单词转成词典原形后再入库,供 BM25 做关键词精确匹配。
- entity store实体存储
- 存放从记忆中抽出的人名/地名的向量库;每个实体挂载关联记忆。
收听指南
正在为产品 Agent 做用户长期记忆或多 agent 共享记忆的工程师;想评估用 MEM0 还是自建的架构决策者。
不打算自己部署模型的话,26:22 之后的模型推荐与收尾可跳过。