这篇笔记来自我对开源 AI 系统的长期拆解:先跑起来,再读迁移文档,最后记录抽象在哪些地方成立,又在哪些地方开始漏水。
问题从来不是“记得越多越好”
只要对话还装得进上下文窗口,LLM 就能维持短暂的连续感。但这不是长期记忆,更不是可靠的应用状态。会话结束之后,模型不会自然记住用户偏好简短回答、上个月换了工作,或者已经放弃某个计划。
最直接的做法是每次把全部历史重新塞进提示词。演示阶段可以,进入产品后很快会腐化:提示越来越长,延迟和成本持续上升,旧信息挤压当前任务,敏感历史也被反复发送。
Mem0 位于原始聊天记录与业务数据库之间。它试图从交互中抽取少量可复用事实,以身份边界存储,再只召回当前问题需要的记忆。它的价值不在于保存一切,而在于有意识地遗忘大部分内容,并在必要时可靠地找回少数内容。
本文以 2026 年 7 月可见的 Mem0 OSS v3 与 Platform 文档为准。仍然围绕 enable_graph、graph_store 或 Neo4j 展开的教程,描述的是旧架构,不应直接复制到新的 OSS 部署。
OSS v3 的四层心智模型
理解当前开源版,不需要先画一张复杂架构图。四个部分已经足够:
- 提取模型:把消息或对话压缩为值得长期保存的记忆候选。
- 向量存储:保存记忆文本、嵌入、元数据,以及伴随的实体集合。
- 历史存储:记录记忆操作,服务于审计、调试和检查。
- 检索管道:先取得语义候选,再按可用情况融合关键词、实体和时间相关信号。
Python 库的本地默认值可能使用 Qdrant 保存向量、SQLite 保存历史;自托管服务栈的默认配置又可能不同。这些默认值适合试验,不是生产架构建议。真正重要的边界是:这里是一套向量存储加操作历史,而不是“向量数据库加图数据库”的强制双库结构。
写入:单次 ADD-only
OSS v3 移除了旧算法中的第二次 LLM 决策。过去,系统会再次判断候选记忆应该新增、更新还是删除;现在的自动提取大致是:
messages
→ 召回相关旧记忆作为上下文
→ 一次 LLM 调用提取独立、持久的事实
→ 批量生成嵌入
→ 精确哈希去重
→ 写入向量存储
→ 抽取并链接实体
→ 在历史存储中记录操作
自动提取的结果是 ADD-only。新陈述不会悄悄改写或删除旧记忆。这样减少了提取成本,也保留了证据,但把“什么才是当前事实”的责任交回给检索和产品生命周期。
例如,用户先说“我住在深圳”,半年后说“我搬到上海了”,两条记忆可能同时存在。系统必须依靠时间表达、元数据、明确的纠错流程,或应用侧归并来判断当前状态。
ADD-only 不等于 SDK 没有管理性的更新与删除接口。它只表示处理新对话时,自动提取算法不再输出 UPDATE 与 DELETE 决策。这个边界在代码评审中值得说清楚。
检索:语义候选,再融合三类信号
搜索先从向量语义召回开始,再对候选结果叠加信号:
- BM25 关键词信号奖励预处理后的词面重合。以 Qdrant 为例,迁移文档说明稀疏关键词搜索需要
fastembed;缺少依赖时会告警并回退。 - 实体信号从查询中抽取实体,与伴随实体集合匹配,再提升链接到这些实体的记忆。
- 时间信号用于表达新近程度或时间相关性,帮助系统面对同时存在的新旧陈述;它不是替代业务版本控制的魔法。
这些信号服务于候选融合与排序。BM25 和实体匹配主要提升已有语义候选,而不是完全独立的召回通道;相关依赖不可用时,语义搜索仍可工作。
实体链接还会产生 linked_memory_ids,便于观察哪些记忆共享实体。但它不是可查询的知识图谱。旧版 relations 返回不再填充,排序分数背后也没有隐藏的 OSS 图遍历接口。
OSS 与 Platform 的图能力必须分开看
Mem0 的“图”经历过一次足以误导选型的变化。在当前 OSS v3 中,以下能力已被移除:
enable_graph/enableGraphgraph_store/graphStore- Mem0 对 Neo4j、Memgraph、Kuzu、Apache AGE、Neptune 的直接集成
- 独立的图检索路径和对外关系结果
OSS 的实体链接取代了这套机制:它抽取名称与名词短语,把实体写入现有向量存储的伴随集合,用匹配结果参与排序。不再需要为了 Mem0 单独部署第二个图数据库。
Mem0 Platform 是另一条产品边界。托管项目可以提供原生 Graph Memory 或关系能力,而不要求客户配置外部图存储。平台功能和套餐会演进,因此应该按准备购买的方案核对,而不是照着旧控制台截图设计。无论 Platform 提供什么,都不能推导出 OSS 的 graph_store 仍然有效。
如果产品真正需要图遍历,例如“找出与这次事故相隔两跳的所有供应商”,就把图数据库作为显式的应用依赖,并建立领域模型。检索排序中的实体加权不是事实图谱的替代品。
一个边界正确的 OSS 最小闭环
按照官方快速开始配置模型、嵌入和存储提供商后,Python 侧的核心流程很小:
from mem0 import Memory
memory = Memory()
memory.add(
[
{
"role": "user",
"content": "我希望发布说明先写风险,再写功能。",
},
{
"role": "assistant",
"content": "之后我会把运行风险放在开头。",
},
],
user_id="user_42",
)
results = memory.search(
"这份发布说明应该怎么组织?",
filters={"user_id": "user_42"},
top_k=5,
)
for item in results["results"]:
print(item["memory"], item["score"])
v3 的参数位置并不对称:add() 可以在顶层接收实体 ID,search() 与 get_all() 则要求把实体 ID 放进 filters。把 user_id 继续作为顶层搜索参数会报错。
在多用户服务里,filters={"user_id": ...} 不是装饰,而是召回的租户边界。它必须来自服务端已认证身份,不能来自模型输出或客户端随意提交的字段。需要更细的所有权时,再加入 agent_id、run_id 或元数据过滤。
召回结果同样是不可信上下文,不是命令。将记忆放在明确分隔的提示区域,限制条数与 token 总量,并禁止存储文本覆盖系统策略。
谁应该拥有哪种状态
持久化架构清晰的前提,是每个组件只负责一类事实。
| 需求 | 更合适的起点 | 不应承担的职责 |
|---|---|---|
| 恢复中断的 LangGraph 执行 | LangGraph checkpointer | 跨会话语义个性化 |
| 记住偏好与反复出现的事实 | Mem0 OSS 或 Platform | 订单、权限、余额和流程真相 |
| 保存权威业务数据 | PostgreSQL 等普通数据库 | 概率式记忆提取 |
| 托管运行和规模化能力 | Mem0 Platform | 跳过供应商、驻留地和成本审查 |
| 控制模型与数据平面 | Mem0 OSS | 零运维承诺 |
成熟 Agent 往往同时使用三层:checkpointer 保存当前运行,Mem0 负责选择性的跨会话召回,普通数据库保存绝不能记错的业务事实。重复并不可怕,只要所有权清楚;真正危险的是把偏好存储逐渐用成客户账本。
纠错、删除和隐私是架构本身
有用的记忆通常很私人,因此记忆质量越高,隐私风险也可能越高。上线之前先写保留策略:哪些类别允许提取,哪些禁止进入记忆,每类保存多久。健康信息、凭证、精确位置和推断出的敏感特征,不应只因为 LLM 觉得“有用”就被保留。
生命周期至少要做到可观察:
- 每次写入记录已认证主体、来源消息、提取版本、记忆 ID 与时间。
- 给用户提供查看、纠正和删除记忆的入口。
- 删除时沿用搜索的身份范围,并核验向量存储、实体集合、历史策略、备份和分析副本。
- 管理审计数据只保留到法律与运维目的所需的期限。
- 用恶意构造的标识和跨用户查询测试租户隔离。
历史存储有助于调试,却也可能保留已经删除的个人文本。“不再参与召回”和“完成个人数据擦除”是两件事,产品、法务和基础设施团队必须在发布前给出同一个答案。
错误记忆的纠正也不能只靠下一轮对话。用户应该能看到旧记忆为何被召回,明确标记它失效,必要时调用管理接口删除,并验证缓存和衍生索引同步完成。否则 ADD-only 会把一次误解沉淀成长期偏见。
延迟和成本要按完整一轮测量
Mem0 在读写两侧都会增加工作。
写入包含 LLM 提取、嵌入、向量插入与实体处理;读取包含查询嵌入或预处理、搜索、信号融合,以及可选重排。用户感受到的是总和,而不是某个组件最好看的单项数字。
至少跟踪:
- add 的 p50、p95、p99 延迟
- 不同
top_k下 search 的 p50、p95 延迟 - 每轮提取的模型 token 与成本
- 嵌入、重排和存储成本
- 活跃用户每月新增记忆数
- 向量与历史数据增长
- 召回记忆带来的额外提示 token
写入未必需要阻塞回答。异步管道能降低对话延迟,但会引入一致性窗口:下一轮问题可能早于新记忆可检索。每个场景都要明确更看重即时性还是吞吐量,并把这段延迟写进测试。
我会如何做本地评测
Mem0 公布了 v3 在 LoCoMo、LongMemEval 上的提升和更低提取延迟。这些是项目方结果,可以说明技术方向,但不能替代采购判断,因为真实用户、语言、实体名称和成本约束都不同。
用获得同意的代表性对话或合成数据建立小型评测集,并标注:
- 应该成为记忆的事实
- 绝不应存储的细节
- 纠正与随时间变化的事实
- 每条记忆应被哪些查询召回
- 容易混淆、但应该排在后面的近邻记忆
然后把评测拆成三层。
提取层关注精确率、召回率、重复率、敏感记忆率,以及每百轮新增矛盾数。专门检查 ADD-only 是否让旧陈述在没有时间上下文时继续影响回答。
检索层关注 recall@k、precision@k、MRR 和“有害召回”——听起来合理、实则错误的记忆。分别跑纯语义、启用 BM25、启用实体信号和启用重排的消融测试。实体用例要覆盖别名、错拼、同名人物、多语言名称与复合产品名,并检查 linked_memory_ids 是否连对记录。
产品层比较任务成功率、用户纠正率、总轮次成本和 p95 延迟,并与没有长期记忆的基线对照。检索指标变好,产品仍可能变差,因为陈旧的个性化往往比通用回答更令人不适。
每次更换提取模型、嵌入模型、自定义指令、向量后端或阈值,都应该回放同一版本的评测集。记忆是一种行为,行为就需要回归测试。
我的判断
Mem0 OSS v3 比旧版“LLM 加向量库再加 Neo4j”的叙述更清晰。单次 ADD-only 让写入路径更容易推理;混合排序承认语义相似度并不足够;内建实体链接去掉了图数据库依赖,同时保留一部分实体感知价值。
代价也因此更清楚:Mem0 是相关性系统,不是真相系统。
当跨会话个性化足够重要,团队愿意为它建立评测、治理和删除机制时,我会采用 Mem0。仅仅因为“Agent 应该有记忆”,我不会引入它。也许 checkpointer 已经解决真正的问题,也许事实本来就在数据库里,也许当前任务只需要一段更克制的提示。
好的记忆不是无限保存,而是在用户控制之下保持选择性的连续。





读者回响