AI Agent记忆系统开源方案对比:从Letta到Mem0的选型指南
做 AI Agent 的朋友几乎都会遇到同一个尴尬:模型再聪明,换个会话它就把前半小时聊的东西全忘光。别急着怪模型,这是上下文窗口的物理边界决定的。所谓 Agent 记忆系统,就是把“工作台上的便利贴”和“仓库里的资料柜”分开管理的一套开源方案集合。这篇文章我挑了目前社区热度高、能真正部署落地的几个开源项目——Letta(原 MemGPT)、Zep/Graphiti、Mem0、Cognee,以及 LangChain 内置的 Memory 组件——做一次多维对比,重点讲清楚它们的存储架构、检索思路、部署成本和适用场景,最后给出可以直接抄作业的选型建议和避坑清单。目标是帮你快速搞明白:这些系统到底在解决什么问题、各自优势在哪,以及你的项目现阶段该用哪一个。
1. 先讲透:为什么 Agent 离了记忆系统就做不成事
1.1 上下文窗口是物理边界,不是越大越好
很多人觉得记忆问题很简单:“把上下文窗口调大不就行了?”实际跑过就知道事情没那么美好。窗口变大意味着每轮请求的 token 量线性增长,推理延迟和费用一起涨,而且模型在超长上下文里会出现明显的“注意力稀释”——就像让你在 100 页资料里找一句话,信息越多越容易漏掉关键的那句。
我实际测过一个代码助手场景:把最近 60 条对话消息全部塞进上下文,模型完全被历史消息带偏,连用户刚刚明确说过的技术栈都答错。原因很简单,历史消息里有大量无效寒暄、重复提问和过时信息,真正有用的中间结果(比如上一步工具返回的数据)只占很少一部分,模型“看到”了但不“注意”到。记忆系统要做的事,不是原样保存所有对话,而是把值得长期保留的信息提取出来、组织好,在需要的时候能精准放回上下文。
1.2 记忆的真实结构:三层,不是一层
把 Agent 记忆类比成人的记忆会更清楚。人的工作记忆相当于当前正在处理的信息,容量很小;情景记忆相当于“昨天聊过什么、做了什么事”的阶段记录;语义记忆则是沉淀下来的事实和规则,比如“用户偏好 Python”“项目要求容器化部署”。开源记忆系统的本质差异,就看它怎么处理这三层:
- 工作记忆:对应 LLM 的上下文窗口,状态天然易失,由模型每次推理时读取。
- 情景记忆:对应会话历史的归档,需要持久化存储,可以按时间回溯。
- 语义记忆:对应从对话中提炼出来的事实、实体关系、偏好,是结构化程度最高的一层。
聊天记录存档只是最原始的“会议纪要”,真正的记忆系统至少要能回答:哪些内容该进入长期存储、哪些该被丢弃、检索时以什么形式注入上下文。不同开源项目的取舍,基本都围绕这三个问题展开。
1.3 开源记忆系统演进脉络:从截断到知识图谱
早期方案很简单,就是滚动窗口截断,只保留最近 N 条消息,缺点是老信息彻底丢失。后来有人做摘要记忆,用 LLM 把历史对话压成一段 summary,这解决了部分问题,但摘要丢失细节、更新成本高。再后来 RAG 思路进来,把历史消息切片存向量库、按相似度召回相关性高的片段,比摘要灵活,但召回的片段往往是碎块、缺乏关系。现在的演进方向是两类:一类像 Letta 那样给 Agent 自主管理内存块的能力,让模型决定什么进什么出;另一类像 Zep/Graphiti 那样引入时序知识图谱,把记忆建成带时间戳的实体关系网络。Cognee 和 Mem0 则在“管道化”上发力,把记忆的写入、抽取、检索做成标准化操作。理解了这条脉络,再回头看各家方案会容易很多。
2. 参与横评的开源选手和选择标准
2.1 六位选手和项目定位
先亮出本次对比的选手阵容。我从一堆项目里筛出这几个,是因为它们基本代表了目前开源 Agent 记忆系统的全部主流技术路线,不是随便挑几个 star 高的凑数。
| 项目 | 一句话定位 | 技术路线 | 部署形态 |
|---|---|---|---|
| Letta(原 MemGPT) | 带自主记忆管理的 Agent 运行时 | 内存分页 + 自我编辑记忆块 | 服务端 + SDK,可本地跑 |
| Zep + Graphiti | 生产级长期记忆服务 | 时序知识图谱 + 会话摘要 | Docker 服务端,独立部署 |
| Mem0 | 结构化事实记忆中间件 | 抽取 + 去重 + 向量存储 | Python SDK,可自托管 |
| Cognee | 认知操作框架 | 图谱 + 向量 + 摘要混合表示 | Python 库 + 服务 |
| LangChain Memory | 框架内置记忆组件 | 缓冲、摘要、向量存储等多种策略 | 库级组件,随代码集成 |
| 自研 baseline | 自己拼装存储 + 检索 | 完全自定义 | 无限制 |
最后一行“自研 baseline”不是来凑数的。对比这件事,如果没有基线就没有参照物。我记得第一次接触记忆系统时也想过“不如自己写个 Redis 存历史消息”,后来发现真正难的不是存储,而是抽取、更新、检索、注入这一整套逻辑。把自研方案放进对比表,你才能直观感受现成方案替你做掉了哪些脏活累活。
2.2 为什么是这几个项目
选择标准有三条。第一,必须能自托管,不能强制绑定某个云服务,否则谈不上“开源落地”。第二,项目在 GitHub 上至少持续维护一年以上,有活跃的 issue 处理和新版本发布。第三,覆盖不同技术路线,让对比有区分度。按这个标准,我没有选那些只提供 API 的闭源记忆服务,也没有选纯粹的消息队列方案——那些只是“存储”,不算“记忆系统”。另外像 Elasticsearch 这类通用存储我也没单独列,因为它本身不是记忆系统,但可以作为底层存储被上面某些项目调用。
3. 核心维度逐项对比
3.1 存储架构:SQLite、向量库、还是图数据库
记忆系统的地基是存储。这块选型直接决定了查询能力上限和运维复杂度,也是最容易劝退新手的部分。
- Letta采用“核心内存 + 归档内存”两层架构。核心内存是常驻上下文的 JSON 块,归档内存落到向量库。整体上存储不重,但需要额外依赖一个向量数据库。
- Zep + Graphiti是最重的:会话数据存 Postgres,知识图谱存 Neo4j,向量检索在单独的向量存储里。换来的是非常强的关系查询能力,比如“这个用户和哪些项目文档产生过交互”。
- Mem0的存储层是可插拔设计,向量库、图库、关系库都能接。默认玩法是把抽取出来的事实存成向量,按用户维度隔离。
- Cognee也是多重表示:同一份输入同时产出知识图谱、向量索引和摘要,存储底层通常需要一个图数据库配合向量库。
- LangChain Memory完全是轻量派,默认内存缓冲,要持久化就得自己接 SQLite、Redis、向量库,存储层 DIY 程度最高。
说句大实话,存储层选择别被“图数据库听起来高级”忽悠。如果你的记忆场景只需要“用户上次说了什么偏好”这类简单键值式查询,用图数据库纯属给自己找麻烦——部署多一个容器、数据模型多一层复杂度。反过来,如果你要做“把与这个项目相关的历史决策全部找出来”这种关系型查询,纯向量库也做不到。选型先想清楚查询模式,再定存储。
3.2 记忆组织与检索机制:谁来决定该记什么、该想什么
这一维度是我认为最核心的差异点,也是各家方案拉开差距的地方。记忆系统不是“存起来就行”,难的是“记什么”和“拿什么出来用”。
Mem0 走的是抽取式路线。它把一组对话丢给 LLM,抽取出用户偏好、事实断言这类结构化信息,再做去重和冲突处理,存成一条条独立记忆。查询时把用户当前问题做向量召回,把相关记忆拼进上下文。这套做法优点非常明显:记忆是干净的、可解释的,适合做用户画像;缺点也很明显:抽取过程依赖模型水平,事实语义会损失,对话中的上下文细节(比如当时的具体情绪、语气)基本留不下来。
Zep/Graphiti 走的是图谱式路线。它不仅记录实体和关系,还给每条边打时间戳,支持“按时间线查询某个实体的状态演变”。比如用户上个月偏好 Python、上周切换到 Rust,图谱里能把这种变化轨迹还原出来,而不是只存一条“用户喜欢 Rust”的覆盖式结论。这对客服、医疗、企业级决策场景非常有用,但代价是图谱维护成本高,实体抽取的准确性直接影响图谱质量。
Letta 的路子最有意思,它把记忆管理权交给 Agent 自己。记忆块类似操作系统的内存页,Agent 在推理过程中可以主动做“换入换出”——把当前对话需要的背景知识从归档内存拉进上下文,把不再相关的内容移出去。这是一种自主式记忆,灵活性极高,但前提是底层模型能力要够,否则模型自己都不知道该记什么,效果会很飘。
Cognee 把记忆组织做成了一套标准管道:数据先进来,经过认知化加工,产出图谱、向量、摘要多种表示,查询时用专门检索算子组合召回。更适合那些想把记忆做成可观测、可调试模块的研究型团队。
LangChain Memory 则是“全是你说了算”。缓冲、摘要、向量检索三种策略任选,组合逻辑由开发者在提示词里写死。灵活度最高,但记忆质量完全取决你自己的设计水平,上限高下限也低。
3.3 上下文管理能力:记忆系统与聊天记录存储的分界线
很多人把“记忆”和“存储”混为一谈,其实分界线在于上下文管理。存储只是把数据放进硬盘,记忆系统还要负责决定什么时候把哪块记忆注入模型、注入多少 token、如何处理新老信息的冲突。
| 对比项 | Letta | Zep/Graphiti | Mem0 | Cognee | LangChain Memory |
|---|---|---|---|---|---|
| 自动管理注入 token | 强,按记忆块换入换出 | 中,通过摘要 + 图谱查询注入 | 中,按召回结果拼装 | 中,检索组合决定注入 | 弱,开发者手工设计 |
| 处理事实冲突/过期 | 靠模型自主更新 | 靠时间戳 + 边更新 | 内置去重覆盖 | 靠管道重跑 | 不处理 |
| 多轮对话中间态保存 | 支持 | 支持 | 偏语义记忆,不擅长 | 支持 | 支持 |
| 多租户隔离 | 靠 Agent/会话隔离 | 原生 Session 隔离 | user_id / agent_id 隔离 | 支持项目级隔离 | 开发者自己实现 |
举个例子会更直观。做客服机器人,用户昨天说“我不用微信支付”,今天又说“还是微信方便”,如果是不带冲突处理的向量检索,召回时两条互相矛盾的记忆会同时进上下文,模型就懵了。Mem0 处理这类情况的做法是更新旧记忆,让新事实覆盖旧事实;Zep/Graphiti 则是保留时间线,模型能感知到“用户态度发生了转变”。没有上下文管理的“伪记忆系统”只会滚滚时间窗口,碰到这类场景必然翻车。
3.4 部署运维成本:一份 Docker Compose 还是三台服务
部署成本是我实测下来最容易让项目夭折的隐性因素。我遇到过团队兴致勃勃引入某重量级记忆框架,结果部署两周都没跑通,最后回退成自研 Redis。
- LangChain Memory:几乎没有额外部署。代码里初始化一下就行,需要持久化就接 SQLite 或 Redis。这是最轻的选择。
- Mem0:也不重。可以嵌入式方式直接用 Python 库,数据落到你指定的向量库;自托管版就多一个 FastAPI 服务进程。
- Letta:需要起一个服务端,带上向量库。官方提供 Docker Compose,整体中等偏轻。
- Cognee:需要接图数据库 + 向量库,管道调优要看日志,运维要求中高。
- Zep + Graphiti:最重。Postgres、Neo4j、向量库、Zep Server 四个组件齐上阵。生产环境还要考虑备份、监控、扩容,没有专门的运维精力慎选。
一个很现实的建议:先看团队里有没有人能长期维护基础设施。如果只有两三个后端开发兼着做,尽量选部署链路短、依赖容器少的方案。记忆系统只是功能模块,不是公司的核心基础设施,别让它把运维精力吸干。
3.5 社区活跃度与生态成熟度
最后看社区。这里我不给精确 star 数,只给量级参考,因为数字变动太快,写死了反而误导人。
| 项目 | 社区热度量级(估) | 活跃度特征 | 主要用户画像 |
|---|---|---|---|
| Letta | 很高 | 迭代快,官方文档完善 | 做自主 Agent、研究记忆机制的团队 |
| Zep + Graphiti | 中高 | 企业级采用多 | 客服、企业知识库、对可解释性有要求的场景 |
| Mem0 | 很高 | 增长极快,生态插件多 | 产品型团队,想把用户偏好做成结构化画像 |
| Cognee | 中 | 学术与研究导向 | 关注认知架构、多跳推理的团队 |
| LangChain Memory | 高(随 LangChain 整体) | 稳定但组件相对基础 | 已在 LangChain 生态内、想快速加记忆的开发者 |
GitHub 热度能反映趋势,但不代表适配你的场景。我见过有人因为某框架 star 高就盲目引入,结果才发现它默认的依赖跟自己技术栈冲突。选型权重建议:适配度 > 社区热度 >> star 数量。
4. 实战选型与最小可用配置
4.1 按场景选型的三条路线
与其对着功能清单纠结,不如直接看你的场景缺口。我把常见需求归成三条路线,对号入座即可。
第一条路线:原型验证、内部工具。目标是“跑通一个带记忆的 Agent”,最快路径是 LangChain Memory 或 Mem0 的嵌入式用法。组件少、改代码就能上,哪怕后面不合适,替换成本也低。
第二条路线:产品化、需要长期用户画像。比如电商助手、知识库问答、CRM 类工具,核心诉求是“记住用户这个人”,优先考虑 Mem0。它的抽取式记忆能输出可读的用户事实列表,业务团队可以直接拿来分析。
第三条路线:对话过程需要高保真、可回溯。比如客服质检、医疗预问诊、复杂任务编排,需要知道“当时为什么这么决策”,优先考虑 Zep/Graphiti。时序知识图谱的价值就在这里,它不只是记住结论,还记住了结论变化的过程。
4.2 最轻量接入:LangChain 内置记忆组件
如果你已经在用 LangChain 的 AgentExecutor,加记忆最直接的是用RunnableWithMessageHistory。这是一段可运行的示例思路:
from langchain_core.runnables.history import RunnableWithMessageHistory from langchain_core.chat_history import InMemoryChatMessageHistory store: dict[str, InMemoryChatMessageHistory] = {} def get_session_history(session_id: str) -> InMemoryChatMessageHistory: if session_id not in store: store[session_id] = InMemoryChatMessageHistory() return store[session_id] agent_with_history = RunnableWithMessageHistory( agent_executor, get_session_history, input_messages_key="input", history_messages_key="chat_history", )注意这个方案默认全部历史都进上下文,跑长对话很快 token 爆炸。要控制成本,建议换成ConversationSummaryBufferMemory,在 memory 里设max_token_limit,超过阈值就自动把旧消息压缩成摘要。这是成本和质量比较平衡的做法。真实项目里别硬上纯内存存储,起码要接 SQLChatMessageHistory 做持久化。
4.3 结构化事实记忆:Mem0 接入示例
Mem0 的玩法很直接:往里面 add 对话内容,它返回抽取出的记忆片段;查询时给一句话,它召回相关记忆。示意配置和调用如下:
from mem0 import Memory config = { "vector_store": { "provider": "qdrant", "config": { "collection_name": "mem0_demo", "host": "localhost", "port": 6333, }, }, "llm": { "provider": "openai", "config": {"model": "gpt-4o"}, }, } memory = Memory.from_config(config) # 写入对话,提取长期记忆 memory.add("用户说:我平时主要用 Rust 写后端,对 Python 也能接受", user_id="user_1") # 检索时调用 result = memory.search("这个用户最常用的编程语言是什么?", user_id="user_1")实测下来,Mem0 的抽取质量非常依赖你喂给它什么样的instruction文本。默认的提取提示词偏通用,如果做垂直行业(比如医疗、金融),建议自定义抽取指令,把它调成只抽取与业务相关的实体和事实,否则记忆库里会混进大量无关信息。
4.4 服务化记忆:Zep / Graphiti 启动与调用
Zep 走服务端部署,最省事的路径是 docker compose 拉一套:
git clone https://github.com/getzep/zep cd zep/docker docker compose up -d如果要用 Graphiti 这套时序图谱能力,需要额外准备 Neo4j,把 Zep 配置里的 graph store 指向它。接口调用是大致这样的模式(不同版本 API 有差异,以官方文档为准):
from zep_python import ZepClient client = ZepClient(base_url="http://localhost:8000") client.memory.add( session_id="user_1", role="user", content="我最近在调研 agent 记忆系统,后续可能会做技术选型", ) facts = client.memory.search(session_id="user_1", text="用户最近在关注什么")Zep 的亮点是异步抽取和摘要:写入请求立即返回,后台异步构建图谱和摘要,不影响主链路延迟。这点在生产环境非常重要,因为记忆写入如果阻塞主请求,体验会非常差。
4.5 Letta 的最小使用与记忆块概念
Letta 是 MemGPT 的延续,安装后可以一键启动:
pip install letta letta server startLetta 的核心概念是 memory block,每个 block 是一段 JSON 结构,Agent 可以自己读写。你可以给每个用户建一个 profile block,里面放用户基本信息;Agent 在对话中如果发现信息有变,会自主去更新 block,而不是每次重新抽取。这套机制的好处是记忆内容随时可查、可改,可解释性很强;坏处是它只对“会用”的模型友好,小模型经常把 block 改错,需要回滚机制兜底。
4.6 关于 embedding 模型选型,一个容易被忽视的坑
上面几个方案的检索效果都依赖 embedding 模型质量。中文场景如果用默认的 OpenAI embedding,召回效果可能不如专门的多语言模型。我测试下来 bge-m3 这类多语言模型的稳定性和中文支持都很能打,而且可以本地部署,不增加 API 成本。选型时记得做一件事:构造一个“跨会话召回集”——拿昨天会话里用户说过的话作为查询条件,看今天能不能召回正确记忆。别只看相似度分数,要实测召回率。
5. 部署避坑与常见问题速查
5.1 我踩过的五个典型坑
第一个坑是只存不删。早期我让 Agent 把每轮对话都写入记忆库,结果第二天记忆库过万条,检索结果稀碎,用户昨天刚改的偏好被十天前的旧偏好干扰。解决办法是设置记忆生命周期:定期清理低价值记忆、设置事实覆盖规则、对陈旧数据归档。
第二个坑是把向量相似度当检索准绳。向量检索召回的相关片段常常是一堆碎片,拼在一起逻辑不通。后来我加了两个后处理步骤:按时间排序 + 过滤冲突项,再拼接进上下文,效果才明显改善。记住,向量召回只是初筛,真正进上下文的记忆还得过一遍清洗。
第三个坑是同步做摘要生成。摘要对 LLM 调用很费时,如果放在请求链路里同步执行,每轮对话多出 1 到 2 秒延迟,用户体感非常差。正确做法是写入阶段异步触发摘要,查询阶段只读。
第四个坑是没有租户隔离字段。做多用户产品时忘了在记忆写入和检索都带上 user_id,结果出现 A 用户问 B 用户偏好的严重事故。这不是模型问题,是工程问题。无论选哪个方案,第一件事排查它的租户隔离模型是否是强制的。
第五个坑是抽取逻辑重复调用大模型。Mem0 这类方案每次 add 都会触发 LLM 抽取,如果前端每敲一个字就调一次接口,token 费用会飞速膨胀。我后来加了防抖和 session 级缓存,同一会话内只抽取一次。
5.2 常见问题排查速查表
| 现象 | 可能原因 | 排查方向 |
|---|---|---|
| 会话重启后答非所问 | 检索结果为空,或注入上下文失败 | 检查记忆检索日志,确认 query 能否召回内容 |
| 记忆重复堆叠、互相矛盾 | 写入链路没去重 | 检查方案是否内置去重;Mem0 开启 dedup,自研需加语义去重 |
| 图谱构建速度慢 | 同步构建或未开增量 | 开启异步 worker,调整批处理大小 |
| 中文检索质量差 | embedding 模型不适配 | 换多语言模型(如 bge-m3),重建索引 |
| API 成本暴涨 | 每轮全量检索/重复抽取 | 限制 top-k、加缓存、缩短 memory 上下文注入长度 |
| 接口写路径超时 | 直接调用 LLM 抽取且同步阻塞 | 改为异步写入,主请求只做本地存储 |
| 图谱关系混乱 | 实体抽取质量差 | 调抽取提示词、过滤低置信实体,或考虑用更强模型做抽取 |
5.3 四个值得长期记住的经验
第一个经验,选型别被 star 数骗了。一个框架热不热,和适不适合你完全两码事。看它的 issue 区、文档质量、最近提交频率,比看 star 数靠谱得多。
第二个经验,先跑最小闭环再上重方案。我建议每一步都先用最轻的做法跑通:先在 LangChain 里加 SummaryBufferMemory 跑一个礼拜,再考虑要不要上 Mem0、要不要上图谱。很多项目跑完最小闭环就会发现需求已经满足了,根本不需要最重的方案。
第三个经验,把记忆当作一等公民设计数据模型。很多团队把记忆当成聊天记录的附属品,随手丢个 JSON 进数据库。等到想按用户偏好精准检索时,数据结构已经烂到无从下手了。从第一天起就想清楚:记忆实体是什么、实体的属性有哪些、实体之间的关系怎么表达。
第四个经验,持续监控记忆命中率。这是我自己后来才加上的指标:每次用户提问后,记录检索系统有没有召回有效记忆。长期命中率低于 30%,说明记忆系统的组织方式有问题,需要调抽取规则或索引策略。没有这个指标,你会一直以为记忆正常工作,直到用户投诉“它怎么还不记得我”才发现问题。
做记忆系统选型这段时间,我最大的体会是:别一开始就迷信“记忆应该像人一样记住所有事”。产品里真正需要记好的东西只有两类——短期内不能丢的中间状态,和长期会重复调用的用户偏好。把这两层分开设计,很多开源方案其实是可以替换的,核心还是你对记忆模型本身的思考和数据建模的精细度。先把手头最小的场景跑通,再谈架构升级,这条路远比“一步到位上重方案”走得稳。