ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Agent长期记忆设计:六大主流方案对比与工程落地

Agent长期记忆设计:六大主流方案对比与工程落地

做Agent开发的朋友应该都有这种体会:单轮对话里模型再聪明,一旦跨了会话,它就像个失忆的人,上次聊好的需求、你反复强调的偏好,全部归零。这个问题的核心,就是Agent的长期记忆。老实说,我带着团队做内部AI助手的那段时间,几乎所有让人抓狂的Bug都出在记忆上——不是模型不够聪明,而是它“记不住”,或者“记错了”。这篇文章想通过拆解六个主流项目的记忆设计思路,把Agent长期记忆这条线彻底捋清楚。文中的方案覆盖LangChain、LlamaIndex、AutoGPT、MemGPT/Letta、LangGraph和CrewAI,适合正在做Agent选型的技术负责人、被多轮对话记忆坑过的AI应用开发者,也适合想系统理解Agent内部机制的进阶学习者。

1. Agent记忆到底难在哪里:先看清问题再谈方案

1.1 短期记忆和长期记忆的分界线到底在哪

理解Agent记忆,最方便的办法是先对照人脑:人的记忆分工作记忆和长时记忆,Agent其实也一样。短期记忆由上下文窗口承担,也就是每次请求里塞进去的那段对话历史;长期记忆则是跨会话仍然需要保留的稳定信息,比如用户的喜好、项目的背景、上次任务的结果。

短期记忆的痛点在于窗口有限且昂贵。以主流大模型的上下文长度为例,即便窗口能装几万token,一旦对话轮次多了、中间还夹杂检索回来的资料,很快就会被塞满。塞满之后的处理只有两种:要么硬截断,把老早的内容丢掉;要么做摘要压缩,牺牲细节换空间。这两种方式本质上都是“拆东墙补西墙”,治标不治本。

长期记忆的难点就更明显了:它要解决存储结构、写入时机、检索策略、更新与遗忘这四个问题。存储结构决定信息放在哪,写入时机决定什么时候记,检索策略决定要用时怎么找回来,更新与遗忘决定旧信息如何被新信息覆盖。这四个问题任何一个没想清楚,做出来的记忆系统都会在真实场景里露馅。

生活化的类比是:没有长期记忆的Agent,就像一个只靠便利贴工作的同事。你每次跟他交代事情,他都要重新问一遍你的习惯和背景;你上次说过“报告用PDF格式”,这次他照样发Word给你。不是他态度有问题,是他根本没有一个“记录本”。

1.2 我为什么选这六个项目来对比

市面上的Agent项目很多,但记忆设计真正有代表性的,仔细筛下来就是这六个。它们分别代表了六条完全不同的技术路线:LangChain代表“模块化记忆管理”,LlamaIndex代表“索引化记忆”,AutoGPT代表“文件与向量库结合的外置大脑”,MemGPT/Letta代表“操作系统式的分层记忆”,LangGraph代表“图状态持久化”,CrewAI代表“多Agent共享记忆”。

选它们还有一个现实原因:它们在业界有足够多的生产环境部署案例,踩坑记录也多,不是停留在Demo阶段的项目。相比之下,一些新出的框架虽然概念很新,但实际跑起来问题还没暴露充分,参考价值反而不如这六个来得扎实。

先给一张总览表,后面再逐个展开:

项目核心记忆机制主要存储载体信息召回方式最擅长的场景
LangChain可插拔Memory组件内存、数据库、向量库直接拼接/固定窗口/摘要/向量检索对话助手、链式流程
LlamaIndex索引化文档记忆向量索引、文档存储检索增强生成RAG问答、知识密集型任务
AutoGPT文件+向量库外部大脑JSON文件、向量库文件读取/关键词/向量检索自主多步任务
MemGPT/Letta分层上下文管理内存页、归档存储工具函数换入换出超长对话、复杂推理
LangGraph图状态Checkpoint持久化状态存储状态恢复、时间旅行可控工作流、状态机
CrewAI三层记忆体系短期内存、SQLite长期存储、实体存储上下文压缩+检索多Agent协作

这张表大家先有个印象,下面我会逐个聊清楚,包括各自的代码长什么样、适合什么场景、有什么坑。

2. 六种主流记忆设计逐一点评:从LangChain到CrewAI

2.1 LangChain:把记忆拆成可插拔的模块

LangChain可能是大多数人接触Agent记忆的第一站,它的思路是“记忆是一个组件,想用哪种就插哪种”。早期最基础的是ConversationBufferMemory,实现方式非常直白:把历史消息一股脑全塞进Prompt。这种方案写起来简单,效果也直观,但一旦对话超过十几轮,上下文窗口就告急,费用也跟着涨。

后来LangChain演进出了几个变体,本质上分成了三个流派:固定窗口派、摘要压缩派、向量检索派。

固定窗口派是ConversationBufferWindowMemory,只保留最近N轮,确保上下文不超限,但代价是更早的信息完全丢失。摘要压缩派是ConversationSummaryBufferMemory,保留最近原始消息,更早的则压缩成摘要,兼顾了信息量和窗口上限。向量检索派是VectorStoreRetrieverMemory,每轮对话写入向量库,需要时用相似度检索取回相关片段。

这里有个值得注意的点:LangChain的设计哲学是“显式传递”。记忆状态是在每次调用LLM之前,由开发者自己拿到并拼进Prompt。好处是逻辑透明,坏处是记忆与业务逻辑耦合在一起,链一复杂,记忆流就乱。团队早期用LangChain做客服机器人,链里有检索、有工具调用、有回复生成,记忆模块放在哪个环节、取哪些历史,我们调了很久才稳定下来。

还需要提醒一句:LangChain传统的Memory模块现在逐渐被LCEL和LangGraph替代,官方也在把记忆职责下沉到状态管理里。但这不代表它的记忆思路没价值。恰恰相反,它总结出的“缓冲、窗口、摘要、检索”这四个关键词,至今仍是所有Agent记忆方案的四个基本招式。

2.2 LlamaIndex:把历史对话当成可检索的文档

LlamaIndex本来是做RAG(检索增强生成)出身的,所以它对记忆的处理方式也带着浓重的索引思维:对话历史也是文档,也应该被切块、嵌入、建索引,查询时通过检索器找回来。

它的核心组件是ChatMemoryBuffer,负责保存最近几轮对话,保证基本上下文;更重要的是MemoryRetriever,这个检索器会把整个对话历史向量化,当前问题来了之后,先从历史里召回最相关的片段,再交给LLM。这个设计巧妙在于:它把“记忆”和“知识库”统一了。你的业务文档、历史对话、用户画像,在LlamaIndex里都可以被当成同一套索引体系下的对象来处理,维护成本大幅降低。

不过统一也带来了一个问题:对话历史是高度口语化的,里面充满了“嗯嗯”“好的”“那你看看这个”之类的话,直接拿去建向量索引,检索质量会受影响。我的建议是,写入前先做一轮清洗,至少把无意义的语气词和重复内容过滤掉,否则检索出来的历史片段可能带着一坨噪声,反而干扰模型判断。

LlamaIndex的记忆设计比较适合知识密集型Agent。比如企业内部知识助手,用户问的问题往往需要结合他之前问过什么、看过什么文档来回答。这种情况下,单纯靠窗口里的上下文是远远不够的,必须从更早的历史里找回相关信息。索引化记忆在这里的价值就体现得很明显。

2.3 AutoGPT:用文件系统和向量库做外置大脑

AutoGPT作为自主Agent的开山之作,它的记忆设计带着浓厚的“野路子”色彩,但思路极其直白:我把重要的事情记到文件里,再用向量库做快速检索。

具体来说,AutoGPT的记忆分为几层。第一层是文件记忆,Agent每做几个步骤,就会把当前的目标、完成情况、遇到的错误保存到workspace里的文本文件里。第二层是向量记忆,任务过程中的重要事件会写入向量数据库,需要时通过相似度检索取回。第三层才是Prompt里的短期上下文。

核心思想可以概括为“反思即记忆”:Agent每完成一个子任务,会自己总结一下,写进记忆文件,下次启动时先读文件恢复状态。这套流程在跑一些多步骤任务时非常有用。我拿它跑过一个“研究某个行业并输出报告”的任务,第一天执行到一半中断了,第二天重新启动,它靠读取历史文件,直接从上次的进度继续,而不是从头再来,这个体验在当时是非常惊艳的。

但AutoGPT的记忆方案也谈不上优雅。最突出的问题是文件结构容易失控。跑几天之后,workspace里散落着几十个文本文件,没有清晰的目录层级,检索时要么靠关键词碰运气,要么得人工翻文件。而且它的记忆写入依赖LLM的自我总结,模型一旦总结偏了,后面的任务就像在一张错误地图上找路,越走越偏。

即便如此,AutoGPT仍然值得研究,因为它展示了一个很重要的思路:并不是所有场景都需要上复杂的数据库,把关键信息写成结构化文件,再配一个轻量检索机制,也能撑起一个可用的长期记忆系统。对小团队、轻量场景来说,这种方案反而是性价比最高的。

2.4 MemGPT/Letta:操作系统式的分层记忆

MemGPT(现已改名为Letta)是我个人最欣赏的一个设计,它的核心思想是用“虚拟上下文管理”来解决上下文窗口的物理限制。名字里的Mem可以理解为Memory,GPT就是大模型,整个项目把操作系统虚拟内存的概念搬到了Agent里。

它把记忆分成清晰的层级:核心记忆(core memory)、工作记忆(working memory)、归档记忆(archive memory)。核心记忆是最基础、必须始终在上下文里的信息,比如用户的身份、任务的固定约束;工作记忆是当前正在处理的任务状态;归档记忆则是暂时用不到、但未来可能需要的旧信息,放在外部存储里。

当上下文快满时,Agent会触发类似操作系统“缺页换入换出”的机制:把暂时不用的内容移到归档,把当前需要的从归档里调出来。这个过程由Agent自己通过工具函数完成,不需要开发者手动干预。这种设计让MemGPT在超长对话、复杂推理场景里非常突出。比如让Agent读一本几百页的书,然后连续追问细节,它可以从归档里精准调出相关章节的信息,而不会因为开头几章的内容被截断而“失忆”。

用计算机的类比来说:传统的上下文窗口像一块固定大小的内存条,塞满了就卡死;MemGPT则像给内存加上了虚拟内存机制,物理内存不够就往磁盘换页,对外表现是“内存无限大”。这套思路对现在的大模型应用非常有启发,因为模型支持的上下文再长,也是有限的,而真实世界的任务信息往往是无限增长的,分层管理是绕不开的方向。

2.5 LangGraph:用图的Checkpoint实现状态持久化

LangGraph是LangChain团队后来推出的一个项目,它的核心主张很明确:Agent应该被建模成一个状态机,而不是一串没有状态的链式调用。长期记忆在LangGraph里本质上就是“状态持久化”。

用LangGraph写Agent,你需要定义一个StateGraph,图中的每个节点是一个处理步骤,所有节点的输入输出都汇聚到一个统一的状态对象里。每执行完一个步骤,状态发生变化,你可以通过checkpointer把这个状态保存到持久化存储中。下次运行的时候,可以重新加载状态,也可以从某个历史时刻“分叉”出去重新执行——这个能力被称为时间旅行。

我拿LangGraph重写过一个多步骤的审批流程:Agent先理解用户提交的申请材料,再调用工具做事项核查,最后生成意见。整个流程的状态全部持久化,中间某一步因为外部接口超时挂了,重启后直接从失败节点继续,而不用让用户重新提交一遍。这种体验在业务系统里太重要了。

和LangChain传统Memory相比,LangGraph最大的区别是:记忆不再是“链旁边的附加组件”,而是“状态本身”。LangChain里你需要在Prompt里手动决定拼哪些历史内容;LangGraph里,所有键值对已经存在State里,需要哪个节点直接读。这套设计让复杂工作流的记忆管理变得可控得多,也天然支持并发任务之间的状态隔离。

当然代价是学习曲线更陡。引入状态机的概念之后,你得想清楚每个节点读什么状态、写什么状态,节点之间的数据依赖怎么定义。对简单对话机器人来说,LangGraph有点杀鸡用牛刀,但一旦流程复杂度上来,这种“结构化状态”带来的收益是非常明显的。

2.6 CrewAI:多Agent协作中的三层记忆

CrewAI走的是另一个方向:它要解决的不是单个Agent的记忆,而是一群Agent在一起工作时怎么共享和沉淀信息。它的设计里,记忆被拆成了三个层次:短期记忆、长期记忆、实体记忆。

短期记忆负责保存当前任务过程中最近几轮的上下文,跟LangChain的固定窗口差不多,作用是为当前任务提供连续性。长期记忆使用SQLite3存储,跨会话保留重要的经验教训和业务规则。实体记忆则负责记录用户、任务对象的属性和关系,比如“某用户偏好简洁回复”“某客户的合同下周到期”。

这三个层次在多Agent场景里意义很大。举个例子,一个研究团队里,研究Agent负责搜资料,写作Agent负责写报告,审校Agent负责检查质量问题。如果没有共享记忆,三个Agent各说各话,研究Agent搜回来的重点,写作Agent完全不知道,审校Agent也不清楚原始依据。CrewAI的做法是让这些Agent共同访问同一套记忆池,写作Agent可以检索到研究Agent沉淀下来的资料,审校Agent也能追溯结论的来源。

这套设计的局限在于信息一致性。多个Agent同时读写同一份记忆,如果没有良好的锁机制,容易出现数据竞争、旧数据覆盖新数据的情况。尤其当成百上千个Agent并行跑时,记忆池的吞吐会成为瓶颈。我在一个试验项目里用CrewAI跑过十几个Agent,短期记忆的读写压力还能接受,但在高并发场景下,需要给底层存储加上足够强劲的数据库支撑,否则响应延迟会肉眼可见地上升。

3. 落地选型与代码实现:抄一套能跑的长期记忆方案

3.1 按应用场景选择记忆方案,别盯着技术热度

很多开发者选型时会犯一个错误:哪家框架热就选哪家,结果做到一半发现它的记忆模型跟自己的业务不匹配。更合理的做法是,先想清楚你这个Agent每天面对的是什么样的交互密度和信息结构。

我按实际场景整理了一份选择对照,供参考:

具体场景推荐方案选择理由
客服/对话机器人,单会话轮次多MemGPT/Letta分层记忆能扛住超长会话,避免窗口爆掉
企业内部知识问答,要结合用户历史提问LlamaIndex对话历史和业务文档统一索引,检索能力强
多Agent协同完成项目任务CrewAI三层记忆天然支持团队共享上下文
业务流程状态强、需要断点续跑LangGraphCheckpoint持久化,故障恢复体验好
自主研究类任务,要长期跟踪进度AutoGPT式文件+向量库轻量、可控,不需要复杂基础设施
团队已有LangChain技术积累,快速起步LangChain Memory + 向量库学习成本低,组件灵活,适合先用起来

强调一句:选型不是单选题,很多成熟项目是多种方案的组合。比如底层用LangGraph管理状态,对话历史检索用LlamaIndex的向量索引,多Agent场景再叠加CrewAI的分工框架。架构上没有银弹,组合拳反而是常态。

3.2 从零实现一个带长期记忆的对话Agent

下面给一个最小可跑的示例,使用LangChain的VectorStoreRetrieverMemory,配合Chroma向量库和OpenAI嵌入模型。它能做到:把每轮对话写入向量库,下次提问时自动召回相关历史。

from langchain.memory import VectorStoreRetrieverMemory from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.llms import OpenAI from langchain.chains import ConversationChain # 1. 初始化向量库,作为长期记忆的存储载体 embeddings = OpenAIEmbeddings() vectorstore = Chroma( persist_directory="./memory_db", embedding_function=embeddings ) # 2. 构建记忆检索器,设置召回数量 retriever = vectorstore.as_retriever(search_kwargs={"k": 3}) # 3. 把检索器包成 LangChain 的 Memory 组件 memory = VectorStoreRetrieverMemory( retriever=retriever, memory_key="history", input_key="input" ) # 4. 创建对话链,每次调用自动读写长期记忆 llm = OpenAI(temperature=0.7) conversation = ConversationChain( llm=llm, memory=memory, verbose=True ) # 5. 第一轮对话,信息会被写入向量库 print(conversation.predict(input="我的名字是小张,我在做电商数据分析")) # 6. 第二轮对话,即使跨了会话,Agent 也能想起用户身份 print(conversation.predict(input="我之前提到过什么身份信息?"))

这段代码里最需要注意的是第3步。VectorStoreRetrieverMemory会自动把过去的对话存进Chroma,并在新请求到来时把最相关的历史片段注入Prompt。你不需要手动维护任何列表,长期记忆“读写”的闭环就完成了。

实际项目中,你可以把persist_directory换成支持并发的云数据库,比如PostgreSQL的pgvector或者Redis的向量模块,原理完全一样,只是存储层换成了更抗压的底座。

3.3 记忆工程必须调好的三组参数

光把代码跑通不算完,想让长期记忆在真实场景里好用,有三组参数你必须亲手调一遍,否则效果会很飘。

第一组是向量化时的分块大小。历史对话通常很长,写入向量库之前要切块。块切得太大,语义不聚焦,检索时容易召回大段无关内容;块切得太小,上下文碎片化,单个块的信息量不足。我的经验是256到512个token比较均衡,具体还要看你的平均句长,这需要花点时间做实验。

第二组是召回数量,也就是上面代码里的k值。k值太小,相关历史召不回;k值太大,噪声挤占Prompt空间。客服场景我一般用k=3到5,知识问答场景可以放宽到k=5到8。关键是要观察召回结果,宁可精准召回两三条,也不要用一堆似是而非的历史把模型绕晕。

第三组是相似度阈值。向量检索返回的结果中,有些历史片段跟当前问题的相似度并不高,放进去反而误导模型。建议设置一个0.7到0.8的相似度下限,低于阈值的直接丢弃,防止记忆系统什么都往外吐。这个阈值需要根据你的嵌入模型整体分布来调,不同模型之间的相似度分布差异还是很明显的,不能无脑套用网上的参数。

这三组参数调好之后,你的长期记忆系统才算是真正“能用”,而不是“能跑”。

4. 记忆系统最常见的5类坑:排查经验与修复技巧

4.1 记忆污染:检索出来的东西带动整个回答跑偏

症状很明显:Agent明明前面答得好好的,突然开始引用一段风马牛不相及的历史,把回答带偏。原因通常是向量检索召回了语义相似但事实上无关的旧片段,模型又把这段历史当成权威依据。

排查思路是先看Prompt里实际注入的历史内容,确认是哪条记录污染了回答。我排查过好几次,最后发现是用户历史对话中反复出现“退货”两个字,检索“退款政策”时把退货相关的抱怨也召回了。

解决办法有三层:第一层是提高相似度阈值,过滤低质量召回;第二层是在写入时增加元数据过滤,比如只写入用户真实陈述,不写入模型自己的碎碎念;第三层是在Prompt里给召回的片段打上“仅供参考”的标签,降低模型对历史的采信程度。

4.2 上下文爆炸:无论怎么压缩,窗口还是满了

这是做记忆系统最容易碰到的瓶颈。对话轮次一多,历史加上检索结果、工具调用结果、系统提示词,几万token的窗口说满就满。

我踩过最深的坑是只做摘要压缩,没做“分层”。摘要会把细节丢失,压缩到最后,Agent确实记住了对话的大概脉络,但用户几周前随口提过的一个关键约束,已经从摘要里消失了。

修复方式是采用MemGPT式的分层策略:短期记忆保留最近几轮完整对话,中期记忆保存按主题整理的摘要,长期记忆归档那些不太频繁使用的原始细节,需要时再精确检索。这三层写起来要多花一些功夫,但这是让系统长期不出问题的正确解法。

4.3 持久化失效:系统一重启,什么都记不住

表现是对话还在,但Agent一夜之间变成了“第一次认识你”。多数原因是向量库没有真正落盘,或者存储路径配置错误。

排查时先看向量库的persist_directory是否存在,再检查是否有权限写入。有一种不太容易被发现的坑是:进程在Docker容器里运行,重启后容器文件系统被重置,本地文件全部丢失。生产环境一定要把记忆存储挂载到外部卷,或者直接用云数据库,不要依赖容器内本地路径。

还有一个隐患是数据格式不兼容。切换了嵌入模型之后,旧向量和新向量无法统一比对,会出现“库里明明有记忆,但检索不到”的幽灵问题。解决方法是版本化你的向量数据,模型升级时做一次全量重新嵌入。

4.4 新旧记忆冲突:用户改主意后,Agent还在按老说法回答

这个问题的经典场景是:用户周一让你用“保守风格”写报告,周五改成“激进一点”,但Agent仍然时不时引用周一的那条记忆,导致输出风格前后矛盾。

根本原因是记忆系统没有“更新与遗忘”机制。技术上可以在每条记忆上打时间戳,检索时对新旧记忆设置权重;或者在做摘要写入前,先对已有高相似度记忆做一次覆盖检查,发现用户已经明确改口,就把旧记录标记为失效。

这个机制在业务系统里尤为关键。如果Agent把用户错误的历史偏好当成当前事实,产生的错误决策可能带来很大影响。所以一套好的长期记忆系统,不只是“记得住”,还要“忘得对”。

4.5 踩过坑之后,我的避坑清单

最后分享几条实战中总结出来的经验,算是一个避坑清单:

第一,检查记忆系统的第一步永远是看Prompt。大多数记忆问题,把实际发给模型的Prompt原样打出来,问题原因就浮现了,不用猜。

第二,记忆的写入要比读取更谨慎。写入垃圾信息,后面所有检索都是灾难。“先清洗、后入库”这条原则必须写进代码逻辑里。

第三,生产环境一定加监控。记录每天的记忆写入量、检索耗时、召回命中率,哪怕是一条简单的日志,都能让排查效率成倍提升。

第四,不要迷信某一个框架。记忆设计不是非黑即白,不同场景、不同阶段,方案需要持续演进。先跑起来,再迭代,用数据说话,比一步到位追求完美架构实用得多。

坦白说,这一路做下来,我的体会是:记忆系统做得越好的Agent,越像一个靠谱的长期协作者。它不需要每次都问一遍你的背景,不需要重复已知信息,做过的决策能延续下去,改过的想法也能及时吸收。这种体验上的差距,往往比模型本身的能力差距更影响用户对Agent的信任感。如果你正在做Agent相关的工作,希望这篇文章能帮你少走一段弯路。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进