长期对话里,用户上周说过“我吃素”,这周模型却在推荐鸡肉。扩大上下文窗口只能推迟遗忘,还会把每次请求变贵;传统 RAG 又把原始对话块整段检索回来,噪声很多。 Mem0 的做法是让 LLM 从对话里提取事实,再用 ADD / UPDATE / DELETE / NOOP 维护一个小而一致的记忆库。
论文要解决的是多会话长期记忆:对话跨越几天、几周,内容远超上下文窗口;而真实对话又不是按主题连续的,用户会在一堆无关话题之间跳。直接把整段历史塞进上下文,成本随长度增长;把原始对话切块做 RAG,又会把大量无关文本一起检索回来。
Mem0 把记忆做成“提取—更新”循环。提取时,LLM 同时看整段对话的摘要和最近 m=10 条消息,从新的消息对里提炼候选事实。更新时,先用向量检索出 top s=10 条相似旧记忆,再把候选事实和这些旧记忆一起交给 LLM,由它通过 tool call 选择 ADD / UPDATE / DELETE / NOOP。也就是说,记忆库的一致性由 LLM 自己维护,不另训分类器。
Mem0g 把事实进一步存成有向图:实体是节点,关系是三元组;新信息进来时先做冲突检测,过时的关系标记为 invalid 而不是物理删除,以保留时间信息。图版本在 LOCOMO 的 temporal 问题上更高,但建图和检索都更贵。
在 LOCOMO 上,Mem0 的 overall J 是 66.88%,Mem0g 是 68.44%;全文上下文仍然最高,72.90%,但 p95 延迟 17.117 s、每次约 26,031 token。Mem0 把 p95 压到 1.440 s,每次查询检索的 token 降到 1,764;代价是 overall J 低 6.02 个百分点。
先分清三个问题:忘掉旧信息、每次请求都变贵、检索回来的内容噪声太多。
论文用“素食者”场景开篇:用户说过自己吃素,几天后模型却推荐了鸡肉菜。这不是模型“没看到”,而是那段对话已经不在当前上下文里。LLM 的参数不会在推理时更新,固定窗口之外的信息等于不存在(§1)。
把窗口从 128k 扩到 200k/10M 甚至更大,能推迟这个问题,但推不掉。论文给了两个理由:第一,对话历史会持续增长,几周、几个月后总会超过任何有限窗口;第二,真实对话的主题并不连续,用户可能先提饮食偏好,再聊几个小时的编程,偏好信息会被大量无关内容淹没(§1)。即使能把全部历史塞进去,每次请求都要重新读一遍,token 成本和延迟都随长度上涨。
Table 2 里 full-context 的 overall J 是 72.90%,是所有方法里最高的;代价是每次请求约 26,031 token,p95 总延迟 17.117 秒。对话再长,这两个数字还会涨。
RAG 把对话切成固定长度块,按相似度取 top-k。块里往往混着与问题无关的句子,模型要在噪声里找答案。Table 2 里最好的 RAG 配置 overall J 是 60.97%,比 Mem0 低 5.91 个百分点。
Mem0 的假设是:与其在查询时从原始对话里“再找一遍”,不如在对话发生时就把显著信息提炼出来,维护成一个结构化、去重、可更新的记忆库。查询时只取相关事实,既减少 token,也减少噪声。
论文把对比对象分成六类,问题集中在“质量、延迟、成本”三者不能同时兼顾。
论文的基线覆盖六类(§3):已有的 LOCOMO 基准方法(LoCoMo、ReadAgent、MemoryBank、MemGPT、A-Mem)、不同 chunk 大小与 top-k 的 RAG、全文上下文、开源记忆方案 LangMem、OpenAI 的 ChatGPT memory 功能、以及记忆管理平台 Zep。Mem0 的全部 LLM 操作使用 GPT-4o-mini;可配置基线中,§3.3 只明确 LangMem 与 OpenAI 使用 GPT-4o-mini,Zep 未说明模型(§2.1、§3.3);RAG 与 LangMem 使用 text-embedding-small-3 作 embedding(§3.3);温度设为 0,除非另有说明(§3.3)。
| 方法 | Overall J (%) | Total p50 (s) | Total p95 (s) | Chunk / memory tokens |
|---|---|---|---|---|
| Full-context | 72.90 | 9.870 | 17.117 | 26,031 |
| RAG (k=2, 256) | 60.97 | 0.802 | 1.907 | 256 |
| LangMem | 58.10 | 18.53 | 60.40 | 127 |
| Zep | 65.99 | 1.292 | 2.926 | 3,911 |
| OpenAI memory | 52.90 | 0.466 | 0.889 | 4,437 |
| A-Mem | 48.38 | 1.410 | 4.374 | 2,520 |
| Mem0 | 66.88 | 0.708 | 1.440 | 1,764 |
| Mem0g | 68.44 | 1.091 | 2.590 | 3,616 |
Table 2 原表的 overall J 为 mean ± 1 SD(10 次独立运行,§3.2),例如 Mem0 66.88 ± 0.15%、Mem0g 68.44 ± 0.17%;本表只转录均值,± 值未转录。token 列是每次查询检索到的上下文,不是记忆库总大小(§3.2)。RAG 行的 256 是 chunk size;k=2 时每次检索两个块,不是 256 token 的检索上下文。
这张表里有两组对照最说明问题。第一组是 LangMem:记忆 token 只有 127,但搜索阶段 p50 就 17.99 秒、p95 59.82 秒,总延迟 p95 到 60.40 秒,交互场景基本不可用。第二组是全文上下文:J 最高,但 token 是 Mem0 的约 14.8 倍,p95 延迟是 Mem0 的约 11.9 倍。Mem0 想证明的是中间那条线:质量接近全文上下文;p95 延迟 1.440 s 低于最好的 RAG 的 1.907 s,但每次查询检索的 memory token 是 1,764,约为 RAG 单块 chunk size 256 token 的 6.9 倍;RAG 行是 k=2,实际拼接两个 chunk(最多约 512 token),对应约 3.4 倍。原文说的 “a fraction of the token and latency cost”(§4.3)是相对 full-context,不是相对 RAG。
Zep 是最接近的对手:overall J 65.99%,比 Mem0 低 0.89 个百分点;它的记忆 token 3,911,p95 2.926 秒。open-domain 上 Zep 的 76.60% 高于 Mem0 的 72.93%(差 3.67 个百分点),也高于 Mem0g 的 75.71%(差 0.89 个百分点)。
Mem0 的核心不是向量库,而是“提取 + 更新”这两个 LLM 环节;图版本只是把存储结构换成了实体—关系图。
每当有一对新消息(通常是用户消息 + 助手回复),系统不会直接存原文,而是构造一个提示:P = (S, {mt−m, …, mt−2}, mt−1, mt)。其中 S 是整段对话的摘要,后两项是最近 m 条历史消息和当前消息对。摘要由异步模块定期刷新,不阻塞主流程;论文这样设计的理由是:摘要提供全局主题,最近消息提供可能还没进摘要的细节(§2.1)。实验里 m=10。
提取函数 φ(P) 的输出是一组候选事实 Ω = {ω1, …, ωn}。这些事实是简短的自然语言陈述,不是原始对话片段;论文说这样做的目的是提高信噪比,让后续检索和回答都只面对“值得记”的内容。
每条候选事实都要和现有记忆库比对。系统先用向量检索取出 top s 条语义最相似的旧记忆(实验里 s=10),把候选事实和这 s 条旧记忆一起交给 LLM;LLM 通过 function-calling 接口从四个操作里选一个(§2.1、Appendix B Algorithm 1):
没有语义等价的旧记忆,就生成一个新 id,把事实加入记忆库。
旧记忆包含可补充的信息时,用新事实替换旧条目;论文的伪代码还比较了信息量,只有新事实信息更多才更新。
新事实与旧记忆矛盾时删除旧条目;事实已存在或不重要时什么都不做。
论文强调这里没有单独训练一个分类器,而是利用 LLM 的推理能力直接判断候选事实与旧记忆的语义关系。这样做的好处是不用为“该加还是该改”标注数据;代价是每次更新都要调 LLM,记忆写入的成本比纯向量库高。
图版本的存储是 G = (V, E, L):节点 V 是实体(如 Alice、San_Francisco),边 E 是关系(如 lives_in),标签 L 给节点一个语义类型(Person、City 等)。每个实体节点带三样东西:实体类型、一个 embedding、创建时间戳;关系存成三元组 (vs, r, vd)(§2.2)。
建图分两步:entity extractor 先从文本里识别实体及其类型,relationship generator 再判断实体之间是否存在有意义的关系,并给关系打标签。新信息进图时,conflict detector 找出可能冲突的已有关系,update resolver 判断哪些关系应该过时;过时的关系标记为 invalid,而不是物理删除,这样时间性推理仍然能看到“曾经成立、后来失效”的事实(§2.2、Figure 3)。
检索也分两路:一路是 entity-centric,先从 query 里识别关键实体,用语义相似度定位节点,再沿入边和出边扩展子图;另一路直接对所有关系三元组的文本做相似度检索,返回超过阈值的关系。两路结果一起作为回答上下文。实现上,图数据库用 Neo4j,实体与关系抽取仍由 GPT-4o-mini 完成(§2.2)。
Mem0 省的是查询时重读历史的开销,付的是写入时提取与更新的事实级 LLM 调用。
写入路径的调用次数由消息对数量和候选事实数共同决定:每来一对新消息,提取器做一次 LLM 调用;每条候选事实再做一次向量检索和一次 tool-call LLM 调用(§2.1、Appendix B Algorithm 1)。它不需要在每次写入时重读整段历史,但总调用数会随消息对数量增长。Table 2 的延迟只统计查询与回答,论文只在 §4.5 给出“图构建最坏情况一分钟内完成”这一句写入侧时间,没有写入 token、延迟分布或吞吐数据。
Table 2 的 token 列是每次查询检索到的记忆上下文:Mem0 为 1,764 token,Mem0g 3,616,Full-context 26,031(§3.2 定义)。Mem0 的 search 延迟 p50 / p95 为 0.148 / 0.200 秒,总延迟 0.708 / 1.440 秒。
§4.5 按 conversation 统计记忆库规模:Mem0 平均约 7k token,Mem0g 约 14k,Zep 超过 600k;原始对话平均约 26k token。查询时并不会把整个库塞进上下文。
图版本的额外开销集中在构建与更新:实体抽取、关系生成与 update resolver 依赖 LLM;冲突检测的具体实现论文未展开。论文称 Mem0g 的图构建在最坏情况下也能在一分钟内完成(§4.5),但没有给出节点/边规模、并发度或失败重试的细节。作为对照,§4.5 报告 Zep 写入后立即检索常失败,数小时后重跑同样的检索,结果明显更好;论文推测这与异步 LLM 调用和后台处理有关。
查询成本不随对话历史线性增长:Mem0 只检索 top s 条相似记忆,Mem0g 只扩展相关子图与超过阈值的三元组(§2.1、§2.2)。写入成本随消息对数量和事实数量增长推断(依据 §2.1:每对消息一次提取调用,每条候选事实一次更新调用);论文没有报告多用户并发写入下的吞吐或限流策略,这两项是生产部署仍需补测的部分。
上半部分是 Mem0 的写入循环,下半部分是 Mem0g 的图构建与检索。
两条流的共同点是“先把信息变少,再交给 LLM”。Mem0 把对话变成事实,Mem0g 把事实变成实体和关系;查询时不再是“读全部历史”,而是“读与当前 query 相关的一小部分”。这也解释了为什么它能同时降低 token 和延迟推断(依据 §2.1 的提取—更新流程:查询侧只读相关事实,写入侧才做提取与更新):省掉的是每次请求重读历史的开销,增加的是写入时的提取与更新 LLM 调用。
先交代评测设置,再看四类问题的 J、overall 与效率;最后指出图版本不是每个维度都更好。
这些数字的边界由 LOCOMO 的规模和指标决定:10 段多会话日常对话,质量分主要来自 LLM 裁判。
| 项目 | 设置 | 出处 |
|---|---|---|
| 数据集 | LOCOMO:10 段多会话对话,每段约 600 轮、平均 26,000 token,平均 200 个问题 | §3.1 |
| 问题类型 | single-hop、multi-hop、open-domain、temporal;原数据集的 adversarial 类因没有标准答案被排除 | §3.1 |
| 质量指标 | F1、BLEU-1、LLM-as-a-Judge(J)。§3.2 称 J 从事实准确性、相关性、完整性、上下文适配四个维度评估;Appendix A 的 judge prompt 实际只要求输出 CORRECT / WRONG 并附一句理由——两处口径并列 | §3.2、Appendix A |
| 效率指标 | search / total 的 p50 与 p95 延迟;chunk 或 memory token 数 | §3.2、Table 2 |
| 推理引擎 | Mem0 的 LLM 操作使用 GPT-4o-mini;可配置基线中 LangMem 与 OpenAI 明确使用 GPT-4o-mini,Zep 未说明模型;RAG 与 LangMem 的 embedding 用 text-embedding-small-3(§2.1 只说明向量库使用 dense embeddings) | §2.1、§3.3 |
| 更新参数 | 最近消息 m=10;相似旧记忆 s=10 | §2.1 |
| 温度 | 0,除非另有说明 | §3.3 |
| J 的重复 | 每个方法在整份数据上独立跑 10 次,报告均值 ±1 SD | §3.2 |
| 图存储 | Neo4j | §2.2 |
这套设置有一个明确的限制:只有一个数据集,而且只覆盖多会话日常对话。LOCOMO 的 10 段对话平均 26k token,大约相当于一段较长对话,不是跨月的真实产品日志。论文用 10 次独立运行的均值来降低 J 的随机性,但没有在第二个数据集上复验。
另一个细节是 adversarial 问题被排除。这类问题测的是“识别无法回答的问题”,没有标准答案,论文说预期行为是模型应该识别出来;排除它是合理的,但也意味着这套评测没有覆盖“记忆里没有答案时会不会胡编”这一风险。
Table 1 按问题类型拆 J。下表只转录 6 个方法;MemGPT、LoCoMo、ReadAgent、MemoryBank 等行以及 F1 / BLEU-1 列未转录。
| 方法 | Single-Hop | Multi-Hop | Open-Domain | Temporal |
|---|---|---|---|---|
| A-Mem* | 39.79 | 18.85 | 54.05 | 49.91 |
| LangMem | 62.23 | 47.92 | 71.12 | 23.43 |
| Zep | 61.70 | 41.35 | 76.60 | 49.31 |
| OpenAI | 63.79 | 42.92 | 62.29 | 21.71 |
| Mem0 | 67.13 | 51.15 | 72.93 | 55.51 |
| Mem0g | 65.71 | 47.19 | 75.71 | 58.13 |
Table 1 原表的 J 为 mean ± 1 SD(10 次独立运行,§3.2),例如 Mem0 的 single-hop 是 67.13 ± 0.65;本表只转录均值,± 值未转录。A-Mem* 为作者重跑 A-Mem 的结果,temperature=0,以生成 LLM-as-a-Judge 分数(Table 1 caption)。
下面把 Table 2 的 8 个方法按三种指标重画。切换指标会重画全部柱子;柱高按当前指标的最大值缩放,标签是表格原值,不是归一化后的分数。陶土红是 Mem0,深陶土是 Mem0g,灰柱是基线。
配置 A:质量。Mem0 的 overall J = 66.88,最好的 RAG = 60.97,绝对差 = 66.88 − 60.97 = 5.91 个百分点;Full-context = 72.90,比 Mem0 高 72.90 − 66.88 = 6.02 个百分点。Mem0g 比 Mem0 高 68.44 − 66.88 = 1.56 个百分点,相对提升 1.56 / 66.88 = 2.3%。柱高(当前指标最大值是 Full-context 的 72.90):Mem0 66.88 / 72.90 = 91.7%,Mem0g 68.44 / 72.90 = 93.9%。
配置 B:p95 总延迟。Full-context = 17.117 s,Mem0 = 1.440 s,降幅 (17.117 − 1.440) / 17.117 = 91.6%;Mem0g = 2.590 s,降幅 (17.117 − 2.590) / 17.117 = 84.9%。LangMem 的 p95 是 60.40 s,比 Full-context 还高。柱高(当前指标最大值是 LangMem 的 60.40):Mem0 1.440 / 60.40 = 2.4%,Mem0g 2.590 / 60.40 = 4.3%。
交互只把表格数值按当前指标的最大值缩放成柱高,不做平均或标准差;表里的 J 是 10 次独立运行的均值(±1 SD 未转录),论文没有说明延迟的重复次数,页面不重新计算。柱高缩放只影响视觉,读数与手算都用表格原值。p95 读数按原表精度显示,60.40 不补成 60.400。
Full-context 的 overall J 是 72.90%,比 Mem0 高 6.02 个百分点,比 Mem0g 高 4.46 个百分点。也就是说,如果只追求准确率,把全部历史塞进上下文仍然更好。Mem0 的卖点是:用 91.6% 的 p95 延迟降幅和 93.2% 的 token 降幅,换 6.02 个百分点的质量损失。这个交换在实时交互里可能值得,但不是“全面超过全文上下文”。
token 的口径是“每次查询检索到的记忆上下文”,不是记忆库总大小:Full-context 每次约 26,031 token,Mem0 检索 1,764 token,减少 93.2%;Mem0g 检索 3,616 token,减少 86.1%(两个降幅都相对 full-context 的 26,031 输入上下文)。记忆库本身按 §4.5 平均为 Mem0 约 7k tokens/conversation、Mem0g 约 14k。这里的 token 不含每次回答的生成开销,论文也没有把写入阶段的 LLM 调用 token 计入这张表。
Mem0g 的 overall J 比 Mem0 高 1.56 个百分点(相对 2.3%),temporal 上高 2.62 个百分点(58.13 对 55.51),open-domain 高 2.78 个百分点。但在 multi-hop 上,Mem0g 的 47.19% 比 Mem0 的 51.15% 低 3.96 个百分点,也低于 LangMem 的 47.92%。作者在 §4.1 和 §4.2 给出了推测性解释——图结构在复杂整合任务里可能存在 “potential inefficiencies or redundancies” / “potential overhead or redundancy”,但没有做消融;这里按 Table 1 并列,不把推测写成结论。
成本也更高:Mem0g 每次查询检索 3,616 token,约为 Mem0(1,764)的 2.05 倍;p95 总延迟 2.590 秒,比 Mem0 的 1.440 秒高。图构建本身论文说在 worst case 下也能在一分钟内完成(§4.5),但这属于写入侧开销,Table 2 的延迟只统计查询与回答。
Zep 的 overall J 65.99%,比 Mem0 低 0.89 个百分点,但 open-domain 是 76.60%,比 Mem0g 的 75.71% 高 0.89 个百分点,也是 J 列最高。这说明“总榜第一”不等于每个问题类型都第一。LangMem 则是另一个极端:记忆只有 127 token,但 p95 总延迟 60.40 秒,是全文上下文的约 3.5 倍。
论文摘要声称「在四类问题上均领先」,但 Table 1 的 open-domain J 最高是 Zep 的 76.60%(Mem0g 75.71%、Mem0 72.93%),两处并列,不替原文消解。
论文摘要说 Mem0 相对 OpenAI 的记忆功能在 J 上有 26% 的相对提升。按 Table 2 的 66.88 对 52.90 复算:(66.88 − 52.90) / 52.90 = 26.4%,与摘要一致。这里是相对提升,绝对差是 13.98 个百分点。
这些限制决定了 Mem0 的数字能推广到哪里,以及部署时要额外补什么。
10 段多会话日常对话、平均 26k token。法律、医疗、企业知识库这类领域没有实验;跨月、跨产品的真实记忆负载也没有覆盖。
J 由另一个 LLM 打分。§3.2 说它从事实准确性、相关性、完整性、上下文适配四个维度评估;Appendix A 的 judge prompt 却只要求输出 CORRECT / WRONG,并附一句理由。两处口径并列,页面不替原文调和。裁判模型可能偏爱某种风格,论文没有报告与人类评分的相关性。
每条新事实都要检索旧记忆并做一次 tool call。Table 2 的延迟只统计查询与回答,没有把写入的 LLM 调用算进去;高频写入场景的成本需要单独测。
对抗问题被排除。LOCOMO 原有的 adversarial 类问题没有标准答案,论文把它排除在评测之外。这意味着页面上的 J 分数不包含“记忆里没有相关信息时,模型会不会编一个答案”这一维度。对生产系统来说,这个维度至少和准确率同样重要。
更新操作本身可能出错。ADD / UPDATE / DELETE / NOOP 由 LLM 判断;如果候选事实抽取错了,或者相似旧记忆检索错了,后续操作会把错误写进记忆库。论文没有给出更新操作的准确率,也没有做对抗性注入实验(例如故意让新消息与旧记忆矛盾)。
图版本的时间信息是“标记”而非“删除”。过时关系被标成 invalid,查询时是否、以及如何过滤 invalid 关系,论文在正文只说“enable temporal reasoning”,没有给出过滤策略的消融。invalid 关系长期累积会不会拖慢检索,论文没有报告。
什么时候别用:如果任务只需要单轮问答、对话不超过上下文窗口,Mem0 的提取与更新开销是多余的;如果写入频率极高而查询很少,省下的查询 token 可能抵不过写入调用。反过来,在多会话、查询频繁、上下文成本敏感的场景里,它的思路最直接:把“每次重读历史”换成“写入时提炼、查询时取事实”。