多轮Agent长期记忆深度拆解:六大主流方案对比与工程避坑指南
如果你搭过真正跑起来的多轮 Agent,多半遇到过这种场景:上下文窗口明明还剩不少,但模型就是忘了用户第一句话说的“我是做茶叶生意的”,转头给你推荐肥料。我花了一个多月时间,把目前比较主流的六个 Agent 记忆项目都翻了一遍源码和文档,又各跑了一些简单场景,把长期记忆到底怎么做这件事彻底理清了。这六个项目分别是 Letta、Mem0、Zep、LangGraph、CrewAI 和 OpenAI 的 Assistants API。
它们都声称能解决长期记忆,但设计思路完全不在一个频道上:有人把上下文窗口当成有限内存来管理,有人把记忆当成结构化事实来抽取,有人坚持“存储原文+知识图谱”,也有人直接告诉你“状态能恢复就算记忆”。这篇文章不只做对比,还会把存储模型、读写时机、遗忘机制、工程成本和落地坑点逐项拆开,给正在做 Agent 开发或者打算给现有 Agent 加记忆层的朋友一份可以直接参考的选型清单。
1. 为什么是这六个项目:长期记忆赛道的六大流派
1.1 六个项目各自代表的路径
先说选型标准。市面上叫得上名的记忆方案不少,但我最终筛出这六个,是因为它们背后的技术路径基本覆盖了当前所有主流方向,你能见到的其他项目,多半是这六类的变体或组合。
Letta(原 MemGPT)走的是“上下文即内存”路线。它把 LLM 有限的上下文窗口类比成操作系统里的内存,再用分页、换入换出的方式,让 Agent 自己管理“哪些信息放眼前、哪些信息挪到后面的档案库”。这个思路很反直觉,但非常贴近模型的工作方式。
Mem0 走的是“抽取式知识库”路线。它不保存对话原文,而是用 LLM 把对话中的实体、事实、偏好拆成一条条结构化记忆。你问它“用户上个月说过什么”,它会直接给结论,而不是甩你一段原始对话。
Zep 走的是“时间线+知识图谱”路线。它先把所有交互记录完整保存,再用后台任务提取实体和关系,构建一个随时间衰减的图谱。这套方案对“谁和谁什么关系”这种查询特别有用。
LangGraph 走的是“状态恢复”路线。它本身不做语义记忆,而是把 Agent 整个执行过程的状态用检查点存下来。任务中途崩了、进程停了,可以从最近一次快照恢复现场。这属于另一种意义上的“记忆”。
CrewAI 走的是“多角色分层档案”路线。它面对的是多 Agent 协作场景,每个 Agent 有自己的短期记忆、长期记忆、实体记忆和上下文记忆,像团队里每个成员都有自己的笔记本。
OpenAI Assistants API 走的是“托管线程”路线。平台把 Thread 当会话容器,所有消息和工具调用自动追加,再配合 File Search 做文件级检索。特点是零运维、闭源、开箱即用。
1.2 用一张表快速对齐记忆边界
把六个项目放在同一张表里看,差别会更清楚。
| 项目 | 思路定位 | 记忆的基本单位 | 开源情况 | 部署形态 |
|---|---|---|---|---|
| Letta | 上下文管理、分页调度 | Memory Block | 开源 | 自托管服务 |
| Mem0 | 抽取式知识库 | Fact、Entity | 开源 | 库/服务 |
| Zep | 时序存储+知识图谱 | Timeline、Entity Graph | 开源 | 自托管服务/云 |
| LangGraph | 状态机+检查点 | State 快照 | 开源 | 库 |
| CrewAI | 多角色分层档案 | 经验、实体、上下文 | 开源 | 库 |
| OpenAI Assistants API | 托管线程 | Thread、Message、File | 闭源 | API |
选型的时候最忌讳拿着一家项目的期待去评价另一家。比如你想让 Agent 记住用户的历史偏好,但选了 LangGraph,最后大概率会失望,因为它保存的是“任务状态”,不是“用户画像”。先对齐这层差异,后面才不会跑偏。
2. 存储模型拆解:六个项目都把记忆写成了什么
长期记忆落到存储层,最关键的是回答三个问题:记忆的最小单元是什么、存放在哪里、如何与上下文交互。这一节逐个拆。
2.1 Letta:Memory Block 与操作系统式分页
Letta 没有把“记忆”抽象成向量库里的一条条文本,而是定义了 Memory Block 这套结构。每个 Agent 可以有一个或多个 block,比如user_info、current_task、archived_notes。Agent 可以通过memory_replace、memory_insert这类工具,像调用系统函数一样主动修改 block 内容。
它还把记忆分成了几个层次。core memory(核心块)始终在上下文里,存的是高频信息,比如用户名字、当前目标;archival memory(档案存储)存放低频但重要的完整背景;recall memory(历史存储)则保存过去的对话记录。Agent 需要某个背景时,会通过搜索工具去档案库里找,而不是把所有内容都堆在眼前。
我用了一个比喻才真正说服自己:普通方案是给 AI 一间无限大的仓库,但 AI 只会用脚尖前 30 厘米的位置;Letta 是给 AI 一个书架加一张索引卡,它知道自己要的书在第几排,需要时才过去拿。代价是模型偶尔会“忘记去查”,所以 prompt 里必须写清楚“当用户提到过往项目时,必须搜索档案库”。
2.2 Mem0:事实与实体,而不是原始信息
Mem0 的核心单位是记忆条目,每条都是一段可独立使用、带作用域的陈述句。比如用户说“我上个月刚从营销岗转到产品岗”,Mem0 不会保存这句话的原文,而是抽取成“用户当前岗位是产品经理,2025 年 X 月从营销岗转入”。这样后续检索时,命中率比搜原文高得多。
存储层上,Mem0 支持接向量数据库、图数据库等不同后端,同时强制用user_id和agent_id做作用域隔离。这一点说起来简单,做起来非常关键。我见过有团队把多用户对话全塞进同一个 collection,结果用户 A 的隐私信息被检索进了用户 B 的会话,属于上线前必须排查的硬伤。
抽取依赖 LLM,这是 Mem0 的成本重心。每轮对话通常要调一次抽取请求,质量也直接受模型能力影响。如果对话本身表达模糊,抽取器很容易把模型猜测的内容当成事实存进去。所以生产环境一定要给记忆条目留人工编辑入口,而不是全自动信任抽取结果。
2.3 Zep:完整时间线加上时序知识图谱
Zep 的设计和 Mem0 有本质区别。它先把所有对话消息完整保存为 narrative timeline,后台再异步抽取实体、实体属性、实体之间的关系,构建一张时序知识图谱。因为这张图带时间戳,所以系统能感知“用户上周提到过供应商 A,这周又提到小李负责对接供应商 A”。
这种结构最大的优势是关系型查询。比如用户问“上次说让我联系谁要报价”,纯向量检索很难把“小李”“供应商 A”“报价”这三个词准确关联起来,但图谱可以沿着关系路径找到答案。如果只做关键词召回,这类问题基本会答偏。
不过要说得实在一点:Zep 的完整形态部署成本不低,通常要起一个后端服务,配向量数据库,开图数据库功能时还要额外维护图库节点。如果项目很小,用它可能属于杀鸡用牛刀。官方也提供了只做历史存储和摘要的轻量模式,但那样的话它跟普通历史存储方案的区别就没那么大了。
2.4 LangGraph:State 快照和检查点
LangGraph 的“记忆”不是记忆库,而是 State。你可以把 Agent 流程定义成一张图,State 是图里流动的字典,节点不断读写 State。加上 Checkpointer 之后,每执行一步,整份 State 都会被持久化到 SQLite、Postgres 这类存储里。
恢复方式也很有意思:不是把旧状态检索出来,而是直接整体加载到当前执行上下文。用户继续对话时,代码只要传入同一个thread_id,图就能从上次中断的位置继续跑。对长时间多步骤任务来说,这种设计非常可靠。
但你要清楚它的边界。LangGraph 不负责“从历史对话中提炼用户偏好”,也不负责“总结出跨会话的经验”。它更像高速公路上的行车记录仪,能还原现场,但不能替你做思考复盘。所以更准确的说法是:它是一个强状态持久化方案,而不是语义记忆方案。
2.5 CrewAI:把记忆按角色拆开
CrewAI 面对多 Agent 协作,按角色把记忆拆成了四类。短期记忆存当前任务执行过程中的中间信息,任务结束就清掉;长期记忆从已完成任务的结果里提炼经验,比如“这类客户对性能比价格更敏感”;实体记忆维护人、组织、产品这类档案;上下文记忆保存会话交互历史,维持连贯性。
这套分层很符合现实团队的情理:团队不会把所有信息都塞进一个公共大脑,而是每个人有自己的工作笔记、客户档案和会议记录。在协同办公、销售助手、客服分诊这类场景里,CrewAI 的模型很直观。
坑也在于“渠道多但质量难控”。长期记忆如果真的依赖 Agent 自动提炼,执行结果质量差,提炼出的经验就会跟着差。而且多 Agent 之间如果共享某些记忆库,一定得想清楚谁能读、谁能写,否则容易出现角色越权读到不该知道的信息。
2.6 OpenAI Assistants:Thread 和文件检索
OpenAI 的方案把 Thread 当作会话记忆容器。用户消息、助手回复、工具调用参数和输出结果,都会作为消息对象追加到 Thread。平台自动持久化,不需要开发者管存储。它的设计重点是“会话内连续”,而不是“跨会话抽取”。
要做跨会话知识,它提供的是 File Search 工具。你先创建 Vector Store,传文档进去,运行时 API 自动检索相关内容并注入上下文。这非常适合把产品手册、FAQ、历史工单作为外部知识源挂给 Agent。
优点是前期开发效率极高,尤其适合不想维护后端存储的团队。缺点是黑盒,你很难干预记忆的抽取和遗忘逻辑,也做不了深度定制。如果你追求长期沉淀一套自有的记忆体系,它只能当临时方案或作为补充通道。
3. 读写时机与调度:什么时候记、怎么取、怎么忘
存储模型解决“记什么”,读写调度解决“什么时候记、怎么取、怎么忘”。这层差异会直接反映在用户体验上。
3.1 写入机制:主动写入、异步抽取、自动保存
我把六个项目的写入方式分成三类。
主动写入型以 Letta 为代表。Agent 根据系统提示中的规则,自己决定是否调用记忆更新工具。优点是灵活,模型造出来的记忆更贴合上下文;缺点是依赖模型自觉,长对话里模型很容易“忘记写记忆”这个动作。
异步抽取型以 Mem0 和 Zep 为代表。对话结束后,由后台任务走抽取管道,把该提炼的提炼出来。优点是主流程不增加延迟;缺点是有时滞。用户刚说完一句重要信息,紧接着下一轮就问“你记住了吗”,很可能检索不到,因为异步任务还没跑完。
自动保存型以 LangGraph、CrewAI 和 OpenAI 为代表。LangGraph 每步自动保存状态快照,CrewAI 在任务结束后自动写入长期记忆,OpenAI 干脆在消息产生的那一刻就追加进 Thread。优点是不会遗漏;缺点是存下来的东西未必是“有效理解”,真正需要时可能召不回重点。
一句话总结写入差异:主动写入考验模型自觉,异步抽取考验任务时序,自动保存考验存储上限。
3.2 读取机制:指针访问、查询注入、状态恢复
读取方式决定了记忆如何进入模型的视野。
Letta 走的是指针访问:系统提示里只放 memory block 的概要,模型看到 block 名后,再按需调用搜索工具读取细节。优点是省 token,缺点是模型得过一道“想到要查”的关卡。
Mem0 和 Zep 走查询注入:每一轮用户问题进来,系统先对问题生成向量,从记忆库里检索 top-k 相关片段,再拼进 system prompt。优点是简单直接;缺点是受检索引擎质量影响很大,查不准就把一堆无关信息塞进上下文。
LangGraph 是状态恢复:它并非“检索”记忆,而是直接把上次保存的 State 整个搬回来,Agent 从断点继续跑。对恢复执行最有效,但你没法拿它做“用户上个月说过什么”的语义查询。
CrewAI 和 OpenAI 介于两者之间。CrewAI 会在生成前注入相关实体档案与 top-k 记忆片段,OpenAI 则是 Thread 天然携带上下文,文件检索按需挂在工具调用里。
3.3 遗忘机制:最容易被忽略,也最能体现设计功力
只记不倒是最常见的建模失误。长期记忆若只看增量不看淘汰,跑几个月后库里会堆满过时、矛盾、冗余的内容。
Letta 用 sleep-time compute,在 Agent 空闲时让模型复盘现有记忆,自己合并、更新、删除旧块。这是主动式遗忘。
Mem0 在抽取阶段过滤低信号信息,用冲突处理机制避免重复和矛盾。这是入口式遗忘。
Zep 有时序衰减,太久没被提到的实体或关系,检索权重会自然下降,慢慢不被召回。这是老化式遗忘。
LangGraph 没有遗忘概念,State 快照只会保留、不会清洗。作为任务现场记录没问题,但做知识管理会越来越臃肿。
CrewAI 长期记忆容量有限,超过阈值按评分淘汰。这是容量式淘汰。
OpenAI 的 Thread 受上下文窗口限制,超出后要么截断要么做摘要压缩;File Search 里的文件默认不删,需要手动管理。从记忆演进的角度看,OpenAI 这层最弱,适合轻场景。
很多 Agent 刚上线表现不错,跑了一个多月反而变笨,大概率不是模型退化,而是记忆库里的旧信息污染了判断。所以在做架构时,遗忘机制和写入机制要同时设计,不能等库存爆了再补。
4. 工程横评:成本、延迟、可控性与观测性
第四部分聊落地时会遇到的工程差异。这部分主要基于我实际跑通各项目后记录的运维体验,都是具体数据以外的经验反馈。
4.1 部署形态和依赖成本
| 项目 | 部署形态 | 额外服务依赖 | 启动成本 |
|---|---|---|---|
| Letta | 自托管服务 | 持久化数据库、向量库、LLM API | 中 |
| Mem0 | Python/JS 库 | 自选向量库/图库,LLM API | 低 |
| Zep | 自托管服务/云 | 后端服务、向量库、图数据库(可选) | 高 |
| LangGraph | Python/JS 库 | Checkpointer(SQLite/Postgres) | 低 |
| CrewAI | Python 库 | 向量库默认用 Chroma | 低 |
| OpenAI Assistants API | 闭源 API | 无 | 零 |
如果团队运维能力有限,LangGraph、Mem0、CrewAI 这类库级方案更容易起步。Zep 的完整图谱模式对资源要求最高,我测试时光是起图数据库加向量库就在 Docker 里折腾了小半天,功能确实强,但要算清楚这成本能不能换来对应的业务价值。
4.2 延迟影响和失败模式
读写延迟角度,我做一个粗略划分:
- Letta:读路径多一次工具调用,如果模型判断失误去搜了无关档案,会拖慢响应;写路径也是显式工具,延迟可控。
- Mem0:读路径是向量检索+注入,通常毫秒级;写路径异步,但如果同步调用抽取接口,单轮耗时可能增加 500ms 到 2 秒。
- Zep:读路径会访问图谱和时间线摘要,延迟高于纯向量检索;写路径完全异步,后台消化。
- LangGraph:读就是恢复 State,用 SQLite 时几乎无网络开销;写是每步落盘,量级很轻。
- CrewAI:读是向量 top-k 注入,毫秒级;写是任务结束后异步流程。
- OpenAI Assistants:读写都由平台托管,延迟主要取决于 File Search 命中的文件量,通常可控。
实际工程里,比延迟更值得关注的是“记忆一致性”。以 Mem0 为例,异步抽取机制虽然不阻塞主流程,但用户刚说完信息立刻追问时,可能查不到刚写入的内容。如果在强一致场景下,最好把关键记忆的写入改成同步,或者给前端一个“记忆更新成功”的反馈信号。
4.3 可定制性与可观测性
可定制性上,开源库明显占优。Letta、Mem0、Zep、LangGraph、CrewAI 都允许替换存储后端、修改抽取 prompt、插自定义检索逻辑。OpenAI 是纯黑盒。
可观测性参差不齐。我实际用下来,Mem0 和 Letta 的记忆条目可读性做得比较好,能看当前 Agent 记住了哪些事实;CrewAI 有实体档案可视化;Zep 可以通过 API 查询实体图,但排查问题时要学一套查询语法;LangGraph 强于状态回放,弱于“解释为什么”;OpenAI 则要手动拉 Thread 消息列表才能复盘。
给团队选型时,我会建议把“能否回答:当前 Agent 记得什么,改过什么,为什么改”作为选型门槛。如果一套记忆系统做不到这三点,出问题后排查成本会高到怀疑人生。
5. 场景化选型:不同 Agent 形态该选哪套记忆方案
可能有人已经等着要结论:到底该用哪个?我的回答是:看你的 Agent 是什么样的。这里按最常见的几类场景给出选型建议。
5.1 典型场景与对应建议
| 场景形态 | 推荐起点 | 理由 |
|---|---|---|
| 个人助理、深度闲聊、需要长期人格设定 | Letta | 分页和 memory block 能兼顾个性与成本 |
| 客服系统、CRM 辅助、记住客户偏好 | Mem0 或 Zep | 事实抽取和实体画像最合适 |
| 关系推理类问题(谁和谁对接) | Zep | 知识图谱擅长关系路径 |
| 长耗时的业务 Agent、需要断点续跑 | LangGraph | 检查点机制天然适合任务恢复 |
| 多 Agent 协作团队,每个角色各有档案 | CrewAI | 四类记忆分层贴近团队结构 |
| 快速落地、不想维护后端存储 | OpenAI Assistants API | 零运维、开箱即用 |
如果你只想给现有 Agent 快速加一层“记得住用户偏好”的能力,Mem0 是最轻的起点;如果你同时要处理复杂关系和多轮协作,可以先用 CrewAI 搭骨架,再外挂 Zep 做关系查询。
5.2 组合使用才是常态
实际上,生产环境很少有人只用单一方案。比较典型的架构是:用 LangGraph 做主流程编排和状态持久化,在其中一个节点上用 Mem0 做用户偏好检索;或者用 CrewAI 管理多 Agent 协同,但把实体记忆库替换成 Zep 的图谱服务。
组合时要格外注意接口隔离。最好在业务代码层抽象一个MemoryService,内部封装不同的 provider,业务侧不直接依赖某个厂商的 storage API。这样即使后面换供应商,也不需要重写业务逻辑。
我见过一个失败案例:团队上了 LangGraph 之后,以为它自带长期记忆,结果用户回来问“你还记得我上个月说过的项目预算吗”,模型完全答不上来。因为 LangGraph 保存的是任务状态,不是语义知识。后来补了一层 Mem0 做事实抽取,问题才解决。这更加印证了“理解项目解决什么问题”比跟风选型重要得多。
6. 落地排坑:四个最容易被忽视的实战问题
记忆系统看着简单,实际掉进去才知道水深。下面这四个坑我基本都踩过,写出来给各位当警示。
6.1 记忆污染比没有记忆更可怕
记忆一旦错了,Agent 会在后续每一轮对话中持续用错误信息作答,而且比没有记忆错得更隐蔽。比如抽取器把“客户说再降价 5% 可以继续聊”存成了“客户已接受当前价格”,后续报价细节全乱。
解决思路:给记忆条目加置信度、来源消息 ID 和更新时间;关键记忆必须支持人工编辑,保留“删除/修改”的入口。宁可少存几条,也不能让系统对内错账深信不疑。
6.2 作用域隔离没做好会串记忆
多用户系统里,忘记按user_id过滤记忆库会出现严重的数据安全事故。Mem0 的存储接口基本都要求传用户标识,CrewAI 也支持按 Agent 区分,但如果你自己接向量库,必须把隔离逻辑放在检索路径的最前面,而不仅存在于写入时。
我在项目里固定画三层隔离:用户级记忆(user-level)、Agent 级记忆(agent-level)、全局知识库(global)。任何记忆请求都要先确定落在哪一层,再决定如何检索。
6.3 记忆注入太多,反而挤掉关键信息
有人把长期记忆做成“每轮把全部摘要塞进提示”,很快上下文窗口就被吃光,模型注意力被稀释,连当前指令都容易执行偏差。我一般把注入量控制在 5 条左右,且根据当前任务类型筛选类别。比如报价场景优先注入价格偏好,而不是用户上周的食谱。
这本质上是个取舍问题。记忆注入和上下文预算要算总账,不能只关注“我们存下了多少”,还要关注“模型真正看到了多少”。
6.4 记忆漂移是隐藏最深的坑
用 LLM 抽取记忆时,模型可能把两条相近事实合并时“漂移”成错误事实,而且过程完全不可见。比如“用户常喝拿铁”和“用户最近试过美式”,被压缩成“用户喜欢美式咖啡”。这类错误在后台自动演化,很难发现。
应对方法是保留原始信息,提高追溯能力。每次抽取都记录来源消息和时间戳,合并操作留版本记录。排查时做到“能回放一条记忆是怎么一步步演变的”,才能定位是抽取提示词问题还是信息冲突问题。
7. 不想上重系统的话,自建一个最小长期记忆方案
最后给想快速起步的团队一个底线方案。如果不想引入完整服务,可以先做一个不到 200 行的最小系统,核心思路是“抽取事实、向量存储、检索注入”。
伪代码逻辑类似这样:
async def extract_and_save(user_id, assistant_id, user_text, assistant_text): facts = await llm.extract_facts(user_text + assistant_text) for fact in facts: vec = await embed(fact["text"]) store.add( user_id=user_id, assistant_id=assistant_id, text=fact["text"], embedding=vec, metadata={"source": "history", "time": now()} ) async def recall(user_id, query): vec = await embed(query) items = store.search(user_id=user_id, embedding=vec, top_k=5) return "\n".join(f"- {item.text}" for item in items)这个方案能快速跑通,但缺了遗忘、更新、合并和冲突处理,上线一段时间后会出现重复事实、陈旧事实共存的问题。所以从一开始就要给每条记忆加created_at、updated_at、replaced_by字段,再写一个简单“同主题更新”逻辑:命中同一实体时,新事实覆盖旧结论。
我在实际项目里还会在系统提示里固定留一个user_profile块,让 Agent 自己维护,效果比全自动抽取更容易控制。原因是模型自己维护时,它知道自己正在改什么,不会像后台批量抽取那样脱离上下文盲目合并。等这层跑顺了,再考虑上图谱或者分页方案也不急。
这些项目前前后后跑了差不多一个月,最大的体会是:长期记忆设计的第一要义,是精确回答“这段记忆要服务于哪个决策”。如果只是想留底,LangGraph 的检查点就够;如果想让它下次开口时能“想起你上周提过的那件事”,还是要走抽取式方案,而不是简单加一个向量库。最后再啰嗦一句:在系统提示里固定一块可编辑的用户画像,让模型每轮结束前检查一次,比任何复杂调度都更容易提升体验。