过去半年,我一直在做一个偏长期的 AI Agent 项目,被同一个问题反复卡住:对话轮数一过五十,Context 里的有效信息就开始“蒸发”。最早想靠更大的上下文窗口扛过去,从 8K 换到 128K,结果只是把问题推后了几十轮,token 成本却翻了几倍。后来我认真调研了“开源 Agent 记忆系统”这个方向,才发现这里面的方案远比想象中多,而且每种方案背后的取舍差异很大。这篇文章就是我基于项目实测和社区资料整理的一份多维对比报告,覆盖六个有代表性的开源项目,用存储、检索、上下文管理、集成成本四个维度做横向拆解。
1. 为什么 Agent 记忆系统突然成了刚需
1.1 上下文窗口再大,也装不下一个真正的工作流
先讲一个反直觉的结论:模型上下文窗口的扩增,并没有解决 Agent 的“失忆”问题,反而把问题藏得更深了。当 Agent 只是单轮问答时,上下文窗口非常够用;但 Agent 一旦走向多步自主执行,情况就完全不同了。一个客服 Agent 要记住用户的历史订单、退款偏好、上次沟通的结论;一个编程 Agent 要记住几十份文件的修改背景和测试结果;一个研究型 Agent 要连续一周多持续回溯前几天的分析过程。这些内容本质上属于“记忆”,不是“上下文”。继续靠截断和拼接历史记录,天然就有上限。
我们早期在 LangChain 里用最原始的方式硬扛,把整轮对话塞进 Memory,很快发现两个问题。第一,Token 消耗呈指数级增长,长任务跑到一半,一次请求的消耗就顶得上之前一天的费用。第二,模型容易被无关历史带偏——它分不清哪条历史对当前决策是关键的,于是把大量噪声一起纳入推理。这个阶段我意识到,Agent 工程里缺的不是更大的窗口,而是一套能自动决定“什么值得记、什么时候取、什么时候忘”的机制。
1.2 记忆不是缓存,而是独立的基础设施层
我后来形成一个判断:记忆应该被设计成独立组件,而不是靠 Prompt 里手动拼接。所谓开源 Agent 记忆系统,做的事情是把对话历史、用户画像、实体关系、任务状态等信息,用一套可写入、可查询、可更新、可淘汰的机制管理起来,在合适时机重新注入上下文。它至少包含三层能力:写入(从交互中抽取值得记住的事实)、存储(用什么引擎持久化)、召回(在什么场景下取出什么内容)。
这里必须区分一个容易混淆的概念:记忆系统不等于 RAG,也不等于向量数据库。向量库解决的是“语义检索”,记忆系统还需要回答三个额外问题:哪些信息值得记?信息如何随时间更新?信息冲突时怎么办?后面评估每个项目时,我重点就是看这三件事谁做得更好。另外,记忆在 Agent 工程里也分层级:短期工作记忆(当前任务的上下文)、情景记忆(历史事件)、语义记忆(关于用户和世界的持久事实)、程序性记忆(如何调用工具、如何完成任务)。不同开源系统对这些层级的覆盖天差地别,这是选型时最容易忽略的盲区。
2. 先认识六个能打的开源记忆系统
我挑项目有几个标准:GitHub 上活跃度不能太低、有明确的记忆抽象设计、在社区里有真实用户讨论。最后留下六个有代表性的:Mem0、Letta(原 MemGPT)、Zep/Graphiti、LangChain 记忆模块、Cognee 和 Memary。它们并不是同一个物种——有的是独立记忆层,有的是 Agent 框架内置记忆,有的是知识图谱引擎。放在一起对比,是为了看清楚不同技术路线的取舍。
2.1 Mem0:把记忆变成 ADD/UPDATE/DELETE/SEARCH 四个动词
Mem0(mem0ai/mem0)是社区里增长非常快的项目,核心抽象非常直观:把记忆操作变成四个动词——add、get、update、delete,再加一个 search。你可以显式写入一条记忆,比如“用户喜欢键盘快捷键操作”,也可以让模型从对话中自动抽取事实,再定期去重、纠错、更新。底层用向量库做相似度召回,用图数据库做实体关联,两个通道的结果会经过重排。
让我印象最深的是它的分层设计:记忆按 User、Agent、Session 三个维度组织,这意味着你能给不同用户维护互相隔离的记忆空间,也能在同一个用户下区分不同会话的上下文。这个设计直接对应业务中的多用户隔离需求,接入 LangChain、LlamaIndex、CrewAI、AutoGen 都有现成封装,还有 Python 和 JavaScript 两套 SDK。API 很克制,新手按文档跑一遍 demo 就能上手,适合作为通用记忆底座。
2.2 Letta:用操作系统的分页思路替模型管理上下文
Letta 的前身是 MemGPT,我把这个项目单独看成一个流派。它的核心隐喻是把 LLM 的上下文窗口比作内存 RAM,外部存储比作磁盘。系统内置一组工具函数:core_memory_append、archival_memory_insert、conversation_search。当上下文快满时,Agent 会自动触发一次“睡眠整理”,把低价值的信息归档到档案存储,把值得长期保留的信息写回核心记忆。从效果上看,是“操作系统在替模型管理内存”。
Letta 本质上是一个完整的 Agent 运行时,自带 Python SDK、REST API 和一个桌面客户端。你要部署的是自己的服务,而不是“导入一个 Python 库”。这带来更高的运维成本,但场景只要是有“一个 Agent 连续跑几周、重启后要恢复状态”的需求,这套设计就很有价值。和 Mem0 相比,Letta 的定位不是记忆数据库,而是把记忆和上下文管理整体打包的 Agent 框架。
2.3 Zep:时序知识图谱驱动的长期记忆服务
Zep 走的是另一条路线:把 Agent 的长期记忆建模成“带时间戳的知识图谱”。它背后的 Graphiti 框架会自动从消息中抽取实体、关系和事实,每条知识都带上发生时间,因此能回答“用户上周提过什么需求”这类时间敏感问题。召回时同时跑语义搜索和图遍历,再把结果合并去重。
Zep 的强项是高频、时间敏感的对话场景。我之前在客服场景里实测,当用户问“上次你说的那个优惠券还能用吗”,Zep 通过图结构能回溯到优惠券对应的完整事件链,准确率明显高于纯向量召回。代价是所有信息都要先经过实体抽取和关系构建,抽取质量直接影响图谱质量。它提供开源社区版自托管,也有商业云 API。
2.4 LangChain 记忆模块:最轻量,也最容易被误用
LangChain 自带的 Memory 类(ConversationBufferMemory、ConversationSummaryMemory、VectorStoreRetrieverMemory、EntityMemory 等),本质上是“会话状态管理工具”,不能算完整的记忆基础设施。ConversationSummaryMemory 会把历史用 LLM 压缩成摘要,适合短周期任务;VectorStoreRetrieverMemory 把历史片段写入向量库,按语义召回。新版本转向 RunnableWithMessageHistory,更接近消息持久化。
把它列入对比,是因为很多团队第一版方案就是“用 LangChain 记忆模块顶一下”,我们也是。这里有两个大坑:摘要不是增量更新,每次都要重算,Token 消耗很惊人;向量召回的历史没有实体关联,无法处理“用户上次说的 A 和这次说的 B 其实是同一件事”。所以我把这套方案定位为对照组——适合 demo,不适合生产级长期 Agent。
2.5 Cognee 和 Memary:知识图谱路线上的另外两个样本
Cognee 对自己的定位是 data memory,也就是“数据记忆”。它会把文档、对话这些非结构化数据抽取成知识图谱和向量索引,再基于图谱做记忆查询和推理。它更适合“知识库 + Agent”的场景,回答问题时能引用图谱中的推理链路,而不只是向量相似度命中的片段。
Memary 是更早期的知识图谱记忆原型,设计思路是把用户画像保存下来,再把对话中的实体关系转成图谱。项目活跃度不高,作为生产底座风险较大,但代码量不大,适合想理解“知识图谱记忆到底怎么运作”的人去读源码。
3. 存储后端对比:不同引擎决定了记忆系统的上限
3.1 向量库:语义相似的召回,但不会治理信息冲突
几乎所有记忆系统的底层都离不开向量检索。Mem0 的默认实现接 Qdrant,LangChain 记忆模块可以接任意向量库,Zep 的语义检索层也基于向量索引。向量库擅长语义相似度计算,但有两个天然短板。
第一,它不知道实体之间的结构化关系。两条记录“张三买了 iPhone”和“李四买了 iPhone”在向量空间中可能高度相似,对业务来说却是完全不同的记忆。如果不加用户 ID 之类的元数据过滤,召回时就会串记忆。第二,它不擅长处理时间线和冲突。系统里同时存在“张三喜欢 A 产品”和“张三已经改用 B 产品”两条记录,向量库能搜到,但不会告诉你哪条更旧、应该被覆盖。
3.2 图数据库:实体关系和时间维度,服务端更复杂
图数据库(Neo4j、FalkorDB 等)擅长记录“谁和谁关联”“过去三个月发生了什么关系变化”。Zep 的 Graphiti 就走这条路,Mem0 也把图存储作为选项。图谱的代价是写入链路极其复杂:一条消息进来,要先做实体识别、关系抽取、实体消歧,再决定是新增节点还是更新已有关系。抽取质量决定了图谱质量,如果实体识别不稳定,图谱里会出现大量别名节点,关系也严重冗余,查询时反而更慢更不准。
图谱路线的记忆系统,短期跑起来效果不一定比向量库好,但跑得越久、知识越密集,优势越明显。比如客服场景下,一个老客户半年内产生了大量订单、退款、投诉记录,向量库只能按“相似片段”召回,图数据库却能沿着实体关系把整个事件链拉出来。这就是“记起来像什么”和“记起来是谁、和谁有关系、什么时候发生的”之间的本质差异。
3.3 混合存储:这几乎成了成熟记忆系统的标配
我观察到一个明显趋势:上线久了、维护好的记忆系统,最后都走向“向量 + 图谱”混合存储,而不是二选一。Mem0 用 Qdrant 做向量召回、用图存储做实体关联;Zep 用向量做语义检索、用 Graphiti 做时序图谱。设计动机一致:向量负责模糊语义召回,图谱负责精确的实体关系和时间线。
混合存储的工程复杂度是真实存在的。写入时要同时更新两套索引,查询时要合并结果并去重,删除时要同步清理两侧数据。选型时不能只看“这个系统用了什么存储层”,还要看它怎么处理两套索引的一致性。有些项目只是把向量库和图数据库堆在一起,查询时各查各的再拼接,效果和真正的融合检索差一个量级。
| 系统 | 默认存储 | 图谱支持 | 时间维度 | 典型部署 |
|---|---|---|---|---|
| Mem0 | Qdrant + 图数据库 | 有 | 弱 | Docker / SaaS |
| Letta | SQLite / Postgres + 向量索引 | 无 | 可回溯 | Docker 自托管 |
| Zep | Graphiti + 向量索引 | 强 | 强 | 自托管 / 云 |
| LangChain Memory | 内存 / 任意向量库 | 可选 | 弱 | 内嵌代码库 |
| Cognee | 图谱 + 向量 | 强 | 中 | Docker |
| Memary | Neo4j | 强 | 弱 | 本地 |
4. 检索与更新对比:好记忆不只看召回率
4.1 纯向量检索的误区:相关性不等于重要性
检索环节最常见的误区是“语义相似度高的历史就值得放回上下文”。对话里的寒暄、临时约束和真正影响决策的事实,向量相似度都可能很高。把一堆高相似度高噪声的历史一起塞回去,反而稀释了有效信息,让模型更困惑。
好一点的记忆系统会在召回后加重排逻辑,把“时间衰减”作为权重之一。比如用户三个月前买的手机型号,和昨天提到的新需求,如果按纯相似度检索,旧记录的得分可能照样很高;但按“越新越重要”的加权方式,旧记录应该被降权或剔除。Mem0 的 search 接口就内置了这层聚合与重排逻辑,而不是简单返回 Top-K。LangChain 的 VectorStoreRetrieverMemory 则基本是“裸的相似度召回”,不做业务级重排。
4.2 图遍历加混合重排,真实场景里的差异
Zep 的检索链路是“语义召回 + 图遍历 + 合并重排”:先通过语义搜索召回一批候选知识,再沿实体关系扩散到关联事实,最后合并去重、按时间排序。效果上不是捞“一段相似的话”,而是把“和这个用户相关的事实网”整个捞出来。在客服场景里,这种检索方式能回答“用户上次提到的退款诉求后来处理得怎么样”,纯向量检索很难做到。
代价是延迟更高。我们在压测时发现,Zep 的端到端延迟比纯向量召回高一倍左右。如果 Agent 主链路本身要求秒级响应,中间加一层图遍历要做缓存或异步预取来平衡。所以选型要先明确场景:需要深度关系推理的,选图遍历;只需要快速回调历史相似片段的,向量库足够。
4.3 写入和冲突消解:真正决定系统下限的环节
记忆系统最难的其实是写入侧,不是检索侧。一段普通对话进来,哪些该写入?和旧记忆冲突时怎么处理?Mem0 的做法是让模型参与决策:“这句话应该 append 到现有记忆,还是 update 某条旧记忆?”这个判断是 LLM 调用,不是规则匹配,所以每次写入都有模型成本。Zep 则是让图谱框架自动合并实体和关系。LangChain 的 Memory 模块基本不处理冲突,谁进去谁永久留着,这也是它不适合当长期记忆核心的原因。
我提示一个实战坑:自动抽取能力越强,越不能“什么都往里扔”。我们的测试里,模型会把环境描述、语气词、一次性指令都当成记忆保存下来,几周后召回时污染非常严重。好的做法是给记忆系统配置写入过滤规则,比如只保留“用户偏好”“事实类信息”“明确的决定”,把临时的状态描述挡在外面。记忆系统的工程化程度,很大程度体现在这层可配置的过滤上。
5. 上下文管理对比:Agent 能连跑多久,看的就是这一层
5.1 Letta 的虚拟分页与核心记忆常驻
Letta 给我的最大启发,是把“记忆”和“上下文”在工程上真正打通。Agent 运行时,核心记忆常驻在上下文里——身份设定、关键事实、任务状态。对话历史可以滚出上下文,存到档案库;当模型发现当前任务需要某段历史时,又通过内置工具主动发起搜索,把相关片段重新拉回上下文。这和手动在 Prompt 里拼接历史是完全不同的心智。
它的记忆分三层:core memory(永久活跃的关键信息)、recall memory(完整对话历史)、archival memory(档案存储)。上下文快满时,系统自动触发分页,把不活跃内容归档,必要时压缩历史。我们用 Letta 跑过一个连续 7 天的任务,中间重启过两次服务,状态恢复基本无感。代价是这套机制本身也在消耗 Token 和增加延迟,任务规模小的时候体现不出优势,反而显得笨重。
5.2 滑动窗口、摘要压缩与记忆库的定位差异
LangChain 的滑动窗口方案简单可靠,但窗口外的关键信息会直接消失;摘要方案能保留宏观脉络,但每次都要重算历史,Token 开销很高,而且摘要天然丢失细节。Mem0 的 get 接口在设计上不负责“上下文管理”,它更像一个独立记忆库,只保证“按需取出相关记忆”,具体塞多少进 Prompt 由 Agent 框架自己决定。Zep 也一样,它只保证“你问某个时间段发生了什么,我能准确回答”,不替你管理上下文窗口。
所以实测结论很直接:如果你要的是“自动化的上下文管家”,Letta 最合适;如果你已经有自己的 Agent 调度器,只缺高质量记忆后端,Mem0 或 Zep 更合适;如果只是快速验证交互逻辑,LangChain Memory 够用,但别指望它扛住长期记忆压力。
6. 集成度与成本对比:能不能落到生产,差别很大
6.1 框架亲和度:你是“接一个库”还是“搬进一套运行时”
这一维度看的是“你的 Agent 框架能不能顺利接入”。Mem0 的集成做得最全:LangChain、LlamaIndex、CrewAI、AutoGen、OpenAI Tools 都有对应封装,业务代码改动很小。Zep 有官方 LangChain / LlamaIndex 集成和独立 SDK。Cognee 偏向 Python 库,集成需要定制。Letta 比较特殊,它自己就是 Agent 运行时,你基本是“搬进 Letta 生态”,而不是“接入一个库”。
如果你已经在 LangChain 生态里,几个项目都能接;如果 Agent 是纯自研调度器,优先考虑 API 设计独立的 Mem0 或 Zep。LangChain 记忆模块在新版本里把很多 Memory Class 标记为 legacy,新项目如果完全依赖它,后续迁移成本非常高。
6.2 部署形态与维护成本:Docker 自托管还是托管 API
部署成本是最容易被低估的一项。Mem0 自托管需要同时跑向量库和图数据库,至少两个容器起步,内存占用不小;Zep 社区版默认依赖 Postgres 和图数据库,运维链路更长;Letta 相对“全家桶”,单机 Docker 能跑,但版本发布节奏和 API 变动都需要跟踪。
为了快速验证,也可以用托管 API。Mem0 有云服务,Zep 也有商业版本,两者都提供免费层做 PoC。我的建议是:demo 阶段直接用托管 API,用真实数据跑通后再评估要不要自托管。不要一开始就在 Kubernetes 上给记忆系统做高可用设计,先把业务价值验证清楚,再补基础设施。
6.3 语言生态和社区活跃度:活跃度直接决定踩坑速度
从社区活跃度看,Mem0 的 Star 增长快,Issue 响应及时,文档质量不错;Letta 有很强的研究社区背景,但迭代频繁,API 变动较大;Zep 的文档偏企业风格,社区版更新速度和资料完整度略逊于前两者。Python 生态里这些项目基本是标配,如果要 JS/TS 方案,Mem0 和 Zep 覆盖得更好,Letta 也有客户端,但不少能力要配合 Python Server 使用。
Commit 频率高的项目通常会更快修掉记忆系统的边角 bug,但也会更快破坏 API 兼容。上生产前,建议固定一个版本并锁定镜像 tag,不要追最新 release。
7. 选型建议:先看场景,再谈方案
7.1 通用对话和个人助理
如果你在做的是通用个人助理、聊天机器人,核心需求是记住用户偏好和长期对话事实,我的首选是 Mem0 做记忆库,配自己的短期上下文管理。理由很简单:它的 API 抽象最清晰,User / Agent / Session 三层维度直接对应业务需求,多用户隔离也好做。我们自己就是这样搭的——LangChain 负责 Agent 编排,Mem0 负责长期事实记忆,中间用一层异步任务做记忆写入过滤。
7.2 客服、CRM 这类时间敏感场景
如果场景里频繁出现“用户上次什么时候提的”“这个客户的两个需求之间有什么关系”,Zep 是更匹配的选项。客服 Agent 需要的不是一条条碎片记忆,而是能按时间回溯的客户关系网。Zep 的时序知识图谱正好在这个甜区。
这里要提醒,Zep 的效果依赖消息质量。如果原始对话噪声大,实体抽取会不稳定,图谱里会出现大量错误关系。需要在入口做一轮消息清洗,把有效的用户反馈、工单记录、订单状态先结构化,再喂给记忆系统。
7.3 长周期自主运行的 Agent
如果 Agent 要连续几周自主运行,中间会主动、会被中断、需要断点续跑,Letta 更合适。它把“上下文管理”也接过去了,省去自己写压缩和调度逻辑。代价是要接受它的运行时约束,并在产品里围绕它做状态恢复方案。我们的经验是:长周期 Agent 的状态恢复能力比记忆召回能力更关键,Letta 的 core memory 常驻机制能给到稳定保障。
7.4 RAG 知识库这类偏数据记忆的场景
如果记忆的对象主要是文档、知识库,而不是对话历史,Cognee 这类语义记忆引擎更贴合。它本质上是对知识库做图谱化,让 RAG 的回答有推理链路可循。纯对话记忆场景硬上 Cognee 会显得重,性价比不高。常规文档型知识库,普通的向量库加 RAG 已经能解决大部分问题,Cognee 的价值在于知识之间需要跨文档推理时才会放大。
| 场景 | 首选 | 次选 | 注意事项 |
|---|---|---|---|
| 通用个人助理 | Mem0 | LangChain Memory | 配置写入过滤规则 |
| 客服/CRM | Zep | Mem0 | 先清洗消息,再做实体抽取 |
| 长时自治 Agent | Letta | Mem0 + 自研调度 | 接受运行时约束 |
| RAG 知识库 | Cognee | 普通向量库 | 文档稳定时效果更好 |
8. 落地前先想清楚这四个坑
8.1 记忆污染:写入的垃圾会持续污染后续决策
最大的坑是记忆污染。记忆一旦写入,就成了“既成的过去”,会持续影响 Agent 后续所有决策。我们用 Mem0 时遇到过典型问题:用户随口说了一句“今天心情不好”,系统把这条当作长期情绪偏好存储,之后每次生成回复都带着过分体贴的语气,用户反而觉得奇怪。更隐蔽的是,模型会把“用户在某次对话中提到的临时限制”当成永久规则,导致后续所有任务都被这个旧限制束缚。解决办法就是上面说的:写规则,只保留可复用的偏好、事实和决定,不保留一次性状态描述。
8.2 并发、身份绑定与隔离
记忆系统的并发问题容易被忽略。多个用户在同一 Session 下、同一个用户开多个 Session、多个 Agent 共用一个记忆库,身份隔离稍有疏漏,A 的记忆就会串给 B。Mem0 的 user_id / agent_id / session_id 分层设计就是为这个准备的,接入时一定要在业务层把 ID 映射做扎实。Zep 类似,也靠 session / user 维度组织。上线前要专门写一组“串记忆”的回归测试——用两个不同用户反复切换对话,确认召回结果里不会出现对方的信息。
8.3 Token 成本失控:记忆比主链路还贵
自动抽取、冲突判断、摘要压缩,每一步都是 LLM 调用。我们的统计显示,记忆系统的运行成本有时比 Agent 主链路还高。特别是用大模型做实体抽取和时间线构建时,成本会随对话量线性增长。优化手段无非几种:批量抽取代替每轮抽取、只在关键事件后做异步写入、降低摘要频率、用小模型做抽取、大模型只做最终决策。成本监控方面,建议在记忆系统 API 层做 Token 计量,按用户维度分别统计,谁在烧钱一目了然。
8.4 遗忘机制:很多开源系统最薄弱的环节
长期运行一定有误记和过期信息,但多数开源记忆系统对“遗忘”的支持很弱。Mem0 有 delete 和 history 操作,能回溯更新;Zep 有图更新机制,但要全网重写一条用户事实,操作并不简单。上线前一定要设计最低限度的遗忘策略:定期清理低置信度记忆、给记忆加置信度评分、为关键信息留人工审核入口。不要等记忆库被陈年垃圾填满再处理,到那时候清洗成本远超预期。
最后再分享一个我个人在多次选型中的体会:记忆系统不是一个能“装完即走”的组件,它需要和业务语义深度配合。“谁值得记住、什么值得记住、什么时候该忘”这三个问题的答案,必须由业务方给出。开源项目替你解决了存储和检索的通用问题,但解决不了业务记忆边界的定义问题。每次接到新项目,我做的第一件事不是跑 demo,而是定义记忆 Schema——哪些字段是用户标识、哪些是客观事实、哪些是偏好偏置,这决定了后续所有检索和更新的质量。选型只是开始,把记忆系统纳入日常测试和监控,才是长期跑下去的关键。