做了大半年Agent应用,我被问得最多的一个问题不是工具调用,也不是并发,而是——你这Agent怎么聊两句就失忆了?要聊清楚这个话题,就必须把Agent长期记忆的设计思路掰开揉碎讲一遍。这篇内容,是我们团队把这大半年里对比过的六个主流项目,从MemGPT到Mem0,从LangChain到Graphiti,逐一复盘后的浓缩版,适合正在做Agent应用、正准备上长期记忆功能、或者只是好奇Agent到底能不能记住事的人。
1. 先看清问题:Agent为什么普遍"记性不好"
很多刚接触Agent开发的人,第一步想到的是把对话历史一股脑塞进上下文,然后发现Agent依然答非所问。其实这不怪模型,而是我们对"记忆"这件事的理解从一开始就是错的。
1.1 上下文窗口不是内存,只是短期工作台
要理解长期记忆,先要破除一个常见误解:大模型的上下文窗口再大,也不是"内存"。上下文窗口更像是你桌面上的白纸——你可以在上面写很多东西,但一关电脑、换一个会话,这张纸就清空了。即便你用了支持超大上下文的模型,把两小时前的一段重要约定一直放在窗口里,也会遇到两个问题:
- 放得下不等于找得回。窗口里塞满了对话原文,Agent在需要某个信息时未必能精准找到,反而被大量无关内容干扰,导致回答跑偏。
- 成本随窗口长度线性上涨。长上下文并不是免费的,很多场景下你每轮都把历史全量喂给模型,费钱且响应变慢。
大模型本身的"无状态"是原生缺陷:每次调用都是一次全新的对话,模型并不知道自己上个回合说过什么。所以我们需要在模型外面加一层"记忆系统"。长期记忆本质上不是模型能力,而是工程问题——你需要在模型之外设计一套写、存、读、更新的机制,让Agent在合适的时机把关键信息放进"仓库",又在合适的时机把它们"搬到桌面上"。
1.2 长期记忆要回答的三个问题
我看过不少方案,也自己动手搭过,发现不管怎么设计,万变不离三个核心问题:
- 记什么:是存原始对话,还是存摘要?是提取用户偏好、实体、事实,还是把知识文档也纳入记忆?
- 什么时候记、什么时候读:是每一轮都写?还是后台异步定期处理?读取时是全量注入,还是按需检索,或者让Agent自己决定调用?
- 怎么更新、怎么忘:推进一个新事实时,旧记忆是该被覆盖还是共存?记忆会不会过期?用户说"我不喜欢这个"时,系统能不能把之前的对立偏好正确处理掉?
这三个问题,就是下面六个方案最主要的差异点。你能看到,有的方案在"记什么"上做文章,有的在"什么时候记"上做文章,有的在"怎么更新"上下足了功夫,各有各的侧重点。
1.3 评价记忆系统的六个维度
后续选型时,我会反复用这六个维度去衡量具体方案,先列出来方便你对照:
| 维度 | 要回答的问题 |
|---|---|
| 容量 | 能存多少历史而不崩溃 |
| 精度 | 取回的信息是否准确、够细 |
| 新鲜度 | 能不能感知"记忆已经过期" |
| 成本 | 存储、检索、token消耗是否可控 |
| 可控性 | 开发者能不能干预写入和读取 |
| 接入成本 | 落地到自己项目要花多少精力 |
你不可能六个维度全都要,不同项目取舍不同。接下来要拆解的六套方案,本质上是这六个维度上的不同取舍组合。看清了取舍,你就明白为什么没有"最好"的记忆方案,只有"最合适"的记忆方案。
2. 六路方案全景:名字、流派与一句话定位
我筛选了六个有代表性、社区讨论度高的项目或思路:MemGPT(现在的Letta)、LangChain系(Memory模块加LangMem)、Mem0、Zep系(Graphiti)、Cognee,以及最常被拿来自建的RAG向量记忆。先给一个速览表,后面逐节拆。
2.1 一句话定位
| 项目/体系 | 核心思想 | 存储介质 | 典型读取方式 |
|---|---|---|---|
| MemGPT / Letta | 把上下文当物理内存,用虚拟内存机制换入换出 | 外部JSON存储+上下文管理 | LLM自己调用函数读写 |
| LangChain Memory + LangMem | 框架内提供多种记忆策略,SDK实现提取 | 内存/向量库/图存储 | 自动注入检索结果 |
| Mem0 | 提取-存储-合成三层记忆流水线 | 向量库 | 语义检索+聚合记忆体 |
| Zep / Graphiti | 带时间戳的时序知识图谱 | 图数据库 | 图谱查询+时间窗口 |
| Cognee | 图谱+向量的混合记忆引擎 | 向量库+图库 | 语义检索/图遍历 |
| RAG自建向量记忆 | 把历史对话切片向量化,检索注入 | 向量库 | top-k检索 |
2.2 五大流派
细看下来,表面六个名字,背后其实是五条路线:
- 操作系统派:代表性就是MemGPT/Letta。核心思路是"让Agent像操作系统管理物理内存一样管理自己的上下文"。模型需要什么记忆,就通过函数调用去外部存储拿,拿完放回,用完换出。
- 框架派:LangChain/LangMem这类,思路是把记忆作为开发框架的标准化模块,不管是Buffer还是Summary还是向量检索,都给你封装好,你来配置。
- 数据管道派:以Mem0为代表。它不依赖模型自主决定,而是用一套独立的"提取-去重-更新-存储"流水线,把记忆当成数据库来管理,主动从对话里挖掘结构化记忆。
- 知识图谱派:Zep/Graphiti和Cognee都偏向知识图谱。前者强调时序图谱,后者强调与语义记忆结合。它们的共同点是:不仅要知道"发生过什么",还想知道"谁和谁有什么关系"。
- 检索增强派:把长期记忆简化为向量检索问题,靠语义相似度取回相关历史。这是最容易被理解、也最容易自己动手的一档。
这五派没有绝对优劣,但有一个共性值得先点出来:长期记忆不是"存得越多越好",而是"在正确的时间找回正确的内容"。接下来我会重点讲清楚每一派的实际取舍,以及它们各自适合什么场景。
3. MemGPT / Letta:让Agent自己管理"换入换出"
3.1 为什么想到把操作系统搬进来
MemGPT之所以叫"Mem",是因为它把大模型上下文窗口类比成操作系统的物理内存,把外部存储类比成虚拟内存。这个类比非常精准:物理内存小、速度快、一断电就清空;虚拟内存大、速度慢、持久化。LLM的上下文窗口,恰好就是那块"又小又快又一次性"的物理内存。
那谁来决定什么时候把哪段记忆从"硬盘"搬到"内存"呢?MemGPT提供了一套函数调用机制,把这些操作封装成系统可调用的工具。Agent在推理过程中如果发现需要某段记忆,就会主动发起读操作;如果发现自己正在处理一段重要内容但当前上下文放不下,就主动把旧内容归档换出。这套逻辑,本质上是在复刻操作系统里的分页与调度。
我在自己做长会话Agent时,最开始就是用MemGPT的论文思路搭原型。说实话,第一次看到它把LLM的函数调用跟OS的系统调用对应起来时,是有一种"对上了"的爽快感的。它彻底回答了"谁来决定记忆的存取"——答案是模型自己,而非外部脚本。
3.2 主上下文、外部上下文与中断
具体到实现,MemGPT里有几个关键概念:
- 主上下文(main context):模型当前真正看到的内容,受窗口大小限制。
- 外部上下文(external context):存放不常被模型看到的记忆,需要时可被加载。
- 中断(interrupt):当外部信息被加载到主上下文时,系统会触发一次新的推理,让模型在完整信息下再回答。
比如用户说"你上周答应帮我写个Rust爬虫",Agent发现这句话提到一个实体"Rust爬虫",但当前上下文没有相关内容,它就会调用工具从外部存储把这个记忆取回,触发中断并继续回答。整个过程不需要开发者外挂一套检索服务,完全由模型自己"决定"。
这个"中断"机制的细节很值得琢磨:它相当于给Agent安了一个"记忆触发器"。模型在生成回答之前,先检查自己的上下文里是否缺少必要信息,缺了就主动去取。这和人类回忆的过程有点像——你被问到一个问题,发现记不清,会停下来说"让我想想",然后从长期记忆里翻出相关内容再回答。MemGPT把这个过程形式化了。
3.3 这套设计好在哪里,又难在哪里
先说优点。MemGPT最大的价值是让"记忆管理"从开发者的义务变成了模型的能力视角:你不用再为每个场景手写检索逻辑,Agent可以按需取用,上下文里始终只保留当下最相关的内容,长对话能力理论上接近无限。
但实际落地时,我个人遇到的麻烦也不少:
- 自我管理不可控。模型可能判断失误,该换出的没换出,该读的没读,最终还是要靠提示词和工具设计去约束。
- 写操作的语义模糊。什么算"值得归档",什么算"临时信息",模型分不清,容易积累垃圾记忆。
- 成本不透明。每一次外部读取都可能触发一轮完整调用,调用链变长后,费用和延迟都会上升。
如果要上手,建议先去读Letta的官方文档和MemGPT论文,把小规模Demo跑通再谈生产。它对"长记忆控制理论"的启发意义,远大于开箱即用的意义。我在团队内部推广时,经常把它定位成"思维模型"而不是"生产组件"——用它来理解Agent记忆的层级关系,比直接落地它更有价值。
4. LangChain系:框架内记忆的务实与天花板
4.1 从Buffer到Summary:三种常规记忆的边界
LangChain是最早把Memory概念普及到大众的框架之一。它早期提供的几类记忆,很多初学者都踩过坑:
- ConversationBufferMemory:把对话全量缓存。实现最简单,但token和存储无限膨胀,对话长了根本扛不住。
- ConversationWindowBufferMemory:只保留最近N轮对话。省成本,但窗口之外的"重要历史"会直接丢失。
- ConversationSummaryMemory:定期把旧对话总结成摘要。省token,但摘要会丢细节,尤其丢数字、名字这类精确信息。
- ConversationSummaryBufferMemory:缓冲区加摘要的混合体,超过阈值就把窗口前面的内容摘要化。比前面几种实用,但本质上仍是"启发式管理"。
- VectorStoreRetrieverMemory:把历史对话按语义向量化,每次按相似度取回几段。已经接近RAG思路,但缺少记忆的更新与冲突处理。
这些模块的共性是:它们主要解决的是"单条链内的短期状态管理",离真正的Agent长期记忆还有距离。你可以在简单的Demo里用得很顺手,一旦Agent要给多个用户服务、跨多个会话保留画像,就会立刻暴露问题。
4.2 LangMem:把记忆提取变成正式管线
LangChain团队后来也意识到,光靠Buffer这类"临时工"不行,于是推出了LangMem。它更像是"记忆的SDK":通过LLM调用从对话中提取语义记忆(用户偏好、事实等)和情景记忆(发生过的事件),再写入存储;同时提供冲突管理机制,比如新旧记忆矛盾时的策略。
LangMem的设计思路接近后面要讲的Mem0的"提取派",但深度绑定了LangChain/LangGraph生态。如果你本来就在用LangGraph编排Agent,LangMem可以顺滑地接进去;如果你用的是别的框架,就得多写不少胶水代码。从我试用的体感来说,LangMem的优势是"开箱即用",跟LangGraph的状态管理配合得不错;短板则是它更像一套示例性质的SDK,真要支撑复杂生产场景,还需要自己在提取质量、去重策略上投入不少调优工作。
4.3 框架内方案的隐藏坑
我自己用过一段LangChain系记忆,有几个体会值得分享:
- "记忆"和"状态"要分清。LangGraph的checkpoint存的是Agent执行到哪一步、中间变量是什么,那是执行状态,不是用户长期记忆。很多人把两者混为一谈,最后发现重启后机器人照样失忆。
- 摘要记忆在跨语言、跨术语场景下失真严重。比如金融场景里"昨天成交价¥123.5"被摘要成"价格有波动",恢复对话时模型就只能乱编了。
- 框架升级频繁,API改动大。LangChain的Memory类在不同版本里反复改接口,我做的一个小项目从0.1升到0.3,光记忆相关代码就重写了一遍。如果做生产系统,建议把记忆存取封装成自己的接口,别在业务代码里到处直接调用框架类。
提示:框架内方案最适合做快速验证,尤其是POC阶段。但如果你想做严肃的长期记忆,最终还是要落到更专门的项目上,或者自己在框架之上做一层稳健封装。
总之,LangChain系的价值是"教育意义"大于"生产意义"——它帮你理解了记忆有哪些类型、各自什么代价,但真正要做长期记忆的时候,你会发现还是得自己动手,或者转向下面这些专门方案。
5. Mem0:提取-存储-合成,把记忆当数据来管
5.1 从消息到记忆的三步流水线
Mem0是我个人用得比较多、也比较推荐的一个开源项目。它的核心思想跟操作系统派完全不同:不指望模型自己管理记忆,而是专门跑一条"记忆流水线",从对话和文档里主动提炼出结构化的记忆。
它的流程可以拆成三步:
- 提取:对每轮对话或每份文档,让LLM提取出关键事实、实体和用户偏好。比如用户说"我喜欢极简风格的仪表盘,不要红色",系统会提取出两条偏好,而不是把这句话原样存进去。
- 去重与合并:新提取的记忆会跟库里已有记忆做语义比对,如果高度相似,就把新旧内容合并或更新,避免重复堆积。
- 合成与存储:最后形成记忆体(memory),写入向量库,同时保留元数据供检索和审计。
这套流程的好处是:存储的不是原文碎片,而是"精炼后的用户画像"。想象一下,你收藏的不是几百条聊天记录,而是一张动态更新的档案卡,每次对话后档案卡被悄悄修正一次。这对Agent的个性化体验提升非常明显,也是我为什么在多个项目中优先选它的原因——它直接站在"数据管理"的视角处理记忆,而不是把记忆当成提示词拼接的一部分。
5.2 ADD、UPDATE、NOOP:记忆的增删改查
Mem0在处理记忆更新时,会对提取出的每条新信息做操作判定。大致有这几种结果:
- ADD:新事实,和现有记忆没有冲突,直接新增。
- UPDATE:新事实部分覆盖旧记忆,需要合并。比如用户之前说"我喜欢邮件通知",后来改口"我只在紧急时收邮件",系统应该更新而不是新增。
- DELETE:新信息表明旧记忆已失效。
- NOOP:新信息与现有记忆重复或价值不大,不动作。
这已经非常像数据库里的change data capture了。记忆不再是静态文本,而是有状态的动态数据。对生产级Agent来说,这个设计很重要,因为用户的偏好是会变的,如果系统只增不改,最终会积累一堆互相矛盾的"印象"。
我在实际集成Mem0时,最关注的其实是UPDATE的触发条件。它依赖LLM判断"新旧信息是否矛盾",而LLM对矛盾的理解是概率性的。所以我在记忆写入前加了一层置信度过滤:只有高置信度的信息才允许触发UPDATE或DELETE,低置信度的先存成草稿记忆。这个调整让我的Agent在记忆稳定性上好了很多,算是踩过坑之后的一个经验。
5.3 为什么Mem0比传统RAG更适合做Agent记忆
很多人把RAG等同于长期记忆,但我试用Mem0之后体会很深刻:RAG擅长"检索原文",不擅长"沉淀认知"。RAG取回的总是原始文本片段,而Agent记忆需要的是"关于用户的事实、偏好、画像"——这些信息往往散落在多轮对话里,需要被提炼和综合。Mem0的提取-合成机制,本质上是在帮Agent完成"从数据到知识"的加工步骤。
当然Mem0也不是银弹。它的提取环节依赖LLM能力,意味着你要为记忆构建支付token成本;去重和合并如果参数没调好,可能会把相似但不同的记忆错误合并。我在生产环境里用的建议是:给每条记忆加上来源时间戳和置信度,这样即使合并错了也能追溯。另外,如果你做的是知识密集型场景(比如企业知识库问答),Mem0的定位其实不太适合,它更适合"用户画像型"的个性化记忆。
6. Zep / Graphiti:时间感知的图记忆
6.1 为什么"记忆会过期"这件事很重要
前面讲的所有方案,都默认记忆是"写入后持续有效"的。但真实世界的记忆,是会过期的。用户上个月说"我喜欢A方案",这个月可能就变了;系统三天前记录了一个实体关系,今天这个关系可能已经解除。如果记忆系统没有时间概念,Agent就容易拿过期的旧印象回答新问题。
Zep早期版本主打对话摘要和记忆服务,后来团队推出了开源的Graphiti,方向移到"时序知识图谱"上。Graphiti的核心差异,不是存成图,而是给图里的每一条边都打上"有效时间"和"入库时间",形成双时序结构。这样,系统查询记忆时,可以根据时间窗口筛选出"当时确实生效"的信息。
举一个具体场景:一个企业客服Agent,用户三月份说要"升级到企业版",五月份又反馈"企业版账单看不懂"。如果记忆系统没有时间概念,Agent可能会把三月份的决策当成当前意图,推荐一堆升级引导;有了时间戳,系统就知道"升级决定是两个月前的事,现在用户的诉求是账单解释"。这就是"新鲜度"维度的价值。
6.2 识别实体、抽取关系、按时间窗口检索
Zep/Graphiti这类方案的管线大致是:
- 从对话中识别实体(人、组织、项目、产品等)和事件。
- 抽取实体间的关系,比如"Alice负责A项目"、"A项目使用了B框架"。
- 把实体作为节点、关系作为边,连同发生时间一并写入图数据库。
- 查询时,支持"在某个时间点,X和Y的关系是什么"这类带时间条件的图查询。
这套设计最典型的应用场景是客服机器人:客户在三个月内反复抱怨某个问题,图记忆能自动勾勒出"这个客户发起过哪些工单、每个工单指向哪个产品模块、有没有同类问题反复出现"的完整脉络。相比纯粹的向量检索,图记忆更适合做多跳推理和关系追溯。
这里有一个很关键的工程细节:实体识别和关系抽取的质量直接决定图谱的可用性。我用Graphiti做过一次小规模验证,发现如果抽取环节不做领域微调,图里会出现大量重复实体的变体,比如"Alice"和"alice@example.com"被当成两个节点。所以上图谱方案,一定要先做好实体对齐的预处理,否则查询结果会让你怀疑人生。
6.3 图谱型记忆的代价
不过图谱方案的门槛也明显。首先,图数据库(比如Neo4j)的运维和查询写法,比一套向量库重得多;其次,实体识别和关系抽取如果做得不好,图里全是噪声,查询结果反而比RAG还差;再者,记忆在这个体系里不是简单的文本,你需要专门设计一套工具API让Agent去查询图谱,这对小团队来说学习成本不低。
我个人的判断是:如果你的场景需要精准的时间线、实体关系和多人协同,图记忆值得投入;如果只是给单个用户做个性化推荐,Mem0和RAG的性价比会高得多。做选型时,不要因为"图谱听起来高级"就上,先想清楚你的需求里到底有没有"关系推理"和"时间回溯"这两个硬指标。
7. Cognee与RAG自建向量记忆:另一个极端的可行性
7.1 Cognee:图谱与语义的结合
Cognee是另一个值得关注的项目。它的定位是"可组合的记忆架构",简单说,它把非结构化的输入按层级处理成语义记忆、实体/事实/关系和可操作的程序性记忆,然后既做向量检索又做图谱遍历,让Agent可以从多个角度回忆。
它的设计哲学是不要绑定特定存储。你可以用向量库、也可以用图库,或者两者都接。对开发者来说,Cognee提供了更高层的记忆抽象,不用自己从零写实体抽取和向量化管线。不过,它也继承了图谱方案的复杂性和LLM抽取的不稳定性,生产使用前要把评估环节做扎实。
我试用Cognee的总体印象是:它更像一个"记忆中台"的雏形,适合团队想做一个统一记忆服务、但还不想从零造轮子的阶段。它的可组合性是一把双刃剑——灵活性高意味着配置项多,如果你对记忆系统没有清晰的认知,很容易在参数海洋里迷失方向。
7.2 自己用向量库搭一套最简记忆
如果你不想引入任何额外框架,最直接的长期记忆方案就是自己搭RAG:把每轮对话按窗口切片,用Embedding模型转成向量,存入向量数据库;每次Agent需要记忆时,把当前问题向量化,做相似度检索,取回最相关的几个片段,注入到系统提示词或上下文开头。
我实际跑通过这样一套简单管线,步骤不复杂:
- 对话分片:按轮或按固定长度切块,每块带上时间戳、对话ID、参与人元数据。
- 向量化入库:用OpenAI或开源的Embedding模型,把每块转成向量,存进Qdrant、Chroma或pgvector。
- 检索注入:用户发起新请求时,先用问题向量检索top-k片段,拼进上下文。
- 定期摘要压缩:当某段历史太长时,用LLM生成摘要,替换原始片段。
这套方案的好处是透明、可控、成本清晰,很适合团队从零起步。我记得第一次搭完,看到Agent能准确引用三天前的对话细节的时候,还是很有成就感的。但它的天花板也很明显:没有实体关系、没有冲突合并、没有过期机制,语料一多,检索结果会越来越碎片化,Agent会开始"记住了很多,但记不牢"。
7.3 自建方案的真实限制
我用这套RAG记忆做了一个内部工具的真实感受:它能应付"用户上个问题说过什么"这种短程记忆,但到了"用户在三个不同时间提过同一个需求的变体"这种中程记忆,就很容易错乱。原因很简单——相似度检索只保证语义接近,不保证逻辑一致。你取回的三段历史可能彼此矛盾,Agent也没有机制去仲裁谁对谁错。
所以,我的结论很明确:自建向量记忆适合作为"起步方案"和"兜底方案",但如果你对Agent记忆的需求超过了简单的"提词器",就一定要考虑Mem0这类带更新机制的提取派方案,或者Graphiti这类有时序图结构的方案。从成本和维护角度看,自建方案在数据量小的时候最划算,随着规模上来,你花在检索调优和去重上的时间,反而会超过直接用成熟方案的投入。
8. 我的选型建议:不同规模Agent该抄谁的作业
8.1 个人助手与POC怎么选
如果你是在做个人知识库助手或者一个用来验证概念的Demo,我最推荐:先用LangChain或直接自建向量记忆跑通主流程,再在关键路径上接入Mem0做重点记忆提取。理由很简单——起步阶段最重要的是让系统先能用、让人先看到记忆带来的体验变化,而不是一上来就把架构搞得无比完备。等到你确实验证了"长期记忆能让用户体验明显变好",再往生产级方案迁移。
8.2 生产级Agent怎么组合
如果一个Agent要面向真实用户长期运行,我的建议是按场景组合而不是单选一个:
- 原始对话和历史事实:交给Mem0或自建向量库,做语义检索和画像沉淀。
- 用户偏好变化与时效性要求高的场景:引入Graphiti这类时序图谱,答案才会"跟得上变化"。
- 长任务执行中的中间状态:交给LangGraph的checkpoint这类系统状态存储,不要让用户记忆和任务状态混在一起。
- 知识库文档问答:继续用RAG,但和用户记忆分开建索引。
这里最重要的教训是我踩过坑后才明白的:不要把"用户记忆"、"对话历史"、"任务状态"混在同一套存储里。它们的写读频率、更新策略、生命周期完全不同,塞在一起,后期几乎没有维护的余地。我们早期图省事,把历史对话、用户偏好、任务队列全塞进一个向量库,结果不到一个月,检索质量就开始崩,最后花了整整一周做数据迁移。分开存储,分开运维,长期看省力得多。
8.3 最后一点实操心得
如果让我给一个最靠谱的起步顺序,我会这样建议:先用RAG+向量库做简单历史召回,让Agent的"短期延伸记忆"先跑起来;然后花一周时间仔细看Mem0的源码或文档,把提取-更新-合并这套思路消化掉;最后,当你发现"用户画像随时间变化"真的影响体验时,再考虑上Graphiti这类时序图谱。
不要一开始就追求大而全。长期记忆本质上是产品能力的演变过程,不是一次技术选型就能一步到位的。我现在回头看,最有效的不是某个项目的具体代码,而是把它当成一套"从数据到知识、从知识到记忆"的系统工程来设计。每种方案解决一个维度的问题,组合起来,你的Agent才能真正拥有一个可持续生长的长期记忆。