☰
开源Agent记忆系统选型:五大方案对比与生产实践
2026/10/28 13:18:46 网站建设 项目流程

把 Agent 放进生产环境之后,大家最先撞上的墙往往是同一面:记忆。一个没有记忆的 Agent,上一句叫着你名字,下一句就忘了你是谁;上午刚确认过的偏好,下午又拿默认语气问一遍。这种场景在开源社区项目里几乎天天都能看到,也正因为痛点足够痛,开源 Agent 记忆系统这两年涌现出好几条完全不同的技术路线:外挂记忆层、内存操作系统、时序知识图谱、框架内置持久化,以及纯手搓的“向量库加检索管线”。我陆陆续续把 Mem0、Letta(原 MemGPT)、Zep/Graphiti、LangGraph 自带的持久化方案,以及自研轻量组合都跑了一遍,记录了不少实测数据,也踩过一些坑。这篇报告不打算讲太多空对空的架构图,重点放在它们能解决什么问题、不能解决什么问题、生产环境下怎么选。

1. 先搞清楚 Agent 记忆到底难在哪

1.1 上下文窗口替代不了记忆

很多团队的第一个误区,是把记忆和“上下文窗口扩大”划等号。模型上下文从 8K 到 128K 再到 1M,确实能塞进更多历史,但记忆不是“能塞进去”就完事。窗口里的内容会参与每一次 attention 计算,塞得越多,单 token 计算成本越高,响应延迟肉眼可见地变差,而且无关信息还会稀释模型对关键信息的注意力。我在测试 1M 上下文的模型时遇到过很典型的情况:模型能答对夹在 60 万 token 里的事实题,但在对话中需要“主动想起”这个事实时,表现反而远不如把这条事实单独拎出来放到显眼位置。更别说 token 费用了,一位做电商客服 Agent 的朋友算过一笔账,把 80 个商品的促销规则全塞进上下文,一次请求轻松五万 token,按他们的日活量,光 prompt 费用就够养一个后端工程师。

从这个角度看,上下文窗口更接近“鱼缸”,而不该当“图书馆”用。鱼缸再大,能养的水草也有限;真正需要长期保留的东西,应该放到记忆系统这个“档案柜”里,等用的时候再精准取出来。

1.2 先给记忆分个类:工作记忆、情景记忆、语义记忆

做选型之前,我建议先把“记忆”拆成三类再谈,因为它们对存储和检索的要求完全不同。

  • 工作记忆(Working Memory):当前会话正在处理的信息,比如用户刚报出来的订单号、正在生成的文档草稿。特点是生命周期短、状态感强。
  • 情景记忆(Episodic Memory):过去发生过的具体交互片段,比如“昨天用户投诉过物流慢”“上次对话里他提到孩子在读小学”。
  • 语义记忆(Semantic Memory):跨会话稳定的知识与偏好,比如“用户偏好价格优先”“这个客户的合同审批流程需要财务介入”。

这三类记忆对应到人的记忆系统也特别好理解。你去一家常去的餐厅,服务员记得你上周点过什么菜,这是情景记忆;知道你不吃香菜,这是语义记忆;正在确认你们桌上了几道菜,这是工作记忆。技术方案上,工作记忆通常由编排框架的会话状态管理解决;情景记忆适合存成对话摘要或事件记录;语义记忆则最适合沉淀为结构化的事实条目。后面所有开源方案,本质都是在用不同方式处理后两类。

1.3 好的记忆系统要解决四个问题

不管选哪种开源方案,最后都要落到四个基本问题上,这也是我用来评估所有方案的核心框架。

第一是写入:什么东西值得记?用户随口一句“这家的菜一般”和反复强调“每次都来这家店”,对记忆系统来说是两种完全不同的信号。好的写入流程要有抽取、去重、置信度判断,而不是对话里出现“我喜欢”就无脑入库。

第二是存储:记在哪?向量库适合语义相似度检索,图数据库适合关系推理,关系型表适合精确查询。没有一种存储能包打天下,所以成熟方案基本都是混合存储。

第三是检索:怎么在需要的时候把记忆找出来?检索不能只看语义相似度,还得考虑时间衰减、重要度、与当前任务的关联性。一个客户三个月前提到“准备采购”和三天前提到“已经立项”,对销售 Agent 的意义完全不一样。

第四是遗忘:记忆会过时,会冲突,还可能涉及隐私。用户说“我不再喝咖啡了”,系统却仍然记得“用户喜欢咖啡”,这就是冲突;离职员工的画像还在被检索到,这就是没有及时删除。遗忘能力决定了记忆系统的长期可信度。

我把这四点列在前面,是因为后面所有的方案对比,本质上都是这张四问题答卷,只不过每个项目根据自己的理念填了不同的答案。

2. 主流开源方案全景:五条技术路线

2.1 外挂记忆层:Mem0

Mem0 是过去两年增长最猛的记忆类开源项目之一,它把自己定位成 AI 应用的“记忆层”。你不需要改 Agent 的推理逻辑,只需要在对话流里多调用一个记忆服务的 API:发送会话内容,它负责抽取、保存,并在需要时返回相关记忆。

它的核心流程是提取候选记忆,用 LLM 判断新记忆与已有记忆是重复、冲突还是新增,决定 add、update 还是 delete,最后写入向量库并可选地维护一份知识图谱。这个“先判断再写入”的流程,解决了我前面说的“写到什么”和“遗忘”问题,属于真正为长期记忆设计的方案,而不是简单的对话历史存储。

实际体验下来,Mem0 的上手成本确实低,官方提供了 Python/JS SDK,也有可视化 Playground,能直接看到每个用户记忆项的形态。它对底层向量库做了抽象,支持 Qdrant、pgvector、ChromaDB 等,也允许你配置不同的 Embedding 模型。但要注意,Mem0 自部署需要一个 API Server 进程,它更像一个服务而非可以 import 的库;记忆抽取和冲突判断都依赖 LLM 的调用,如果底层模型太弱,写入的噪声会很重,需要增加置信度阈值或人工审核环节。

说实话,如果是做个人助理、文档问答这类需要“跨会话记住用户偏好”的场景,Mem0 是当下最省事的起点。它的问题在于你再往企业级走的时候,多租户、权限、审计这些都要自己包一层,因为它默认解决的是“记忆”本身,而不是“企业记忆合规”。

2.2 内存操作系统派:Letta(原 MemGPT)

Letta 的来头很有意思,它最初叫 MemGPT,提出的核心理念是:把 LLM 的上下文窗口想象成内存 RAM,外部存储像是磁盘,模型需要通过“函数调用”来完成内存的分页、换入换出。这个想法最早是论文形式出现的,后来做成了开源 Agent 框架。

在 Letta 里,每个 Agent 默认有几类记忆块:core memory(包含 persona、human 等小型的、始终在上下文里的块)以及 archival memory(大批量的、按需检索的档案区)。Agent 自己决定什么时候把一段对话摘要写入记忆、什么时候从档案区搜索旧记忆,不需要你在外部编排复杂的存储流程。

这套设计的优势是它把记忆管理内化成了 Agent 的自主行为,适合做“能自己管理长期目标”的研究型 Agent。劣势同样明显:它不是一个可插拔的记忆组件,而是一个完整的 Agent 运行时。你一旦选定 Letta,就同时选定了它的 Agent 循环、工具调用和托管方式。对于已经有自己编排体系的团队来说,引入 Letta 意味着放弃一部分控制权。另外它的学习曲线比 Mem0 陡,第一次上手时如何设计 memory block 的说明文本,会直接影响 Agent 的行为质量,这部分几乎没有官方最佳实践,得自己摸索。

2.3 时序知识图谱派:Zep / Graphiti

Zep 是另一个生产导向的记忆服务开源项目,这几年他们把底层能力开源成了 Graphiti,一个时序知识图谱框架。它跟向量库存的记忆最大的区别在于:Graphiti 里的实体、关系、事件天然带时间戳,比如“用户 A 在 3 月 5 日投诉了物流”,在图上就是带时间的节点和边。

这套设计带来的直接好处是,检索出来的记忆有故事线,而不仅仅是一堆相似片段。你问它“这个客户最近为什么不满”,它能在图谱里沿着“订单延迟 -> 客服不响应 -> 客户投诉”的路径找到因果线索。Graphiti 还支持区间检索,把某个时间窗口内的相关子图整体捞出来,再交给模型生成回答。这在客服、CRM、企业知识库这类需要理解实体关系的场景非常有用。

代价是部署体量明显变大。Graphiti 需要 Neo4j 这类图数据库配合使用,加上向量索引,整套服务的运维复杂度比 Mem0 高一个量级。如果你只是做一个轻量聊天机器人,用 Graphiti 有点杀鸡用牛刀;如果是企业级对话平台,需要理解大量客户关系,它的上限更高。

2.4 框架内置持久化:LangGraph 与 LlamaIndex 的记忆组件

LangGraph 自带 checkpointer 机制,用 thread_id 隔离不同会话,把图的执行状态定期持久化到 Postgres、SQLite 等存储里。它解决的核心问题是“任务的断点恢复”和“同一个用户跨轮次的基础上下文”,但严格说,这只是会话级的持久化,并没有自动完成长期记忆抽取。你需要自己在图里加节点,把对话摘要提炼出来写入外部存储。

LlamaIndex 同样提供了一系列记忆相关的抽象,比如 ChatMemoryBuffer、VectorMemory 等,可以理解为给 RAG 场景准备的记忆模块。它们的好处是跟框架无缝集成,没什么学习成本;坏处是默认能力偏弱,你很少能靠开箱即用的配置,就得到一份高质量长期记忆。

这类框架内置方案的定位,更像是一块基础的“地基”。我的建议是,如果你的编排本来就用 LangGraph,可以先把 checkpointer 用起来,保证会话断点不丢,至于跨会话的长期记忆,再在上面挂一个专门的记忆服务或自研抽取层。

2.5 自研轻量组合:向量库 + Embedding + 检索管线

最后一类,也是很多团队最终会走的路:不用现成记忆系统,自己用向量库加 Embedding 模型加一条抽取检索管线实现。技术栈很标准,Qdrant 或 pgvector 做存储,BGE、text-embedding-3-small 这类模型做向量化,再加一个 LLM 节点定期把对话改写成语义记忆条目。

这样做的好处是百分百可控。你可以完全自定义写入策略,比如“只在用户主动表达偏好时写入”“每周做一次记忆合并压缩”;也可以完全掌控数据归属,毕竟记忆数据通常涉及用户隐私。

代价也很明显:所有坑都是自己的。我在后面会专门给一个精简的代码骨架,但先说结论:自研方案适合用来验证产品,或者有专门基础设施团队的公司长期维护,不适合在人力紧张的项目里临阵磨枪,因为你既要调抽取效果,又要处理并发写入,还要设计召回排序,每一项都不轻松。

3. 多维对比:功能、性能、成本与许可证

3.1 功能维度横向对比

直接上一个我整理的对比表,基于目前各个项目的主线能力,细节以你当时拉到的版本为准。

方案记忆类型自动抽取冲突/更新处理知识图谱会话隔离多租户可视化
Mem0语义记忆有有可选按 user_id需自建有 Playground
Letta工作+情景+语义Agent 自主有限的块更新无Agent 级需自建有 UI
Zep/Graphiti情景+语义有有强按 session/user有有
LangGraph Checkpointer工作/会话状态无无无按 thread_id库级无
自研轻量组合取决于你你自己写你自己写可加你自己写你自己写无

这张表的重点是看清每个方案的“默认边界”。Mem0 默认很聪明,但不替你管企业权限;Letta 默认很自主,但你要接受它的运行时;Graphiti 默认关系理解最强,但部署最重;LangGraph 默认最朴素,但最干净。

3.2 性能与资源消耗:别只看召回 latency

性能这块,我分写入和读取两条链路来谈。

写入路径上,自研组合最快,因为你决定哪些内容要入库;Mem0 多了一步 LLM 抽取和冲突判断,一次写入通常要 1-3 秒,取决于模型速度;Letta 的写入由 Agent 自己触发,不稳定,有时很快,有时 Agent 会反复改写同一个块;Graphiti 因为要更新图结构和时间索引,写入比纯向量库重,尤其是实体关系密集的时候。

读取路径上,向量 top-k 检索本身都很快,几十毫秒级别,真正拉开差距的是“召回后的处理”。如果只是把相似片段拼进 prompt,所有方案都在百毫秒内;如果要做图谱多跳推理或者二次重排,延迟会升到几秒钟。我建议所有做选型的人,先定一个自己的延迟预算,再倒推方案。比如客服场景 2 秒内必须出回复,那就别上“先查图再推理再回答”这种链路,哪怕效果再好。

资源消耗方面,Mem0 和 LangGraph 都比较轻,一个 Postgres 加一个 embedding 服务足够;Letta 需要常驻 Agent Server,内存占用不算小;Graphiti 则是实打实要一台 Neo4j 的机器,生产环境至少给到 4C8G 起步,最好是独立部署。

3.3 集成成本与开发体验

集成成本要从三个角度看:接入底座、SDK 覆盖、上手资料。

Mem0 提供 REST API 和 Python/JS SDK,文档完整,社区案例多,上手最快。Letta 需要你理解它的 Agent 概念,如果你本来就有 LangChain 或自研编排,会有重复造轮子的挫败感。Zep/Graphiti 的 SDK 也不错,但公开案例集中在英文社区,中文资料少,团队需要能直接读源码。LangGraph 如果本身在用,集成成本为零,但别指望它自动生成记忆。自研方案集成成本最低、开发成本最高,需要的是你把一条管线写稳。

从长期维护看,我会更看重“问题定位难度”。用 Mem0 遇到奇怪行为,你能做的通常是调阈值,但内部逻辑是个黑盒;自研方案遇到问题,你可以一步步打印数据流定位。这个权衡没有标准答案,看团队习惯。

3.4 许可证与生产合规

开源许可证是选型里最容易忽略、出事最麻烦的一环。基于我目前对项目仓库 LICENSE 的了解:Mem0、Graphiti、LangGraph、Letta 都是宽松许可或较友好(以仓库实际为准),可以商业集成;Zep 社区版用的是 AGPL 类许可,如果你打算把它作为外部服务的一部分提供出去,需要特别留意 AGPL 对衍生服务和分发的要求,最好先请法务看一眼。所有开源项目版本迭代都很快,许可证也可能调整,我建议把“拉下仓库那一刻的 LICENSE 文件”作为选型评审的一部分固定下来。

另外,如果你的部署环境要求数据不出内网,任何一个云托管版本都要打问号,优先确认自部署能力。这也是我觉得“自研轻量组合”永远不会消失的原因——它天然满足最严格的数据边界要求。

4. 选型建议与避坑要点

4.1 按应用场景选

整理了三条相对清晰的路线,可以直接抄:

  • 个人助理、智能问答、文档机器人:Mem0 最省心。它把“记住用户偏好”这件事做成了标准服务,一个 Playground 就能验证效果。
  • 客服、CRM、营销自动化、企业知识库:优先考虑 Zep/Graphiti。这类场景的痛点不是“记住偏好”,而是“理解客户关系全貌”,时序图谱的因果链路优势非常明显。
  • 复杂多步 Agent 编排、已用 LangGraph:先用 LangGraph checkpointer 保证会话状态,再挂一个自研或 Mem0 的记忆层做长期记忆。

4.2 按团队能力选

团队只有两三个人、没有专职运维,建议先试用托管版本的记忆服务把业务跑通,再评估私有化成本;团队有 RAG 和向量库经验,但不想背上重服务,自研轻量组合反而更快;团队有后端基础设施能力,并且 Agent 产品已经走到商业化阶段,这时候上 Graphiti 这类重方案是值得的,因为后期迁移记忆系统的成本远高于提前规划。

4.3 避坑清单:我踩过的和看别人踩过的

记忆污染是最常见的坑。让模型直接从原始对话里抽取“事实”而没有置信度判断,很快会把闲聊内容当成用户画像写进知识图谱。一个客户随口说“这个功能要是能降价我就买”,结果系统记住了“该用户有强烈购买意向”,销售 Agent 后续的跟进全跑偏了。解法是给写入加阈值、加人工确认,或者让抽取模型只处理明确信号。

记忆冲突处理比新增重要。没有冲突检测的记忆系统,就是一本越写越乱的笔记本。同一个用户今天说“在备选方案里最看重价格”,明天说“其实更看重售后”,如果系统只新增不更新,检索出来就会自相矛盾。好的记忆层必须支持对已有条目的合并、废弃、时间戳标记。

召回不能只看 top-k。单纯靠向量相似度召回容易把三个月前的旧记忆和昨天的强相关记忆混在一起。我自己的做法是检索时把“更新时间衰减”作为一个可配置权重加进排序,重要记忆另给 boost,能显著改善“模型刚好在关键时刻想不起来”的问题。

多租户隔离要提前做。很多开源记忆服务默认没有多租户概念,只在 API 里传一个 user_id 做区分。如果你的产品要服务多个企业客户,数据隔离必须在检索层强制校验,而不是靠“命名空间约定”,否则一次越权访问就够你写事故报告。

最后是隐私与删除。记忆数据往往是从真实对话里抽出来的,里面可能带姓名、电话、地址。除了加密存储,一定要提供按用户粒度的彻底删除接口。真要在生产环境长期跑,我还会加一条每周自动清理“过期且未在使用”记忆的定时任务,保持数据仓库没那么混浊。

4.4 多提一句趋势

我看到的短期趋势是记忆系统会继续向标准化接口演进,读写协议、记忆条目规范会逐渐沉淀下来,未来一个用户可以在不同 Agent 产品之间携带自己的记忆档案。这也是为什么我现在推动团队选型时会优先选外挂式记忆层,而不是把记忆逻辑写死在 Agent 编排代码里。记忆一旦和业务代码耦合太深,后面任何迁移都痛苦。

5. 部署调优与值得复制的经验

5.1 Mem0 自部署的三个关键配置

如果你决定自部署 Mem0,请把注意力放在三个配置上:Embedding 模型、向量存储、提取频率。Embedding 模型选不好,后面所有相似度检索都是沙地上盖楼。我现在生产环境会用本地部署的 embedding 服务,不上云,既控制成本也满足数据边界;向量存储不要用默认的内存模式,至少要落到 pgvector 或 Qdrant。提取频率这里,有一个我踩过的坑:官方默认会把每轮对话都送入记忆提取,但用户经常在闲聊中暴露大量噪音,我后来改成“每 N 轮对话做一次批量提取”,并加上了最小置信度阈值,记忆库干净了很多。

5.2 Letta 的 Memory Block 写法建议

用 Letta 的团队,第一步是把 core memory 的 block 写清楚。一个比较有效的模板是:每个 block 第一段定义职责,第二段说明触发条件,第三段给出具体示例。比如 human block 里写“这是用户的静态档案,更新时间不频繁;当用户明确表达新偏好、纠正之前的信息时应调用 update_memory,请不要把一句闲聊当成偏好写入”。这样 Agent 才知道自己的记忆边界在哪。别写散文,别追求像人话,block 描述本质上是给 Agent 的代码注释。

5.3 自研记忆管线的最小可用骨架

最后给一套我验证过的最小自研骨架,用伪代码表示,重点看流程设计而不是具体 API。

# 写路径:对话结束后异步执行 async def save_memory(user_id, conversation): candidates = await extract_facts(conversation) # LLM 抽取事实,带置信度 for fact in candidates: if fact.confidence < 0.7: # 低置信度丢弃 continue similar = await find_similar(user_id, fact) if similar and await is_conflict(fact, similar): await update_memory(similar.id, fact) # 冲突 -> 更新而非新增 elif not similar: await insert_memory(user_id, fact) # 读路径:组装 prompt 前调用 async def retrieve_memories(user_id, query): hits = await vector_search(user_id, query, top_k=30) hits = await boost_by_recency(hits) # 时间衰减加权 hits = await dedup(hits) # 去重,别把同一事实变体重复塞 return hits[:5] # 控制记忆注入量

这套骨架最值得抄的两个思想:写路径里“冲突就更新”,读路径里“去重和衰减比多召回更重要”。从这套骨架再往上加知识图谱、全局用户画像,只是扩展细节,方向不会错。

我在实际项目里还加了一道“记忆审计日志”,所有写入和更新都留痕。这样一旦发现模型行为异常,可以快速回溯是哪条记忆污染了 prompt,而不是对着整个记忆库瞎猜。这一点在很多开源方案里都不内置,但对生产排障帮助极大。

最后说点个人体会。做 Agent 的朋友容易把记忆系统想得太高端,一上来就恨不得上知识图谱、多路召回。我在踩过几次坑之后的真实感受是:大多数场景先跑通“提取事实、存进表、检索再注入”这条最笨链路,就已经能解决 80% 的“失忆”问题;等用户量上来、对话关系复杂起来,再逐步引入更重的记忆方案也不迟。开源 Agent 记忆系统这一波还很年轻,没有银弹,选型时多花两天对照自己的场景做验证,比事后迁移省下的时间多得多。如果你也在评估这些方案,欢迎把你压测到的数据拿出来一起对比,这种选型信息对所有人都很值钱。

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

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

立即咨询