☰
Agent长期记忆如何选型?六大开源方案深度对比
2026/10/31 3:53:10 网站建设 项目流程

1. 先想清楚 Agent 长期记忆到底难在哪

如果你最近在写 Agent,大概率遇到过这样的尴尬:单轮对话里 Agent 像是把一切都记在心里,但换个会话问它“三天前我们讨论过的那份配置细节”,它只能一脸茫然。这不是提示词写得不好,而是长期记忆的底座没有搭起来。我陆陆续续拆过不少开源项目,得到的结论是:Agent 长期记忆远不是“存进向量库再召回”这么简单,它牵涉到记忆分块、写入时机、更新冲突、遗忘策略这些环环相扣的决策。

先把一个容易混淆的点说清楚:Agent 的长期记忆和人的记忆不完全一样。你现在看到的上下文窗口,本质上是模型注意力的一次快照,窗口再大也有上限,而且塞满无关信息后,真正重要的内容会被稀释掉。把两者类比成工作台和档案柜会很贴切:上下文窗口是工作台,只能放正在处理的东西;长期记忆是档案柜,负责把做完的、没做完的、以后可能还要翻出来的东西分门别类放好。问题是,Agent 不知道哪些东西值得归档,也不知道归档时该保留细节还是保留结论。

1.1 上下文窗口再大,也不是长期记忆

我看到很多初学者踩的第一个坑,是以为把历史对话全部丢进上下文,Agent 就有记忆了。这在小规模演示里没问题,但一旦对话超过十几轮,Token 消耗会迅速上涨,输出延迟也跟着变大。更麻烦的是,模型对超长上下文的注意力并不均匀,真正关键的信息可能淹没在一堆寒暄里。长期记忆的价值不在于“能装下所有历史”,而在于“按需召回该想起的东西”。

举个实际场景:一个客服 Agent,用户第一天说“我下周要搬家,后面地址会换”,第二天说“请把那个订单的发票寄到新地址”。如果不做记忆抽取,系统得把第一天整段对话重新塞进去;如果做了记忆抽取,只需要把“地址变更”这一条事实存下来,第二天按需取回。长期记忆要做的是把对话压缩成可检索、可更新、可复用的知识片段,而不是原样保存。

1.2 一个可靠的记忆系统至少要处理四个生命周期

我一般会把记忆系统拆成四个阶段,用来评估一个项目到底做到位了没有。

  • 写入:什么时候、由谁决定把内容沉淀为记忆。如果每一轮对话都写入,记忆库里全是噪声;如果写入太少,Agent 又会选择性失忆。
  • 检索:给定当前上下文,如何把相关记忆拉回来。这里涉及向量相似度、关键词命中、图谱关系等多种路径。
  • 更新:当新信息与旧记忆冲突时,是追加、覆盖还是保留版本。比如用户说“我喜欢喝美式”,三天后又补充“其实我更爱拿铁”,这时候旧记忆不应该原封不动留在库里。
  • 遗忘:记忆不可能无限增长,需要有淘汰策略,否则长期积累后检索质量会下降,上下文也会被无效记忆撑爆。

这六个项目的区别,本质上是这四个阶段的取舍侧重点不同。Mem0 比较重视“写入和检索”,Letta 更重视“上下文边界内的写入和替换”,Zep / Graphiti 把“更新和时态”做到了极致,LangGraph 把控制权完全交给开发者,CrewAI 和 AutoGen 则在多 Agent 场景里做了不同程度的封装。先把骨架搭起来,后面再看每一家怎么填肉。

2. 六套主流方案,先把“参照系”摆出来

2.1 它们并不是同类竞品,而是不同抽象层

比较这六个项目之前,有一点必须先说清楚:它们不在同一个抽象层次上。Mem0 可以理解成一个“记忆中间件”,你可以在 LangChain、LlamaIndex、CrewAI 里调用它;Letta 更像一个完整的 Agent 运行时,你是在用它的整套上下文管理逻辑;Zep / Graphiti 是偏底层的图谱记忆引擎;LangGraph 是 Agent 编排框架,它提供的是状态持久化能力而不是现成的抽取记忆逻辑;CrewAI 和 AutoGen 则是多 Agent 框架,记忆只是它们内置能力的一部分。

这意味着选型时不能只说“谁的效果好”,还要看你能接受多大接入成本、想在哪个层面控制记忆。如果只是给产品加一个“记住用户偏好”的功能,你大概率不需要自己去写一套 Letta,用一个 Mem0 就能解决。但如果你的 Agent 有大量状态流转、分支、回滚需求,那 LangGraph 的持久化能力反而比记忆库更关键。

2.2 一张表看清各项目的定位

先放一张总览表,后面逐项拆解时会不断引用它。

项目核心设计隐喻主要记忆载体特色适合从哪层接入
Mem0混合检索 + LLM 蒸馏向量库 + 图谱 + 关键词索引提供 ADD、UPDATE、DELETE、GET 等记忆操作通用记忆中间层
Letta(原 MemGPT)操作系统分页式上下文内存 Block + 外部存档Agent 自己决定写哪些记忆,自编辑完整 Agent 运行时
Zep / Graphiti时态知识图谱图数据库 / 图索引带双时间戳的增量图记忆记忆服务 / 图谱引擎
LangGraph图状态机Checkpoint + 持久化 Store状态快照、时间旅行、可回滚Agent 编排框架
CrewAI多 Agent 团队协作向量数据库短期、长期、实体、查询四类记忆多 Agent 应用
AutoGen / AG2可插拔接口文本缓冲 / 向量库等Memory 抽象,后端可替换多 Agent 框架内置

这张表里的“主要记忆载体”是指默认实现,不是唯一实现。比如 Mem0 可选的向量库很多,LangGraph 的 Checkpoint 也能挂 Redis、Postgres,Zep 也可以换图数据库。别被默认设置框住,关键看思路。

3. 六个项目的核心机制逐项拆解

3.1 Mem0:让模型先把对话“蒸馏”成记忆,再混合检索

Mem0 是我个人在项目里用得比较多的一个记忆中间件。它的核心思路不是“把整段对话塞进向量库”,而是让 LLM 先把对话里值得记住的事实抽出来,变成一条条独立的记忆,再存储起来。比如用户说“我周末喜欢去爬山”,它可能生成一条记忆,包含用户的偏好和场景,这样后续检索时不用依赖原始聊天记录。

Mem0 提供的操作很直观:ADD 写入、UPDATE 更新、DELETE 删除、GET 查询。每次写入前,它会把新记忆和旧记忆做比较,如果发现语义高度重叠,会直接更新旧记录而不是新增一条。这能缓解一个非常常见的痛点:记忆库里堆了几百条重复内容,检索出来全是一样的意思。

它的设计巧妙之处在于混合检索。默认情况下不是只跑向量相似度,还会结合关键词匹配和知识图谱关系,最后做一次重排。这跟我们直接用 Embedding 做相似度检索有本质区别:向量检索擅长语义模糊匹配,关键词检索擅长精确命中,图谱擅长带关系推理,三者互补之后,召回效果会稳很多。示例如下:

from mem0 import Memory memory = Memory.from_config({ "llm": {"provider": "openai"}, "vector_store": {"provider": "qdrant"} }) # 写入:LLM 会抽取成结构化记忆 memory.add("I love hiking on weekends", user_id="user_1") # 检索:混合召回 related = memory.search("user's hobbies", user_id="user_1")

实际落地时,有几个细节值得注意。第一,一定要做好 user_id 或 entity_id 隔离,否则两个用户的信息会互相污染。第二,写入前最好设置一个相似度阈值去重,Mem0 的默认逻辑不一定完全符合你的业务。第三,不要把 Mem0 用得过于“傻瓜式”,有些高价值记忆需要你在业务侧手动调用 UPDATE,不能只靠模型自动判断。

适用场景很明确:需要给对话型 Agent 增加个性化记忆、偏好记忆、事实记忆的团队。它的接入成本低,社区活跃,问题排查也相对容易。

3.2 Letta(原 MemGPT):把 Agent 的上下文当“内存页”来换入换出

Letta 的思路是六家里最有“系统感”的。它脱胎于 MemGPT 论文,核心隐喻是操作系统的虚拟内存管理。你手头的内存条就是上下文窗口,装不下所有东西时,把不急着用的内容搬到硬盘,也就是外部存档;等需要时再按页换入。

在 Letta 里,记忆被分成了几个层次。Core Memory 是始终留在上下文里的核心记忆块,比如 persona、human 这类必须长期稳定的信息;Archival Memory 相当于外部档案库,容量大,Agent 可以主动去检索;Recall Memory 则保存历史对话记录,供回溯。最妙的是,记忆的写入和替换是 Agent 自己通过工具调用完成的——它可以在合适的时间点执行记忆查看、替换、搜索之类的操作。

这就和 Mem0 形成鲜明对比。Mem0 是外部记忆系统,由 LLM 抽取记忆但不在 Agent 的上下文管理策略里;Letta 是让 Agent 自己意识到“我现在该记住什么、该忘掉什么”。好处是记忆更新非常灵活,坏处是模型需要多走几步工具调用,延迟和失败率都会上升。如果模型的工具调用能力不强,很容易出现“该记的没记,不该删的删了”。

我实际用下来的感受是,Letta 特别适合角色一致性要求高的场景,比如陪伴型 Agent、长期虚拟角色聊天。它能保持 persona 跨会话稳定,同时不会因为聊天记录太长拖垮上下文。但它的学习曲线确实比别的项目陡,需要把 Memory Block 的格式和更新权限设计清楚,否则 Agent 自己改着改着就把状态改乱了。

3.3 Zep / Graphiti:用双时间轴的知识图谱对抗记忆衰减

Zep 开源的 Graphiti 是我个人认为最“重”但上限也最高的记忆方案。它不把记忆看成一条条文本,而是看成一个不断生长的知识图谱:节点是实体,比如人、公司、订单、项目;边是实体之间的关系和事件。每一轮对话进来,它会增量抽取实体和关系,更新到图里,而不是全部重新计算。

Graphiti 最有辨识度的设计是双时间戳。每条边会记录两个时间:一个是事件发生的业务时间,比如“用户在 3 月 15 日下单”;另一个是信息被系统摄入的时间,比如“这条对话是在 3 月 20 日才被记录进来的”。这两个时间轴分开,最大的价值是能回答“当时是怎么回事”和“我们现在认为事实是什么”的区别。在金融风控、客户生命周期这类场景里,这个能力非常值钱。

举个例子:客户 A 和 B 公司的合作关系在 6 月结束,但系统可能在 7 月才从一段对话里发现这个事实。双时间轴下,你既能查到“合作关系结束于 6 月”这个业务事实,也能知道“系统在 7 月才感知到”。这种时间感知是普通向量库完全做不到的。

代价是基础设施更重。你要部署图谱构建服务、管理实体抽取质量、设计查询子图逻辑。如果你做一个简单问答机器人,Zep / Graphiti 是杀鸡用牛刀;但如果你做企业级知识型 Agent,需要追踪复杂关系和时间线,这个方向值得花时间研究。它的设计思路其实很值得借鉴:即便不用它的开源实现,也可以在自己的记忆系统里加入事件时间和更新时间两个字段。

3.4 LangGraph:把记忆做成图状态的一部分,而不是额外魔法

LangGraph 和前面几个项目不在一个赛道上,它本质上是 Agent 编排框架,但它的持久化机制让长期记忆变成一个非常自然的事。核心是 Checkpoint:图的每个节点执行完,整个状态都会被保存下来。状态里可以放对话摘要、业务数据、用户上下文,下次会话可以恢复到上次执行的位置继续跑。

这个设计最实用的一点是“时间旅行”。因为每一步都有快照,你可以回滚到之前的某个状态,重新调整 Agent 的行为再继续执行。对于生产环境排障来说,这比“记忆库返回了几条相似文本”要可靠得多。因为你能看到完整状态,而不是只看一条被抽取出来的记忆。

另外 LangGraph 还有一个 Store 模块,用来存跨线程、跨会话共享的长期数据。Store 支持 namespace 隔离,比如按用户 ID 或团队 ID 分目录,粒度控制很好。你可以把用户偏好放在 one 用户的 namespace 下,把团队级知识放在 team 的 namespace 下,不同层级互不干扰。

from langgraph.checkpoint.sqlite import SqliteSaver # 每个节点执行后都会保存状态快照 graph = workflow.compile(checkpointer=SqliteSaver.from_conn_string("checkpoints.db"))

LangGraph 的取舍很清晰:不做自动抽取,记忆规则完全由你定。这意味着它的学习和设计成本更高,可定制性也最高。如果你原本就在用 LangGraph 搭 Agent,那直接在状态里加记忆字段,比再引一个外部记忆库要顺滑得多。我的经验是,别让状态图膨胀得太快,定期清理旧的中间状态,否则 Checkpoint 存储会越滚越大,最终拖慢整个图执行。

3.5 CrewAI:多 Agent 团队的四层记忆分工

CrewAI 是另一个面向多 Agent 协作的框架,它的记忆设计很有“团队感”。它不是单一的长期记忆,而是把记忆分成了四类:短期记忆负责当前任务的执行上下文,长期记忆跨会话复用,实体记忆关注文档里的关键人物、组织、地点,查询记忆存储用户过去提出过的查询,用来做偏好分析。

这种分工的价值在于,不同角色在同一个 Crew 里需要访问的记忆并不一样。比如一个研究型 Agent 需要长期记忆中的行业报告摘要,一个执行型 Agent 可能只需要短期记忆里的任务进度。如果所有记忆混在一个池子里,检索时很容易相互干扰。CrewAI 的四类记忆天然做了隔离,从产品角度说,这对多团队协作场景更友好。

启动方式也比较简单,可以在创建 Crew 时打开记忆:

from crewai import Crew crew = Crew( agents=[...], tasks=[...], memory=True, memory_config={ "provider": "chroma", "config": {"collection_name": "my_crew_memory"} } )

但要注意,CrewAI 默认的短期记忆有队列长度限制,任务执行完会逐步淘汰,不是永久保留。长期记忆依赖向量数据库,具体检索效果与 Embedding 模型和 chunk 策略关系很大。如果发现 Agent 在团队协作中把记忆记串了,多半是向量库没有按照角色或任务隔离,官方配置里的 collection_name 值得花点心思设计。

3.6 AutoGen / AG2:用组合式 Memory 接口把后端留给使用者

AutoGen 以及它的持续维护版本 AG2,在记忆上的设计和 LangGraph 类似,都属于“框架内置但策略开放”。它提供的是 Memory 抽象,你可以给一个 Agent 挂上不同类型的记忆对象,比如文本记忆、向量数据库记忆,或者把多个 Memory 组合成一个 CompositeMemory。

这种设计最大的好处是测试方便。你可以在单元测试里用一个简单的文本 Memory,跑通流程后再切换到更重型的向量数据库后端。记忆不跟具体厂商绑定,以后想从 Chroma 换到 Qdrant,只需要改配置,不一定需要改 Agent 业务代码。

和多 Agent 结合时,AutoGen 的思路是让每个 Agent 持有自己的记忆,同时允许跨 Agent 共享某些记忆对象。这比全局一个记忆库要清晰,因为它能模拟团队里不同角色“各自记住各自的东西”这一自然现象。缺点是接口相对底层,如果你需要一个“开箱即用”的高级记忆策略,比如自动摘要、自动遗忘、冲突合并,需要自己封装或者依赖第三方实现。

4. 横向对比:六套方案的关键差异

4.1 记忆生成的自动化程度与污染风险

从自动化程度看,LangGraph 几乎不自动生成记忆,全部由开发者控制;Mem0、CrewAI 依赖 LLM 自动抽取;Letta 也是自动生成,但多了 Agent 主动管理的环节;Zep / Graphiti 依赖实体关系抽取,介于规则和模型之间。自动化程度越高,开发越省心,但记忆污染风险也越高。

我见过不少项目在 Mem0 上只做了加法没做减法,导致记忆库里存了几百条重复、低质、互相矛盾的记忆。反观 LangGraph,状态里每一层变化开发者都能看到,出问题容易回滚,但代价是前期的状态设计要花很多时间。选型时可以问自己一个问题:如果记忆写错了,我需要多长时间发现和修复?这个问题的答案决定了你适合偏自动还是偏可控的方案。

4.2 时间感知与遗忘能力

这六个项目里,时间感知能力差距非常明显。Graphiti 是唯一把“事件时间”和“摄入时间”都纳入模型的项目,它能回答“当时发生了什么”和“我们什么时候才知道”。LangGraph 的 Checkpoint 也有时间属性,你可以回溯状态,但它不对记忆内容做自动时间管理。Mem0 有短期和长期记忆的划分,能处理一部分时间衰减,但它没有细粒度的事件时间轴。Letta 主要通过 Memory Block 的替换和淘汰来“遗忘”,不显式维护时间信息。CrewAI 的短期记忆有队列过期机制,长期记忆则基本靠向量检索。

如果你正在做一个需要严格追溯“事实变化过程”的产品,比如供应链协同、客户关系管理,那么请认真考虑图记忆方案。如果只是聊天偏好,那么普通的短期/长期记忆划分一般就够用了。

4.3 接入成本、可控性、可观测性

方案接入成本可控性可观测性典型成本瓶颈
Mem0低中中LLM 抽取调用、向量库
Letta中高中中服务端部署与上下文管理
Zep / Graphiti高高高图谱构建质量与关系抽取
LangGraph中高高Checkpoint 状态膨胀
CrewAI低低中向量库依赖
AutoGen / AG2中中中自研后端存储

可观测性容易被忽视,但长期记忆恰恰最需要它。一个记忆库如果只能查询,不能解释“为什么这条记忆被召回”,那么调试起来会非常痛苦。好的做法是给每条记忆保留来源、时间戳、更新历史,最好还能回溯到原始对话。Zep / Graphiti 在这一点上做得最完整,Mem0 和 LangGraph 也相对容易扩展,因为它们的结构比较清晰。

5. 落地实践与踩坑记录

5.1 我自己选型的四步法

接触的项目越多,我越觉得选型不应该从工具出发,而应该从“你的 Agent 到底要记住什么”出发。我一般按四步往下梳理。

  • 先梳理记忆类型。把 Agent 需要记住的信息分成几类,比如用户偏好、业务事实、任务进度、对话摘要。不同类别可能对应不同方案,不一定只选一套。
  • 再判断准确性要求。如果记忆出错会导致资损或责任问题,那就必须选可控性强的方案,并且设计人工审核。如果只是个性化推荐,那自动化方案就行。
  • 然后考虑可修正性。长期记忆是会被用户纠正的。比如用户说“我改主意了”,系统必须能更新或删除旧记忆。这时候要注意选择的库是不是支持结构化更新,而不只是追加一条。
  • 最后才选具体组件。进程内做原型用 CrewAI 或 Mem0 很方便,生产环境高并发通常要单独部署记忆服务。

拿客服 Agent 举例,我会用 Mem0 存用户偏好和地址变更,因为这类信息适合按用户维度检索;同时用 LangGraph 做任务流程的状态管理,因为工单流转有明确状态,需要回滚;如果涉及复杂的客户生命周期,再把图记忆引入,用来追踪客户和产品的长期关系。没有一套方案能覆盖所有需求,组合使用反而更合理。

5.2 常见问题与我踩过的坑

下面这个速查表,基本是我在真实项目里遇到的典型问题,每条后面都是血泪教训。

现象可能原因排查 / 解决
记忆串台了,用户 A 的偏好出现在用户 B 的上下文里检索时没有按 user_id 过滤给所有记忆加用户隔离字段,在索引上直接过滤
记忆一直重复,库里全是一模一样的句子写入前没有做查重设置相似度去重阈值,优先 UPDATE 而不是新增
上下文又被塞爆,Agent 反而变笨了每次召回的记忆条数太多限制 top_k,增加 score 阈值,检索后做一次压缩
Agent 自己把重要记忆删了没有做权限分层把核心记忆块设置为只读,删除操作走白名单
图谱查不到关系,节点之间是孤岛事件抽取断点,或实体命名不一致检查输入事件格式,建立实体别名库
长周期运行后,两条记忆互相矛盾没有做版本控制更新时保留旧版本,写入冲突时按时间戳判断
检索结果不够准,向量召回一堆无关文本只用了单一 Embedding 相似度切换 hybrid search,叠加关键词和关系检索

这里我想特别强调一个观念:长期记忆不是“存了就完事”,它需要持续维护。我的做法是在每次重要业务动作完成后,由业务代码主动调用记忆更新接口,而不是完全依赖模型自动抽取。比如用户确认了退货地址,我就在代码里显式写入一条“当前退货地址已变更”的记忆。这样能显著降低记忆污染率。

5.3 一个小技巧:留出“更新钩子”,不要只靠自动检索

自动检索是长期记忆的下半身,上半身是业务侧对记忆的主动写入和更新。我见过很多团队把精力全花在检索调参上,却忽略了在代码里埋更新钩子。结果是库里存了一堆过时信息,再好的检索也救不回来。

一种实际做法是:每当用户明确纠正 Agent 或确认了某个关键信息,就通过记忆 API 执行 UPDATE 或 DELETE。比如用户说“我上次说错了,我平时喝的是拿铁”,这时候应该把旧的“美式”记忆删掉,再写入“拿铁”。如果只是把新对话追加进去,下次检索可能同时召回“美式”和“拿铁”,Agent 就会犯迷糊。

另外,我通常会给记忆加“最后确认时间”之类的业务字段。这样检索结果可以按时间排序,或者设置一个新鲜度阈值。尤其对于用户偏好类记忆,时间越近,可信度越高。不同的记忆内容适用不同的保鲜策略,这个需要结合自己业务摸索。

6. 个人的一点体会

如果你问我长期记忆最重要的是什么,我会说不是存储,也不是检索,而是“让记忆可以被修正”。 Model 抽取出来的记忆一定会有错,而且用户的需求是会变的。一个不可修正、不可删除、不可追溯的来源的记忆系统,使用时间越长,问题越大。

我最早做 Agent 记忆时犯过一个错误,以为把用户的所有历史都存下来,模型就能更懂用户。后来发现事实恰恰相反:记忆越多噪声越大,真正聪明的 Agent 是能在合适的时机想起那条唯一重要的记忆。后来我调整了策略,把记忆系统拆成“结构化事实”和“时间线”两条路:结构化事实给即时检索,时间线给回溯分析。现在回看,这个思路和 Zep / Graphiti 的设计理念是相似的。

最后分享一个小习惯:每次接入新的记忆方案,我都会先在代码里做一次“记忆可观测性演练”,故意写入一条错误记忆,然后模拟用户纠正它,看整个链路是否清晰。如果这个过程要花半天才能调试完成,说明这套方案的可维护性还不够。长期记忆是 Agent 的地基,地基不稳,上层再聪明也白搭。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询