Agent 长期记忆这个话题,最近在 AI Agent 相关社群里基本是必聊的。很多人把 Agent 当成一个能用工具、能调模型的“大脑”,但真正跑起来就会发现,它更像一个失忆的小金鱼——聊完一轮,下次对话就什么也不记得了。之前的偏好、中间结论、任务进度一旦消失,整个多轮交互都会变得很碎片。所以六个主流项目是怎么设计长期记忆的,就成了绕不开的问题。
这篇文章不是那种泛泛的概念科普,我会直接对比几个真实项目的实现思路:LangChain 的消息历史机制、CrewAI 的任务级上下文、Dify 的会话变量、AutoGPT 的经验存储、Mem0 的独立记忆层,以及 MemGPT/Letta 的“内存分页”设计。你会看到它们各自解决了哪一层问题,又在哪里偷了懒。如果你正在纠结“我的 Agent 到底该不该上记忆系统、该怎么接”,这篇就是给你的落地参考。
1. 先搞懂:Agent 的“记忆”到底分几层
1.1 会话内记忆与跨会话记忆
我见过不少新手把“长期记忆”直接理解成“把历史聊天记录都塞进上下文”。这个想法不能算错,但只覆盖了一层很浅的需求。站在工程实现的角度,记忆大体可以拆成两类。
一类是会话内记忆,也就是多轮对话里同一次任务中传递的上下文,比如用户刚说要查天气,Agent 先问城市,再问日期,这里城市和日期就属于会话内的临时状态。另一类是跨会话记忆,也就是用户第一次对话时说“我一杯美式加两块糖”,第二次再来点单,Agent 依然记得这个口味偏好,这属于跨会话的长期信息。
这两类记忆在技术上完全是两码事。会话内记忆通常放在上下文窗口里,跟着大模型的 token 一起走,简单直接,但代价是消耗 token、长度受限。跨会话记忆则需要外部存储,需要检索、需要写入策略,甚至还要考虑“哪条信息值得记住、哪条信息该被淘汰”。大多数 Agent 框架其实只在会话内记忆上做了很好的支持,跨会话记忆往往是后加的模块。这也是很多新手觉得“我的 Agent 记忆很蠢”的根源——它根本没有一个真正的长期存储层,只是把所有东西都堆在上下文里。
1.2 长期记忆的核心难题:遗忘与召回,而不只是存储
如果长期记忆只是“存下来”,那这事就太简单了,Redis 里放个 JSON 不就完了。真正复杂的是两件事:该记什么、以及该忘什么。
先说该记什么。用户每轮对话可能有大量冗余内容,比如“我先试试”“哦不对”“改成明天吧”。如果把这些全部写进长期记忆,存储会爆炸,检索也会被噪声淹没。所以必须要有一套机制来判断信息的“记忆权重”——是用户的身份偏好、任务的关键参数,还是临时修正的指令,这几类信息的价值完全不同。
再说该忘什么。记忆久了必然面临过期问题:用户三个月前喜欢喝热饮,今天突然说想喝冰的,如果旧记忆没有被覆盖或衰减,Agent 就会拿过时的偏好来服务新请求。好的长期记忆系统一定配套了更新与衰减策略,而不是只进不出。
另一个难点是召回。就算你存了 10 万条用户偏好,真正能派上用场的可能只有其中三到五条。如何在海量历史中找到这五条,而且要在几百毫秒内完成,这就依赖向量检索、重排、混合搜索等手段。存储只是地基,召回才是决定用户体验的上层建筑。很多项目之所以记忆效果差,不是没存东西,而是查不出来。
2. 六个主流项目怎么选:横向对比框架
2.1 一眼看懂的设计偏好
在看单个项目之前,我建议你先建立一个对比框架,不然容易被各家的术语绕晕。我自己的判断维度有三个:记忆的载体放在哪、写入时机是什么、召回方式是什么。
载体决定了记忆是跟着链路走,还是独立存储。有些框架把记忆放在会话对象内部,链路结束就销毁;有些框架把记忆沉淀到外部数据库或向量库,做到持久化。写入时机决定记忆是每次对话都写,还是只在任务结束时写,这直接影响存储量和决策延迟。召回方式也各不相同,有的是全量注入上下文,有的是按相似度检索,有的是靠规则提取关键字段。
还有一个隐藏维度是“记忆的可解释性”。比如 LangChain 的 memory 对象,你随时可以打印出来看里面存了什么;但 AutoGPT 的经验文件是一堆自然语言文本,它想起了某段经验后,你是很难判断这段经验对当前决策是否合适的。这些都直接影响你调试 Agent 的难度。
2.2 六项目的定位与核心差异速览
我直接给一个表格,方便你快速建立整体认知:
| 项目 | 记忆定位 | 存储载体 | 写入时机 | 召回方式 | 适合场景 |
|---|---|---|---|---|---|
| LangChain | 链路级记忆组件 | 内存、Redis、向量库等 | 随消息历史写入 | 全量截断或向量召回 | 快速给链加入多轮能力 |
| CrewAI | 任务级上下文记忆 | Crew 内存(短期/长期) | 任务执行过程中 | 按任务/实体检索 | 多 Agent 协作流水线 |
| Dify | 应用级会话变量 | 平台内置存储 | 对话流程中按节点写入 | 变量直接引用或检索 | 低代码搭建应用 |
| AutoGPT | 经验型长期记忆 | JSON 文件 | 任务成功后总结 | 按描述字符串匹配 | 自主执行长周期任务 |
| Mem0 | 独立记忆层 | 向量库 + KV 存储 | 对话后异步提取 | 混合检索 + LLM 提取 | 给任意 Agent 接入统一记忆 |
| MemGPT/Letta | 分层内存代理 | 内存分页管理 | 自主决定换入换出 | LLM 判定 + 工具调用 | 超长对话、自治 Agent |
这张表的核心信息是:每个项目的记忆设计都不是随便拍的,而是服务于它的产品定位。LangChain 是通用框架,所以记忆做成可插拔;CrewAI 强调角色分工,所以记忆跟任务绑定;AutoGPT 是自主 Agent,所以记忆要支撑长期任务,但不能太重。理解了这一点,你选型时就不会只看“谁有记忆”,而是看“谁的记忆模型匹配我的业务”。
3. 逐个拆解:六种长期记忆设计思路
3.1 LangChain:把记忆当作可插拔的链上组件
LangChain 大家都很熟,它的记忆设计思路是“组件化”。在 LangChain 的早期版本中,记忆就是一个 ConversationBufferMemory,作用是把对话历史塞进下一次 prompt,相当于把短期记忆平移到上下文窗口里。后来演化出了 ConversationBufferWindowMemory(只保留最近 N 轮)、ConversationSummaryMemory(用一次额外 LLM 调用做摘要)、ConversationSummaryBufferMemory(最近内容原文保留 + 更早内容摘要化)等变体。
LangChain 的工程优势是抽象干净:Memory 接口和 Chain 解耦,你可以随意替换。同时它也暴露了一个很现实的问题——绝大多数字段记忆策略只是“截断+摘要”,并没有真正的长期推理能力。你不能指望 ConversationSummaryMemory 理解用户的深层偏好,它只会忠实压缩文字。
实操中我建议:如果只是给一个客服机器人加多轮能力,用 ConversationBufferWindowMemory 就够,别上摘要,省一次 LLM 调用;如果对话超过 20 轮,再考虑 Summary 或 BufferWindow。但你要清楚,LangChain 的记忆组件至今缺少“记忆写入策略”,它不会判断哪句话值得长期记住,只做机械的历史记录。所以不要把 LangChain 的 memory 当成真正的长期记忆,它更接近“上下文管理工具”。
3.2 CrewAI:任务上下文中的短期与长期协作记忆
CrewAI 是我认为在记忆设计上“概念最像企业团队”的框架。它把 Agent 组织成 Crew,多个 Agent 围绕任务协作。它的记忆分为三类:短期记忆(ShortTermMemory)、长期记忆(LongTermMemory)和实体记忆(EntityMemory)。
短期记忆负责存储当前任务的上下文,比如某个 Agent 刚查到的数据库主键;长期记忆存储从一个任务中学到的经验,比如“处理这类报表时,客户通常偏好 Excel 而非 PDF”;实体记忆存储关键实体及其属性,比如“供应商 A 的交付周期是 5 天”。这套设计的精妙之处在于:它把记忆按用途拆分,而不是一锅端。Agent 在检索时,会按语义相关度从长期记忆和实体记忆中召回有用的片段,再注入上下文。
但 CrewAI 有明显的边界:它的记忆是围绕“任务执行”存在的,不是围绕“用户”存在的。如果你的业务是一个用户长期使用你的产品,需要记住用户的偏好,CrewAI 记忆并不擅长,它的长期记忆更偏向团队工作知识的沉淀。实操中我通常用它做内部流程自动化的 Agent 团队,而不是面向 C 端用户的助手应用。你用 CrewAI 做客服 Agent,会感觉到它确实很适合工具调用和多 Agent 协作,但用户画像会被放在任务记忆之外,需要自己额外补。
3.3 Dify:用会话变量实现低代码长期记忆
Dify 这类低代码平台,被很多产品经理拿来搭应用。它的记忆设计非常朴素直接:会话变量(Variable)。在 Dify 的工作流里,你可以定义一个全局变量,比如 user_preference,然后在一个节点里提取用户的偏好写入该变量,后续对话节点再直接引用变量来生成回答。
这种方式的优点是直观、可控、可调试。你在画布上就能看到变量在每个节点的流转,出了问题也能快速定位:是提取节点的 prompt 不够准,还是变量写入时机不对。Dify 还支持在应用设置里配置“长期记忆”,自动存储 AI 总结出的用户关键信息,并在新会话里注入到上下文中。
缺点是:它严重依赖你的流程设计能力。低代码平台不会替你思考“哪些信息该记住”,需要你主动在流程中加入“提取记忆节点”。遇到复杂记忆场景,比如记忆的分页、多条相似记忆的合并,Dify 很难优雅处理。我建议产品同学用 Dify 做 MVP 验证时,把记忆设计的重点放在“变量名规范化”和“提取节点的提示词”上,这两个地方决定了记忆质量的上限。比如,变量名别叫 a,要叫 user_preference_shipping_address,否则后期维护会让你想哭。
3.4 AutoGPT:把“经验”写进文件仓库
AutoGPT 是 2023 年很火的概念级项目。它的长期记忆设计思路是所有项目里最“朴素且大胆”的:把执行过程中学到的东西以文本形式写入一个 JSON 文件,并在后续任务中用向量嵌入做相似度召回。
AutoGPT 的记忆是一个名为 MemoryManager 的模块,底层是 JSON 文件存储 + 向量索引,每个记忆条目都有 description、content 和 embed 字段。它的思路是:任务跑完后,把“学到了什么”总结成自然语言,存到一个免费商用模型生成的向量里,下次遇到相似任务时,通过向量相似度找到相关经验,然后作为额外上下文注入给 LLM。
这套设计的优点在于实现简单、零运维成本;缺点也很致命:记忆质量完全取决于总结 prompt 的质量。如果总结太笼统,召回就没有意义;如果总结太细节,存储又会膨胀。而且 AutoGPT 的记忆“写入时机”是任务完成之后,如果任务中途崩溃,这段经验就丢失了。实操中,我很少直接拿 AutoGPT 做生产应用,但它的“经验文件”思路特别适合个人项目的冷启动,你可以把一个 AutoGPT 的 JSON 文件当作一个可读的日志,观察 Agent 到底记了什么,再调整你的总结提示词。
3.5 Mem0:独立于框架之外的记忆中间层
Mem0 是近一年开始走红的项目,定位是“给任何 AI Agent 提供统一的记忆层”。它不是一个 Agent 框架,而是一个可以独立部署的记忆服务。它的核心思路是:对话结束后,记忆服务异步提取关键信息,写入向量库 + KV 存储;新请求进来时,服务先从记忆中检索相关信息,再把它们注入到 prompt 里。
Mem0 的设计思路更适合拿来对比学习,因为它做了一个非常关键的动作:记忆与 Agent 解耦。你用 LangChain、CrewAI、甚至自己手写的循环都无所谓,记忆在整个架构里变成了一个独立中间件。这带来一个好处:记忆写入和召回的升级,不需要修改你的 Agent 主流程,只需要调整 Mem0 的策略。而且 Mem0 默认支持了添加、更新、删除记忆等原子操作,比 LangChain 那种固定窗口方式灵活得多。
Mem0 的召回策略是混合检索:向量相似度 + 关键词过滤 + LLM 重排,并且按记忆条目的类型(事实、偏好、任务)、时间衰减因子做综合排序。这种设计很贴近真实需求,因为用户偏好常常是动态变化的,旧偏好需要被新偏好覆盖。实操中我在自己的项目里集成 Mem0 比较多,基本的接入方式是通过它提供的 API 接口:在对话循环结束后调用 add 接口写入记忆,在生成回答前调用 get 接口查询相关记忆。
3.6 MemGPT/Letta:把大模型当操作系统,给记忆做分页
MemGPT(后来项目改名叫 Letta)是我个人觉得最有想象力的一种设计。它的灵感来源是操作系统里的虚拟内存分页:当记忆不够用时,把一部分记忆“换出”到大容量外部存储,需要时再“换入”到上下文中。它不把长期记忆当成历史消息的存储,而是当作一个受管理的分层内存系统。
MemGPT 内部维护两条记忆通道:外部存储和上下文窗口。它教 LLM 使用“记忆管理工具”,例如 core_memory_append、memory_replace、memory_search,让模型自主决定:当前信息应该写入核心记忆,还是归档到外部存储;某个记忆已经不重要了,应该从上下文里移除。这就把“记忆策略”的执行权交给了模型自己,使得它在超长上下文的场景下表现得特别灵活。
这种设计思路的迁移价值很高:即使你不用 MemGPT,也可以借鉴它“记忆分页”“记忆换入换出”的概念,在 Prompt 里约定好分级记忆的规则,让 Agent 看到一个长对话时自动做摘要与归档,而不是把所有原始文本塞进上下文。实操中,如果我要完成一个需要跑 50 轮以上工具调用的 Agent,我大概率会考虑尝试 Letta 或参照它的思路自己封装,因为传统的截断策略在长周期自主任务中会丢失大量有效状态。
4. 实操演练:如何用一般化的思路给 Agent 接入长期记忆
4.1 低成本方案:对话摘要 + 外部存储
如果你暂时不想引入重型框架,我给你一套“手工记忆”方案,适用于原型验证或小流量应用。核心思路是两条:摘要替代原文,存储替代上下文。
具体步骤是:第一,每次对话结束时,把当前会话的完整历史交给 LLM 做一次摘要,提炼出用户的关键事实、偏好、未完成任务。第二,将这段摘要以 JSON 格式写入本地文件、数据库或 Redis。第三,新会话开始时,读取最近 N 次会话的摘要,合并去重后拼接到系统 Prompt 中。这套方案没有用到向量库,但已经可以实现跨会话记忆。我自己的一个宠物店预约助手就是这么做的,记忆体量不大,每轮摘要也就 200 字,完全够用。
为了让摘要更有效,我会用固定的摘要模板:“用户ID、核心偏好、当前任务状态、待确认事项、过期时间”。固定模板的好处是结构化,方便后续解析与合并。要注意的是,不要用全文历史作为摘要输入,那会白白消耗大量 token;一般只需要最近 3 轮对话的原文和上一条摘要,这样 LLM 既能理解最新变化,又不会遗忘前文。
4.2 向量库方案:在记忆里做相似度召回
当摘要型记忆无法满足需求,或者历史记忆条目超过几百条时,就该上向量库了。比较稳妥的流程是:
第一步,设计记忆条目粒度。不要一大段存一个条目,那检索时会“打偏”;建议把一条记忆控制在 50 到 200 字之间,例如“用户偏好:阿航喜欢中等酸度的手冲咖啡,偏好柑橘调”。第二步,每个条目生成 embedding 向量,存入向量数据库。这里注意,embedding 模型要用一个不随模型升级变化而变化的稳定版本,否则历史向量与新向量维度不一致会导致检索异常。第三步,用户新提问时,先用向量相似度找出 top 10 记忆候选,再通过一个轻量 LLM 做一次重排,筛选出真正相关的 3 到 5 条。最后把这几条记忆格式化后放进 Prompt。
向量检索其实很容易踩“相关性假象”的问题——语义相似但不一定有用。比如用户问咖啡店营业时间,向量库可能会召回一条关于“咖啡烘焙程度”的记忆,因为都和咖啡相关。所以一定要在召回的环节加上“任务类型”或“实体名称”的过滤条件。我自己比较习惯在写入时就给记忆打标签,比如“user_preference#coffee#roast_level”,这样召回时就能用标签做前置过滤。
4.3 记忆的更新与淘汰:别让垃圾记忆拖垮效果
长期记忆最容易被忽略的就是“更新与淘汰”。很多人只做加法,不做减法,结果记忆库越积越乱。我建议你在顶层设计时就定三条规则。
第一条,同实体同属性只保留最新值。例如用户偏好里同时存在“喜欢冰美式”和“最近改喝热拿铁”,后者应覆盖前者。这里不一定非要做语义判断,可以先做一个结构化规则,按“实体名+属性名”定位覆盖。第二条,设置遗忘周期。每条记忆写入时可以带一个 ttl 字段,比如 30 天、90 天,到期后不再自动召回。如果用户再次提到相关事实,则刷新 ttl。第三条,定期做记忆质量复审。可以用一个离线脚本跑批量任务,对记忆库中召回率低、点击率低(如果有反馈)的记忆做删除归档。这一条更像“知识库运营”,越用越聪明的前提是定期清理。
不淘汰记忆的话,你会遇到一个很典型的症状:Agent 的回复慢慢变得“又老又新”,总是在关键决策里混入过期的偏好,导致用户明显感觉到不对劲。这时候别急着怀疑模型,先查记忆库里的脏数据,大概率是历史记忆在干扰。
5. 记忆接入时最容易踩的坑与排查技巧
5.1 新老记忆冲突怎么处理
最常见的问题是:用户上次说“我不吃香菜”,这次说“帮我换一份多加香菜的汤面”。Agent 依然记住了旧偏好,导致“不吃香菜”被注入上下文,和新指令产生冲突。这种问题根源在记忆管理缺乏“与当前指令的优先级判断”。
我的处理办法是:在系统 Prompt 里明确写一条规则,“如果记忆中的信息与当前用户明确陈述的指令冲突,以当前指令为准,且立即更新记忆”。这等于给记忆加了一个“服从当前指令最高优先级”的开关。另一个技巧是召回记忆时,过滤掉用户明确修正过的实体。比如维护一个“用户修正记录”,一旦检测到语义冲突,就把旧记忆标记为“需更新”,并异步重写。实操里,我用一个很简单的 Prompt 模板检测冲突:“总结以下当前对话与历史记忆之间的冲突点,并生成新的记忆条目”。让 LLM 做一步轻量判断,准确率相当高。
5.2 记忆膨胀导致上下文劣化
很多 Agent 会在第 10 轮以后,回复质量肉眼可见地下降,不是因为模型变笨,而是因为塞进上下文的记忆太多、太杂。这属于“上下文污染”或“信噪比下降”。
排查时先看日志:打印最终发送给模型的 Prompt,统计记忆所占的字符比例。如果记忆占比超过 50%,那大概率是记忆召回策略太宽松了。把 top K 从 10 降到 5,并提高相关度阈值;同时把“最近更新”作为一个排序因子,宁可少召回,也不要召回错的。我自己的实践建议是:把记忆内容控制在总 Prompt 的 30% 以内,其中核心任务相关指令至少占 50% 以上,抓大放小,Agent 会靠谱很多。
5.3 多 Agent 之间的记忆隔离与共享
如果项目里有多个 Agent 协作,你肯定会面临一个问题:是否要共享记忆。共享过头,Agent A 的隐私数据会泄露给 Agent B 的上下文;完全隔离,又会重复收集用户信息,效率极低。
我的建议是建立三层权限模型:全局共享层(比如用户的基础身份信息、全局偏好)、任务共享层(比如当前订单的状态,只对参与任务的 Agent 开放)、私有层(每个 Agent 自己的私有上下文,不可跨 Agent 读取)。实现上,你可以在记忆库的条目上加一个字段 access_scope,例如 global、task、private,在召回时过滤。这套模型特别适合用 CrewAI 或自研编排框架搭建的多角色系统,能有效避免“记忆越权”。实操中,我用 Self-RAG 的风格做了记忆访问判定:在召回后,让 LLM 快速判断这条记忆是否能被当前角色访问,能通过规则约束的绝不交给模型判断。
5.4 冷启动:记忆为空时怎么办
新用户第一次来,记忆库当然是空的。这时候如果硬要召回,往往什么也查不到,导致 Agent 没有上下文可用。比较好的做法是提供一个“默认先导记忆”的机制,比如通过用户画像问卷、登录态数据、或 Cookie 里的最近偏好来初始化记忆。
我在一个旅行规划 Agent 里就做了这个:用户没开口前,先通过 IP 归属地或历史订单,写入一条“用户可能所在地”的记忆,然后基于这条记忆做一个轻量的主动对话。这样用户感知上会觉得这个 Agent 好像“懂我”,而实际上只是初始化了一个很弱的先验记忆。冷启动时还要注意:不要让 Agent 假装知道它不知道的事。记忆为空时明确说“了解得还不多,推荐几个大众选项”,比硬编一些通用的、冒犯性的假设要好得多。
6. 把记忆设计落地的最终建议
如果看完全文你只记住三件事,我强烈建议你记住这三条:第一,长期记忆不是上下文窗口的扩展,而是独立的存储、召回、更新系统;第二,记忆一定要分级分层,在这个维度上,MemGPT 的分页思路和 CrewAI 的多层记忆结构都值得参考;第三,好的记忆设计要让“遗忘”成为一等公民,过期、冲突、无用的数据必须能被机制主动清理。
我最近的几个项目都开始统一采用这种模式:用一套独立记忆服务(类似 Mem0 的定位)承载所有用户记忆,上层 Agent 框架随意换,记忆服务保持稳定,外层业务逻辑只负责定义“哪些信息值得记住”。这套模式适合已经在做 Agent 落地,但对记忆质量还不满意的团队。
最后分享一个很小的实战细节:在你给 Agent 接入记忆之前,先用一个没有任何记忆的空白版本和一个有记忆的版本,跑同一组 20 条测试用例,对比两次输出质量差异。很多人接完记忆发现回答反而变差了,原因可能在于记忆检索噪声大于收益。小样本对比测试能让你明确感知到记忆接入的边际收益,再决定是否值得投入更多研发精力。