给 AI 装上记忆:Mem0 源码级拆解

每次对话都从零开始的 AI,不是真正的助手。这篇文章拆解 Mem0 如何用一层”记忆中间件”解决 LLM 的失忆症——从架构设计到核心算法,从源码实现到生产选型。
一个反直觉的事实
主流 LLM 的上下文窗口已经长到 128K 甚至 1M token。按理说,”记忆”应该不再是问题——把所有历史对话塞进 Context Window 不就行了?
但事实是:更长的上下文窗口不但没有解决记忆问题,反而让问题暴露得更加彻底。
三个层面:
-
成本问题。把过去 30 天的对话记录全塞进去,每次请求消耗 50K+ token 的 input。粗略估算:按 GPT-4o 的定价($2.5/M input),日活 1 万用户、每人 3 次交互,光 input 就烧 $3750/天——还没算 output。实际情况更糟,因为同一用户的多轮交互中上下文会累积,后面几轮的 token 消耗远高于第一轮。
-
性能问题。上下文越长,模型的注意力越分散。早期研究(Liu et al., 2023)表明,当有用信息处于长上下文的中间位置时,模型的召回率会显著下降——这就是著名的 “Lost in the Middle” 问题。虽然 2025-2026 年的新模型已经大幅缓解了这个现象,但核心矛盾仍在:Context Window 越大,信噪比越低。把 30 天的对话全塞进去,真正相关的可能只有几百 token。
-
根本性问题。LLM 是无状态的。每次 API 调用都是一次独立事件——模型处理完请求就”失忆”了。Context Window 只是一个临时的”工作台”,不是永久存储。真正的记忆需要的是:跨会话持久化 + 智能检索 + 自动去重更新。
这正是 Mem0 要解决的问题。
Mem0 是什么
Mem0(读作 “mem-zero”)是一个开源的 AI 记忆层——它不是一个向量数据库,也不是一个 RAG 框架,而是夹在你的应用和 LLM 之间的一层”记忆中间件”。
顺便说一下它和 RAG 的区别:RAG 是”检索预先准备好的文档来增强生成”,Mem0 是”从实时产生的对话中自动提取事实来增强记忆”。RAG 的知识源是静态的,Mem0 的知识源是动态的——每次对话都可能产生新的记忆。
用一句话概括它的核心设计思想:
把非结构化的对话流,自动蒸馏为结构化的事实片段,存进多级存储,按需精准召回。
GitHub 上 54K+ Stars,300+ 个发版,Y Combinator 孵化——这些数字说明它不是玩具项目。但比数字更值得关注的是它的架构选择。我们来拆。
架构全景:三层架构
Mem0 的架构是一个干净的三层结构:
┌─────────────────────────────────────────────┐
│ Client Layer │
│ Python SDK · TypeScript SDK · REST API · CLI│
├─────────────────────────────────────────────┤
│ Core Memory System │
│ Memory 类 · 事实提取 · 去重 · 动作决策 │
│ Factory Pattern → LLM / Embedder / Store │
├─────────────────────────────────────────────┤
│ Storage Backends │
│ Vector Store · Graph DB · SQLite (History) │
└─────────────────────────────────────────────┘
第一层:Client Layer
提供多种接入方式。自托管用 Memory 类,云平台用 MemoryClient,还有 CLI 工具可以在终端直接操作记忆:
npm install -g @mem0/cli
mem0 add "偏好暗色模式和 Vim 键位" --user-id alice
mem0 search "Alice 的偏好是什么?" --user-id alice
第二层:Core Memory System
这是 Mem0 的大脑。核心是 Memory 类(mem0/memory/main.py),它通过 工厂模式 动态组装组件:
# 工厂模式:一个 config 搞定所有组件
memory = Memory.from_config({
"llm": {"provider": "openai", "config": {"model": "gpt-4o-mini"}},
"embedder": {"provider": "openai", "config": {"model": "text-embedding-3-small"}},
"vector_store": {"provider": "qdrant", "config": {"host": "localhost", "port": 6333}},
})
这里的设计哲学很明确:关注点分离。LLM、Embedding 模型、向量数据库——每个环节都可以独立替换。支持 18+ LLM 提供商、11+ Embedding 提供商、24+ 向量数据库。你可以用 Ollama 跑本地模型 + Qdrant 做存储,完全不依赖外部 API。
第三层:Storage Backends
Mem0 不只用一种存储——它用三种:
|
|
|
|
|---|---|---|
| Vector Store |
|
|
| Graph Store |
用户 -- 喜欢 --> Python),做关系推理 |
|
| SQLite |
|
|
为什么需要三种?因为不同类型的”记忆”适合不同的存储和检索方式:
-
“用户喜欢深色模式” → 语义向量,适合模糊匹配
-
“用户 Alice 在公司 X 工作” → 知识图谱,适合关系推理
-
“这条记忆在 3 天前从’素食主义者’更新为’纯素主义者’” → 关系型存储,适合审计追溯
核心算法:单轮提取 + 智能决策
这是 Mem0 最值得拆解的部分。2026 年 4 月发布的新记忆算法(官方称 New Memory Algorithm,本文简称 v3)做了一个关键改变:事实提取从多轮 LLM 调用简化为单轮 ADD-only 提取,再配合一轮独立的动作决策——官方称相比上一版算法,Token 效率提升约 3-4 倍(对比基线是 Mem0 自己的 v2 算法,而非全量上下文方案),同时准确率大幅提升。
记忆处理的完整流水线
当你调用 memory.add(messages, user_id="alice") 时,背后发生了什么?
用户对话
→ ① 消息解析 parse_messages() 归一化为字符串
→ ② LLM 事实提取 llm.generate_response() 提取结构化事实(ADD-only)
→ ③ 相似度搜索 vector_store.search() 找到已有相似记忆
→ ④ 动作决策 llm.generate_response() 判断 ADD/UPDATE/DELETE/NONE
→ ⑤ 存储更新 写入 Vector Store + SQLite 审计记录
让我们逐步拆解。
Step 1: 事实提取(Fact Extraction)
输入一段对话:
用户: 我叫 Alex,最近从北京搬到了上海,在做量化交易相关的工作。
助手: 欢迎来到上海!量化交易是个很有意思的领域。
Mem0 用 LLM 从中提取结构化事实:
{
"facts":[
"用户名字是 Alex",
"用户最近从北京搬到了上海",
"用户从事量化交易相关工作"
]
}
v3 算法的关键改变:提取阶段只做 ADD,不做 UPDATE/DELETE。提取阶段只管“蒸馏事实”,不管“和已有记忆冲突了怎么办”——这个复杂逻辑交给后面的动作决策阶段。
为什么这样更好?旧算法在提取阶段就尝试判断 ADD/UPDATE/DELETE,导致提取和决策逻辑耦合。LLM 在一次调用中同时完成事实提取和冲突解决,容易出错(尤其是当已有记忆列表很长时)。拆成两步后,每步的 Prompt 更聚焦,LLM 的输出质量更高。这是典型的关注点分离在 Prompt Engineering 中的体现。
不过这个设计也带来一个开放问题:存储膨胀。提取阶段只做 ADD,意味着记忆会持续累积。虽然后续的动作决策阶段会通过 UPDATE 和 DELETE 清理过时信息,但它依赖于“新事实与旧记忆被相似度搜索命中”这个前提。如果一条旧记忆从未被新事实命中,它就会永远留在那里。生产环境中可能需要配合定期的记忆审计或 TTL 策略来控制增长——这一点我们在后面“遗忘曲线”部分会再展开。
另一个细节:Mem0 区分了用户记忆和 Agent 记忆。如果设置了 agent_id,系统会使用不同的提取 Prompt——Agent 确认的操作(“我已经帮你订了周五的会议室”)也会被记录为一等事实。
Step 2: 相似度搜索(Similarity Search)
提取出事实后,Mem0 把每条事实嵌入为向量,然后在向量数据库中搜索已有的相似记忆。
这一步是为了找到可能需要更新或去重的记忆。比如:
-
新事实:“用户住在上海”
-
已有记忆:“用户住在北京”
-
相似度:0.92 → 可能是同一事实的更新版本
v3 算法引入了多信号检索——不只用语义相似度,而是三路并行:
-
语义检索(Semantic):基于向量余弦相似度
-
关键词检索(BM25):传统文本匹配,擅长捕捉专有名词
-
实体链接(Entity Linking):提取实体并跨记忆做链接
三路结果融合排序。这解决了纯语义检索的一个经典问题:比如用户说“帮我查一下 iPhone 15 的订单”,语义检索可能返回“用户对苹果产品感兴趣”(太泛),但 BM25 能精确命中包含“iPhone 15”的历史记忆。又比如在量化交易场景中搜索“MACD 指标”,BM25 能直接命中,而语义检索可能只返回“用户关注技术分析”。
要启用完整的混合检索,安装时需要带上 NLP 支持:
pip install mem0ai[nlp]
python -m spacy download en_core_web_sm
Step 3: 动作决策(Action Determination)
这是最精巧的部分。Mem0 把新提取的事实和检索到的已有记忆一起交给 LLM,让它决定每条事实该执行什么动作:
|
|
|
|
|---|---|---|
| ADD |
|
|
| UPDATE |
|
|
| DELETE |
|
|
| NONE |
|
|
这里有一个巧妙的工程细节:UUID 映射策略。真实的记忆 ID 是类似 f47ac10b-58cc-4372-a567-0e02b2c3d479 这样的 UUID,如果直接传给 LLM,容易出现幻觉(模型可能编造不存在的 UUID)。Mem0 的做法是把 UUID 映射成简单的整数(“0”、“1”、“2”),传给 LLM 做判断,拿到结果后再映射回真实 UUID。
这背后是一个更普遍的工程经验:不要让 LLM 处理它不擅长的事情。UUID 是结构化标识符,LLM 擅长语义理解,不擅长精确字符串匹配。用简单整数替代复杂 ID 是“扬长避短”的经典做法——这个思路在其他需要 LLM 处理结构化数据的场景中同样适用。
# 简化后的逻辑(mem0/memory/main.py)
uuid_mapping = {}
for idx, memory inenumerate(existing_memories):
uuid_mapping[str(idx)] = memory.id# "0" -> "f47ac10b-..."
# LLM 返回: {"action": "UPDATE", "id": "0", "new_text": "用户住在上海"}
# 映射回: {"action": "UPDATE", "id": "f47ac10b-...", "new_text": "用户住在上海"}
Step 4: 执行动作
根据 LLM 的决策,执行对应的存储操作:
-
ADD:生成新向量,写入 Vector Store,在 SQLite 记录创建事件
-
UPDATE:用新文本重新生成向量,替换 Vector Store 中的旧记录,在 SQLite 记录更新事件(保留旧值用于审计)
-
DELETE:从 Vector Store 删除向量,在 SQLite 标记为已删除
-
NONE:更新
updated_at时间戳(表示这条记忆仍然活跃)
每一步都在 SQLite 中留下审计记录——你可以追溯任何一条记忆的完整变更历史。
基准测试:数字与现实
v3 算法在三个公开基准上的表现:
|
|
|
|
|
|---|---|---|---|
| LoCoMo |
|
|
|
| LongMemEval |
|
|
|
| BEAM (1M) |
|
|
|
Mem0 团队开源了完整的评估框架(memory-benchmarks[1]),任何人都可以复现。
特别值得注意的是 LongMemEval 上助手记忆召回 +53.6 分的提升。这得益于 v3 算法将 Agent 生成的事实提升为”一等公民”——当 Agent 说”我已经帮你设置了每周三提醒”,这个信息也会被存入记忆,下次用户问”我设了什么提醒”时可以准确召回。
一个重要的 caveat:这些基准主要基于英文场景。在非英文环境、高噪声对话、或极端长上下文场景下,混合检索的召回质量、Graph Store 的维护成本、以及事实提取的准确率都可能有波动。把这些数字当作“上限”而非“保证”更稳妥。
10 分钟上手
说了这么多架构,来看看实际用起来有多简单:
from openai import OpenAI
from mem0 import Memory
openai_client = OpenAI()
memory = Memory()
defchat_with_memory(message: str, user_id: str = "default_user") -> str:
# 1. 检索相关记忆
relevant = memory.search(query=message, filters={"user_id": user_id}, top_k=3)
memories_str = "\n".join(f"- {m['memory']}"for m in relevant["results"])
# 2. 构造带记忆的 Prompt
system_prompt = f"你是一个有记忆的 AI 助手。以下是关于用户的已知信息:\n{memories_str}"
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": message}
]
# 3. 正常调用 LLM
response = openai_client.chat.completions.create(
model="gpt-4o-mini", messages=messages
)
assistant_reply = response.choices[0].message.content
# 4. 自动提取并存储新记忆
messages.append({"role": "assistant", "content": assistant_reply})
memory.add(messages, user_id=user_id)
return assistant_reply
就这么简单。memory.add() 会自动完成事实提取、去重、冲突解决。memory.search() 会做混合检索返回最相关的记忆。
实际成本估算:每次 add() 调用背后有 2 次 LLM 调用(事实提取 + 动作决策),加上 Embedding 调用。以 GPT-4o-mini 为例,一轮典型对话(5-10 条消息)的 add() 大约消耗 2000-4000 token input + 500-1000 token output,成本约 $0.001-0.003。如果用更大的模型(如 GPT-4o)做提取,成本会高 5-10 倍。search() 相对便宜,主要是 Embedding 调用的成本。选择什么模型做记忆提取,是成本和质量之间的权衡——默认的 gpt-4o-mini 对大多数场景够用。
你的代码几乎不需要改动——在已有的 LLM 调用前加一步 search,调用后加一步 add。这就是”记忆中间件”的设计优势。
多级记忆隔离
Mem0 的记忆不是一个大杂烩——它按四个维度做隔离:
# 用户级:每个用户有独立的记忆空间
memory.add("我喜欢暗色模式", user_id="alice")
memory.add("我喜欢浅色模式", user_id="bob")
# Agent 级:不同 Agent 可以有不同的"人设记忆"
memory.add("我是一个专注量化交易的助手", agent_id="quant_bot")
# 会话级:单次会话内的临时上下文
memory.add("用户正在调试一个 Python 脚本", run_id="session_123")
# 应用级:跨用户的全局知识
memory.add("公司的报销政策是...", app_id="hr_assistant")
这种隔离确保了:Alice 的偏好不会泄漏给 Bob,量化助手的人设不会污染客服助手,单次 debug 会话的上下文不会被长期保留。
横向对比:Mem0 vs Letta vs Zep
2026 年的 AI 记忆层赛道有三个主要选手,它们的设计哲学截然不同:
|
|
|
|
|
|---|---|---|---|
| 定位 |
|
|
|
| 类比 |
|
|
|
| 集成成本 |
|
|
|
| 记忆模型 |
|
|
|
| 核心优势 |
|
|
|
| 适用场景 |
|
|
|
怎么选?
如果你已经有一个 Agent 框架(LangChain / LlamaIndex / CrewAI),想快速加上记忆能力 → Mem0。几行代码接入,不需要改架构。
如果你在从零构建一个需要长期运行的自主 Agent → Letta。它的 “LLM OS” 理念——Agent 像操作系统一样主动管理自己的记忆——适合需要 Agent 在数周或数月内保持连贯身份的场景。代价是要买入它的整个运行时。
如果你的应用需要”这个信息是什么时候变化的”这种时序推理 → Zep。它的 Graphiti 引擎用双时序模型(事件发生时间 vs 记录时间)追踪信息演变,在审计和合规场景下特别有用。
我的判断:大多数场景选 Mem0 不会错。它是三者中集成成本最低、社区最大、灵活性最高的方案。等你的需求复杂到 Mem0 满足不了的时候,你自然会知道该换什么。
生产部署:三种模式
Mem0 提供三种部署方式,适应不同阶段:
1. 库模式:本地开发和测试
pip install mem0ai
数据默认存在本地磁盘(Qdrant 嵌入式模式 + SQLite)。零运维,开箱即用。
2. 自托管模式:数据主权
cd server && make bootstrap
# 一条命令启动整个栈,创建管理员,签发 API Key
# 访问 http://localhost:3000 管理面板
Docker Compose 一键部署,数据完全在你的服务器上。适合对数据主权有要求的企业场景。v1.0 之后默认开启认证——生产环境必须设置 ADMIN_API_KEY。
3. 云平台模式:零运维
注册 app.mem0.ai[2],获取 API Key,接入 SDK。Sub-50ms 检索延迟,自带 Dashboard 管理记忆和分析。
from mem0 import MemoryClient
client = MemoryClient(api_key="your-api-key")
client.add("用户喜欢暗色模式", user_id="alice")
选择建议:试水用库模式,团队开发用自托管,生产上量用云平台。
超越”记住偏好”:一些值得思考的方向
Mem0 目前解决的核心问题是”记住用户说了什么”。但 AI 记忆的疆域远不止于此:
1. Agent 记忆 ≠ 用户记忆
v3 算法已经迈出了关键一步——Agent 确认的操作也被记录为事实。但更深层的问题是:Agent 能否从自己的错误中学习?
想象一个编程助手,它昨天帮你调试时走了弯路——先试了方案 A 失败了,最终用方案 B 解决了。如果这个”方案 A 不适用于这类问题”的经验能被记住,下次遇到类似问题就能直接跳过方案 A。
这需要的不只是”事实存储”,而是经验蒸馏——从具体的操作序列中提炼出可复用的策略。目前 Mem0 的 custom_fact_extraction_prompt 配置提供了定制化的入口,但这个方向还有很大的探索空间。
2. 记忆的遗忘曲线
人类的记忆有遗忘机制——不重要的信息会自然衰减。AI 记忆目前是”完美记忆”——一旦存入就永远存在。但在生产环境中,一个两年前的用户偏好可能已经过时了。
Mem0 的 updated_at 时间戳和变更历史为实现”记忆衰减”提供了基础设施,但目前还没有内置的衰减策略。这可能是一个有意义的扩展方向。
3. 记忆的可解释性
当你的 AI 助手说”根据我的了解,你偏好使用 Python 而不是 Java”——用户可能会问:“你是怎么知道的?”
Mem0 的 SQLite 历史记录可以回答这个问题:这条记忆来自于 2026-04-15 的一次对话,原文是”帮我用 Python 重写这个 Java 脚本,我更喜欢 Python 的语法”。这种可追溯性在企业场景中尤其重要。
写在最后
回到开头的问题:更长的 Context Window 能解决 AI 的记忆问题吗?
答案很清楚:不能。Context Window 是工作记忆(Working Memory),Mem0 提供的是长期记忆(Long-term Memory)。就像你不会把整个图书馆搬到办公桌上——你需要的是一个聪明的图书管理员,知道什么时候该把哪本书放到你桌上。
Mem0 的设计选择给了我们几个启发:
-
“记忆中间件”是对的抽象层级。不要在 Agent 逻辑里硬编码记忆管理,把它拆出来作为独立的关注点。
-
用 LLM 管理记忆,而不只是存储记忆。Mem0 的核心竞争力不是向量数据库(那东西谁都有),而是 LLM 驱动的事实提取、去重和冲突解决。
-
多信号检索比纯语义检索靠谱得多。语义 + BM25 + 实体链接三路融合,是 v3 算法准确率大幅提升的关键。
如果你正在构建需要”记住用户”的 AI 应用,Mem0 值得认真看一看。
相关资源
-
项目地址:github.com/mem0ai/mem0[3]
-
论文:Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory[4](arXiv 2504.19413)
-
文档:docs.mem0.ai[5]
-
评估框架:github.com/mem0ai/memory-benchmarks[1]
夜雨聆风