Agent 的"失忆"问题,几乎是每个做对话系统的人都绕不开的一道坎。你精心调教的助手,在单轮对话里表现得像个专家,可一旦跨会话、跨天、跨项目,它就把之前聊过的偏好、约束、结论全忘干净,每次都要用户从头交代一遍背景。这不是模型能力不够,而是架构里压根没给它准备"记住东西的地方"。长期记忆要解决的核心,就是让 Agent 在会话结束后仍能保留关键信息,并在后续交互中按需召回——它决定了 Agent 能不能从"一次性工具"进化成"持续协作的伙伴"。
围绕这个目标,业界已经跑出了几条差异明显的技术路线。Mem0 走的是轻量抽取加向量检索的路子,Zep 把时间维度做进了记忆图谱,Graphiti 用双时态图结构处理事实的演化,另外还有以 Letta(原 MemGPT)为代表的分层记忆管理、以 Cognee 为代表的数据管道式记忆,以及 LangMem 这类偏工具化的记忆原语方案。这篇内容不打算给你一个"谁最强"的排名,而是把六个主流项目的设计思路拆开对比,讲清楚每种方案在解决什么问题、代价是什么、什么场景下该选谁。如果你正在给 Agent 加记忆,或者被"记不住"和"记太多"两头夹击,这篇应该能帮你少走几段弯路。
1. 长期记忆到底难在哪:先看清问题的形状
很多人一上来就问"用哪个记忆库好",但如果不先搞清楚长期记忆的难点分布,选型基本靠猜。我见过不少项目,前期随便接了个向量库就上线,结果三个月后记忆库膨胀到几十万条,检索出来的全是噪音,Agent 反而被"错误的记忆"带偏。所以这一节先把问题本身讲透。
1.1 记忆不是存储,是"写入-管理-召回"的闭环
把长期记忆理解成"存东西"是最常见的误解。真正的记忆系统是一条闭环链路:写入决定什么信息值得记、以什么形式记;管理决定旧信息怎么更新、冲突怎么消解、过期怎么清理;召回决定在当前上下文里该取出哪几条。三个环节里任何一个掉链子,整体体验都会崩。
举个具体的例子。用户第一次说"我住在杭州",第二次说"我搬到上海了"。如果系统只会追加写入,那检索时可能同时召回"住杭州"和"住上海"两条矛盾信息,Agent 就会精神分裂。这就不是存储容量的问题,而是管理环节缺失——没有机制去判断新事实是否覆盖旧事实。Mem0 和 Zep 在这块都做了专门设计,后面会细讲。
再比如召回。用户问"帮我推荐个周末去处",系统如果按语义相似度硬检索,可能召回一堆历史闲聊。真正有效的召回需要结合时间衰减、重要性权重、当前意图多个维度。这就是为什么单纯"向量库 + top-k"的方案,在真实场景里往往不够用。
1.2 三类记忆,别混为一谈
从业者圈子里现在比较通用的分法是三层:
- 工作记忆(Working Memory):当前会话的上下文窗口,随对话结束而消失,本质是 token 预算管理。
- 短期记忆(Short-term Memory):跨几轮对话的临时状态,比如用户当前正在处理的任务进度。
- 长期记忆(Long-term Memory):跨会话持久化的用户偏好、事实、经验,是本文讨论的重点。
很多号称"有记忆"的产品,其实只做到了短期记忆——把最近 N 轮对话塞进 prompt。这能撑住一次会话,但用户明天再来,一切归零。真正的长期记忆必须落到外部持久化存储,并且有独立的读写策略。
提示:判断一个 Agent 是否具备真正的长期记忆,最简单的测试是——关掉会话,隔一天再问它"我上次让你记的那件事是什么"。答不上来,就是没有长期记忆。
1.3 为什么"全塞进上下文"这条路走不通
有人会想,现在模型上下文都上百万 token 了,直接把历史全塞进去不就行了?实测下来这条路有三个硬伤。
第一是成本。每次请求都带上几十万 token 的历史,推理费用会指数级上升,尤其是高频调用的 Agent 场景,账单会非常难看。第二是注意力稀释。上下文越长,模型对关键信息的注意力越容易被淹没,这就是所谓的"lost in the middle"现象——中间部分的信息召回率明显下降。第三是无法跨用户隔离。多用户场景下,你不可能把所有人的历史都塞进同一个上下文。
所以长期记忆的本质,是用外部存储换上下文空间,通过精准的写入和召回,让有限的上下文里只放最相关的几条记忆。理解了这一点,再看六个项目的设计,思路就清晰多了。
2. Mem0:把记忆抽取做成一条轻量流水线
Mem0 是这几个项目里上手门槛最低的一个,也是很多团队做记忆功能的第一站。它的核心思路很直接:用 LLM 从对话里抽取事实,存进向量库加图存储的混合结构,召回时做多路融合。听起来简单,但它在"抽取"和"更新"这两个环节的设计,是它区别于裸向量库的关键。
2.1 抽取阶段:LLM 当"记忆编辑"
Mem0 的写入流程大致是这样:一段对话进来,先由 LLM 判断哪些内容值得记,抽成结构化的事实条目,比如"用户偏好素食""用户在做一个 Rust 项目"。这一步的价值在于降噪——原始对话里大量寒暄、重复、无关内容被过滤掉,进库的都是精炼事实。
这里有个容易踩的坑:抽取的粒度。抽得太细,一条对话能抽出二十条记忆,库膨胀快且碎片化;抽得太粗,一条记忆里塞了多个事实,后续更新时没法单独修改。我的经验是一条记忆只承载一个可独立更新的事实,这样冲突消解才好做。Mem0 默认的抽取 prompt 偏中等粒度,实际用的时候建议根据自己的场景调一调。
2.2 更新阶段:ADD / UPDATE / DELETE 三态决策
这是 Mem0 比较有辨识度的设计。新抽取的事实进来时,系统会先检索出相关的旧记忆,然后让 LLM 做一个决策:
| 决策 | 触发条件 | 效果 |
|---|---|---|
| ADD | 新事实与旧记忆无冲突 | 直接新增一条 |
| UPDATE | 新事实补充或修正旧记忆 | 修改已有条目 |
| DELETE | 新事实否定旧记忆 | 删除失效条目 |
这个三态机制解决的正是前面说的"住杭州 vs 住上海"问题。用户说搬家了,系统检索到"住杭州",判定为 UPDATE 或 DELETE,而不是傻乎乎地追加。实测下来,这套机制对用户画像类记忆效果很好,因为这类事实更新频繁且冲突明显。
不过要注意,这个决策依赖 LLM 判断,会有误判。我遇到过把"用户喜欢咖啡"和"用户喜欢茶"判成冲突然后删掉一条的情况——其实两者可以共存。所以生产环境里,关键记忆的删除最好加一层人工确认或软删除,别让 LLM 直接物理删除。
2.3 召回阶段:向量 + 图 + 元数据的多路融合
Mem0 的召回不是单纯向量相似度,而是把向量检索、图关系检索、元数据过滤结合起来打分。图存储的作用是捕捉实体间关系,比如"用户 - 工作在 - 某公司 - 位于 - 某城市",这种链式关系纯向量很难表达。
这套融合召回在多跳问题上优势明显。用户问"我同事推荐的那家餐厅在哪",如果记忆里有"同事是张三""张三推荐了某餐厅""某餐厅在某地"三条,图检索能把它们串起来。纯向量方案可能只召回其中一条。
2.4 什么时候该选 Mem0
Mem0 适合快速落地、场景偏用户画像和偏好记忆的项目。它的 API 设计友好,几十行代码就能跑通,社区活跃,文档也全。但如果你的场景需要强时间推理(比如"上周三我说了什么")或者复杂事实演化追踪,Mem0 的图能力相对有限,这时候就该看 Zep 和 Graphiti 了。
3. Zep:把时间轴焊进记忆图谱
Zep 和 Mem0 最大的区别,是它从设计之初就把时间当成一等公民。Mem0 的记忆是"当前状态",Zep 的记忆是"随时间演化的状态序列"。这个差异决定了它更适合处理"事情是怎么一步步变成现在这样的"这类问题。
3.1 时序知识图谱:事实带时间戳
Zep 底层用的是 Graphiti 引擎(对,就是下一个要讲的项目,Zep 团队做的),构建的是一张时序知识图谱。每条边(事实)都带有效时间区间,比如"用户 - 居住于 - 杭州"这条边,有效时间是 2023-01 到 2024-06,之后被"居住于 - 上海"取代。
这种设计的好处是历史可追溯。用户问"我去年住哪",系统能精确回答杭州,而不是被最新状态覆盖。这在客服、健康管理、个人助理这类需要回顾历史的场景里非常关键。
3.2 双时态:事实的"发生时间"和"记录时间"分开
Zep 和 Graphiti 都用了**双时态(bi-temporal)**模型,这是理解它们的关键。简单说,每个事实有两个时间维度:
- 事件时间(valid time):事实在现实世界里成立的时间段。
- 摄入时间(ingestion time):系统什么时候知道这个事实的。
为什么要分开?因为信息有延迟。用户可能今天才告诉你"我上个月换了工作",事件时间是上个月,摄入时间是今天。如果只有一个时间戳,系统就没法区分"这件事什么时候发生的"和"我什么时候知道的"。双时态让系统能回答"截至上周,我知道的用户状态是什么"这类复杂查询。
3.3 增量更新与冲突消解
Zep 的写入是增量的,新事实进来会与图谱里相关边做比对,自动处理时间区间。旧事实不是被删除,而是被标记为失效,时间区间闭合。这样既保留了历史,又保证了当前状态查询的准确性。
实测中我发现,这套机制对频繁变更的状态特别友好,比如用户的职位、地址、项目状态。但代价是存储和计算开销更大——维护一张时序图比维护一个向量库复杂得多。小规模场景可能感觉不到,数据量上去后,图数据库的运维成本要提前考虑。
3.4 Zep 的适用边界
Zep 适合需要时间推理、事实演化追踪、历史回溯的场景。典型如个人助理(记住用户的时间线)、CRM(客户状态变迁)、医疗健康(症状和用药历史)。如果你的场景只是"记住用户喜欢什么",用 Zep 属于杀鸡用牛刀,Mem0 更划算。
4. Graphiti:双时态图引擎的底层逻辑
Graphiti 是 Zep 团队开源的底层引擎,可以独立使用。把它单独拎出来讲,是因为它的双时态图模型是这几个项目里最"硬核"的设计,理解了它,你对记忆系统的认知会上一个台阶。
4.1 节点、边、时间区间:图的基本构件
Graphiti 的图里,节点是实体(人、地点、事件、概念),边是实体间的关系。每条边携带四个时间字段:valid_at、invalid_at、created_at、expired_at。前两个描述事实在现实中的有效区间,后两个描述系统记录的区间。
当一条新边进来,Graphiti 会做边失效检测:如果新边与旧边语义冲突(比如同一实体的同一关系类型),旧边的invalid_at被设为新边的valid_at,实现平滑过渡。这个过程是自动的,不需要人工干预。
4.2 混合检索:语义 + 关键词 + 图遍历
Graphiti 的召回支持三种检索方式的组合:
- 语义检索:基于 embedding 的相似度。
- 关键词检索:BM25 这类传统全文检索,对专有名词、ID 特别有效。
- 图遍历:从种子节点出发,按关系扩展。
三者融合打分,能覆盖不同类型的查询。我实测下来,关键词检索在召回精确实体名时比纯语义更稳,比如用户问"关于项目 Apollo 的进展",BM25 能精准命中"Apollo",而语义检索可能召回一堆泛泛的项目讨论。
4.3 增量构建:不用重建整张图
很多图记忆方案的痛点是"更新要重建"。Graphiti 支持增量写入,新数据直接并入现有图,不需要全量重算。这对持续运行的 Agent 至关重要——你不可能每次用户说句话就把整张图重建一遍。
4.4 自己用 Graphiti 的注意事项
Graphiti 是引擎不是产品,直接用它需要自己搭存储(默认接 Neo4j 或 FalkorDB)、自己写抽取逻辑。上手成本比 Mem0 高不少。我的建议是:如果你需要双时态能力但不想从零搭,直接用 Zep;如果你要深度定制图结构,再考虑 Graphiti。另外,图数据库的 schema 设计要提前想清楚,实体类型和关系类型定得太随意,后期查询会很痛苦。
5. Letta(MemGPT):把操作系统那套内存管理搬进 Agent
Letta 的前身是 MemGPT,它的思路和前面几个都不一样——不是外挂一个记忆库,而是把 Agent 的记忆管理做成一个分层系统,让 Agent 自己决定什么时候把信息在层之间搬运。这个类比很妙:就像操作系统用内存和硬盘的分层来管理数据,Letta 用上下文窗口和外部存储的分层来管理记忆。
5.1 分层结构:主上下文与外部存储
Letta 把记忆分成几层:
- 核心记忆(Core Memory):常驻上下文窗口的关键信息,比如用户画像、当前任务目标。容量有限,但随时可见。
- 归档记忆(Archival Memory):外部存储的海量信息,需要时通过工具调用检索。
- 召回记忆(Recall Memory):对话历史的完整记录,可搜索。
Agent 通过函数调用在这些层之间搬运信息。比如它可以把"用户提到下周要出差"写进核心记忆,把详细行程写进归档记忆。
5.2 自编辑记忆:Agent 自己管自己
Letta 最有意思的设计是自编辑记忆(self-editing memory)。Agent 可以主动调用工具修改自己的核心记忆,比如追加、替换、删除条目。这意味着记忆管理不是外部逻辑硬编码的,而是 Agent 根据对话自主决策的。
这个设计的好处是灵活——Agent 能根据情境判断什么重要。坏处是不可控——Agent 可能记错、记漏,或者把不该记的记下来。生产环境里,我建议给自编辑加审计日志,记录每次记忆变更,方便排查问题。
5.3 上下文压力下的换页机制
当上下文快满时,Letta 会触发类似操作系统的"换页":把不常用的信息从核心记忆移到归档记忆,腾出空间。这个机制让 Agent 能处理超长对话而不丢关键信息。
5.4 Letta 适合谁
Letta 适合需要 Agent 自主管理记忆、处理超长交互的场景,比如长期陪伴型助手、复杂任务 Agent。它的框架比较重,学习曲线陡,但一旦跑通,能力上限很高。如果你只是想给现有 Agent 加个记忆,Letta 可能过重;如果你在从零设计一个"有自我意识"的 Agent,它值得深入研究。
6. Cognee:把记忆当成数据管道来建
Cognee 的定位和前几个略有不同,它更像一个记忆的数据管道框架,强调从原始数据到结构化记忆的 ETL 流程。它的口号是"几行代码给 Agent 加记忆",但底层做的事不少。
6.1 ECL 管道:抽取-认知-加载
Cognee 的核心是ECL(Extract, Cognify, Load)管道:
- Extract:从各种数据源(文档、对话、API)抽取原始内容。
- Cognify:用 LLM 和规则把原始内容转成实体、关系、摘要,构建知识图。
- Load:把结构化结果加载进图数据库和向量库。
这个流程的好处是可组合、可扩展。你可以往管道里插自定义的处理步骤,比如特定领域的实体识别、敏感信息过滤。
6.2 图与向量的双存储
Cognee 同时维护图存储和向量存储,查询时可以走图遍历也可以走向量检索。这和 Mem0、Graphiti 的思路类似,区别在于 Cognee 更强调数据接入的灵活性——它支持从多种格式和来源构建记忆,适合数据源复杂的场景。
6.3 模块化带来的取舍
Cognee 的模块化是双刃剑。好处是灵活,坏处是配置项多、默认行为不明确。我实测时花了不少时间搞清楚它的默认管道到底做了什么。建议上手时先跑官方示例,把默认流程摸清楚,再逐步替换组件。
6.4 Cognee 的适用场景
Cognee 适合数据源多样、需要构建领域知识库的项目,比如企业知识助手、文档问答 Agent。如果你的记忆来源就是对话,Mem0 或 Zep 更直接;如果你要从一堆 PDF、网页、数据库里构建记忆,Cognee 的管道设计能省不少事。
7. LangMem:把记忆拆成可组合的原语
LangMem 是 LangChain 生态里的记忆工具包,它的定位不是完整方案,而是提供记忆操作的基础原语,让你自己组装。这个思路和前面几个"开箱即用"的项目形成对比。
7.1 三种记忆类型对应三种原语
LangMem 把记忆分成三类,每类有对应的管理工具:
- 语义记忆(Semantic):事实和知识,对应"用户是谁、喜欢什么"。
- 情节记忆(Episodic):经历和事件,对应"上次我们做了什么"。
- 程序记忆(Procedural):技能和规则,对应"遇到 X 该怎么做"。
每类记忆都有写入、检索、更新的原语,你可以按需组合。
7.2 与 LangGraph 的深度集成
LangMem 和 LangGraph 配合得很好,可以把记忆操作嵌进 Agent 的状态图里。比如在某个节点后自动触发记忆写入,在另一个节点前做记忆召回。这种流程化的记忆管理,适合逻辑复杂的 Agent。
7.3 原语方案的代价
原语方案灵活但需要自己搭骨架。你得决定什么时候写、写什么、怎么召回,LangMem 只给你工具不给你策略。对经验丰富的团队这是优势,对新手则是负担。我的建议是:如果你已经在用 LangChain/LangGraph 生态,LangMem 是自然选择;如果是独立项目,先考虑 Mem0 这类完整方案。
8. 六个项目的横向对比与选型建议
讲了这么多,该拉个表横向对比了。选型没有绝对优劣,只有场景匹配。
| 项目 | 核心思路 | 时间能力 | 上手难度 | 最适合的场景 |
|---|---|---|---|---|
| Mem0 | LLM 抽取 + 混合检索 | 弱 | 低 | 用户画像、偏好记忆 |
| Zep | 时序知识图谱 | 强 | 中 | 时间推理、状态演化 |
| Graphiti | 双时态图引擎 | 强 | 高 | 深度定制图记忆 |
| Letta | 分层自管理记忆 | 中 | 高 | 超长交互、自主 Agent |
| Cognee | 数据管道式记忆 | 中 | 中 | 多源知识库构建 |
| LangMem | 记忆操作原语 | 弱 | 中 | LangChain 生态集成 |
8.1 按场景选:三个典型决策路径
路径一:快速给对话助手加记忆。选 Mem0。API 简单,几十行代码跑通,用户偏好类记忆效果稳定。别一上来就追求时序能力,大部分场景用不上。
路径二:需要记住"事情怎么变的"。选 Zep。比如个人助理要回答"我上个月的计划完成得怎样",时序图谱是刚需。如果还要深度定制,用 Graphiti 自己搭。
路径三:Agent 要自主管理超长记忆。选 Letta。它的分层和自编辑机制是为长跑设计的,适合陪伴型或复杂任务 Agent。
8.2 按团队能力选:别高估自己
我见过太多团队选了最"先进"的方案,结果卡在运维上。图数据库、双时态、增量更新这些能力都有代价——运维复杂度、调试难度、团队学习成本。小团队、快速迭代阶段,Mem0 这类轻量方案往往是最优解;等业务跑起来、需求明确了,再迁移到更重的方案也不迟。
8.3 混合方案:不一定只选一个
实际项目里,混用很常见。比如用 Mem0 管用户偏好,用 Zep 管任务时间线,两者通过用户 ID 关联。这种组合能各取所长,代价是架构复杂度上升。我的经验是:先单方案跑通,遇到明确瓶颈再拆,别一开始就设计复杂架构。
9. 落地时最容易翻车的几个细节
选型只是开始,真正决定成败的是落地细节。这一节分享几个我在实际项目里踩过的坑,都是文档里不会写的。
9.1 记忆写入的时机比内容更重要
很多人纠结"记什么",却忽略了"什么时候记"。我的经验是:不要在每轮对话后都触发写入,那样噪音太多、成本太高。更好的策略是在会话结束、任务完成、或检测到关键信息时批量写入。Mem0 支持异步写入,Zep 支持批量摄入,用起来能省不少 token。
9.2 召回数量不是越多越好
召回 top-k 的 k 值,很多人拍脑袋设 10 或 20。实测下来,3 到 5 条往往是最优区间。召回太多,噪音淹没信号,还挤占上下文;召回太少,可能漏掉关键信息。建议根据场景做 A/B 测试,别用默认值。
9.3 记忆的"遗忘"要主动设计
长期记忆不是记得越多越好。用户三个月前随口提的一句偏好,可能早就过时了。系统需要主动遗忘机制——按时间衰减、按重要性、按访问频率清理。Zep 的时间区间闭合是一种遗忘,Mem0 的 DELETE 是一种遗忘,但都需要你配置策略。没有遗忘机制的记忆库,最终会变成垃圾场。
9.4 隐私和隔离是红线
多用户场景下,记忆必须严格按用户隔离。我见过因为查询没加用户过滤,导致 A 用户看到 B 用户记忆的事故。所有记忆操作都要带用户 ID 过滤,这是底线。另外,敏感信息(密码、身份证号)应该在写入前过滤掉,别指望模型自己判断。
注意:记忆系统一旦上线,用户数据会持续累积。提前设计好数据保留策略和删除接口,既是合规要求,也是产品责任。
9.5 评测记忆效果:别只看"记住了没"
评估记忆系统,不能只测"能不能召回"。要分维度测:召回准确率(该召回的召回了没)、召回精确率(召回的是不是都相关)、冲突消解正确率(矛盾信息处理对不对)、时间推理准确率(时序问题答得对不对)。建一套自己的评测集,比看任何 benchmark 都靠谱。
10. 我个人的选型心得
折腾了这几个项目之后,我最大的体会是:记忆系统的复杂度应该匹配业务的真实需求,而不是技术的新鲜度。大部分 Agent 场景,用户要的只是"别让我重复交代背景",Mem0 这种轻量方案就够了。真正需要双时态、时序图谱的场景,其实没那么多。
另一个心得是先跑通闭环再优化。别一上来就纠结抽取粒度、召回算法,先用最简单的方案把"写入-召回"跑通,观察真实数据里的问题,再针对性优化。我见过太多团队在选型阶段纠结几个月,结果业务需求早就变了。
最后,记忆系统是长期演进的东西,不是一次搭好就完事。用户行为会变、数据分布会变、模型能力会变,记忆策略也得跟着调。把它当成一个持续运营的模块,而不是一个一次性交付的功能,心态会好很多。