ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Agent长期记忆设计深度拆解:六大框架对比与落地实践

Agent长期记忆设计深度拆解:六大框架对比与落地实践

如果你做过Agent开发,大概率撞过这堵墙:Agent在单轮对话里表现惊艳,逻辑清晰,像个资深工程师;一旦任务拉长到几十轮,或者跨天复用,它就开始选择性失忆——昨天刚确认的偏好、上周攒下的关键决策,全都跟没发生过一样。用户问一句"我之前不是说过吗",你只能尴尬地复位上下文。这不是模型笨,而是Agent缺少一套"长期记忆"机制。

这个问题的本质,在于大模型是无状态函数:每次调用都是独立的一次前向计算,模型本身不保留任何跨调用的信息。所谓"记忆",完全靠开发者在外围搭一套读写系统来模拟。我这两年观察下来,市面上的Agent框架基本都在做同一件事——把记忆从"上下文窗口"里挪出去,再用各种手段按需拿回来。只不过每一家对"记忆是什么、什么时候写、什么时候读"的理解完全不同。

这篇文章我想把六个主流项目的长期记忆设计思路放在一起横向拆一遍。它们分别是MemGPT/Letta、LangGraph、AutoGPT、CrewAI、OpenAI Assistants API、Claude Code。这六个项目各自代表了Agent记忆设计的一条独立技术路线,你看完会发现:没有银弹,只有取舍。

1. 先搞清楚Agent"记不住"到底卡在哪

1.1 上下文窗口不是硬盘

很多刚入门的开发者会误以为,模型context window那么大,把历史消息全部塞进去不就完了?这个思路短期有效,但本质上是在用"记忆力"冒充"记忆"。Context window是一个临时工作台,不是持久化存储,它有三个天然上限:

第一,容量上限。即便长上下文模型能做到200K甚至1M token,一次常规Agent任务对话几十轮后,消息累积很快就会顶到窗口边缘。这时候你面临两难:要么粗暴截断早期消息,丢掉关键信息;要么继续膨胀,延迟上升、成本暴涨。

第二,成本上限。LLM按token计费,历史消息每次全量重发,等于每一轮都在为过去的对话重复买单。一个跑几十轮的Agent任务,真正有信息量的可能只有几轮,但你付出的却是全部轮次的token成本。

第三,注意力稀释。模型的能力在长上下文上会衰减,业界管这叫lost in the middle。把几千条历史消息密密麻麻堆进prompt,模型对真正重要的那个细节反而反应不过来。我有一次在实测中,把某个决策埋在第30轮对话里,后续对话中再问,模型居然给出了相反结论——它就是"看漏了"。

1.2 长期记忆缺失引发的连锁问题

抛开技术细节,从产品视角看,没有长期记忆的Agent会暴露出很明显的短板:

  • 跨会话断裂。用户周一跟Agent梳理了项目背景,周二再开一个新会话,Agent完全不记得。用户得把背景重新讲一遍,这个体验用"灾难"来形容不为过。
  • 无法积累经验。Agent今天在某个任务上踩了坑,明天遇到同类任务,它没有任何机会记录这个坑,于是继续踩。你是在用一个永远没有成长的新员工。
  • 多Agent协作困难。一旦任务拆给多个Agent执行,A执行完的关键结论如果不落盘,B就完全不知道。团队协作变成了"各干各的、然后互相问发生了什么"。
  • 没有个性化。用户偏好、习惯、历史决定,全部需要每次重新灌输。真正的个人助手,前提就是得"记得我是谁"。

这四点合起来,决定了长期记忆不是锦上添花,而是Agent从Demo走向生产环境的必经之路。有了这个共识,我们再来看六个项目各自给出的答案。

2. 六强名单:为什么是这六个项目

Agent记忆设计这个赛道,项目其实很多,但大部分方案都在互相借鉴。我特意挑了六个代表性项目,背后的选择逻辑是:它们分别覆盖了Agent记忆设计的几条主流路线。

  • MemGPT/Letta:把操作系统虚拟内存的分层思路引入Agent,记忆管理内化为Agent技能。这是理论最完整的一条路。
  • LangGraph:用状态机与持久化Checkpointer实现记忆,工程化程度最高,是生产级Agent最常选的路。
  • AutoGPT:最早把"外部向量数据库+相似度检索"用到Agent长时记忆中的项目之一,是检索式记忆的开山代表。
  • CrewAI:专注多Agent协作,把记忆按用途切成多类,是团队视角的设计范本。
  • OpenAI Assistants API:官方托管方案,用Thread机制解决存储问题,是零运维路线的代表。
  • Claude Code:把记忆外化成人类可读的Markdown文档,是可审计记忆的典型。

这六个放在一起,基本覆盖了当前Agent记忆设计的所有核心分支。你不管在哪个框架上做开发,都能从这里找到对应思路。

3. 六套记忆架构的逐项拆解

3.1 MemGPT/Letta:把记忆管理变成Agent的内建技能

MemGPT第一次提出时,用了一个非常形象的类比:大模型的上下文窗口相当于操作系统主存(RAM),外部存储是磁盘(Disk)。普通程序内存不够时会做分页换入换出,Agent的记忆也应该这样分层管理。

在这个设计里,记忆被分成两个层级。主上下文(in-context)就是Agent当前工作状态,承载核心系统提示和常用记忆块;外部上下文(external context)则放在向量数据库或结构化存储里,可以无限扩展。关键点在于,谁来决定把哪段记忆换入换出?答案是Agent自己。MemGPT把记忆管理包装成了LLM可以调用的工具:比如core_memory_append、archival_memory_insert、archival_memory_search。Agent会自己判断"当前对话是否需要回忆之前的内容",然后主动调用检索。这相当于赋予了Agent自我管理记忆的能力,这也是它跟普通向量检索方案最大的区别。

Letta是MemGPT的工程化落地版,提供了完整的REST API和Agent运行时。我实际用下来,Letta把记忆分成了两块:core memory(用户、人格、人类等固定模块的可编辑记忆块,始终在上下文中)和archival memory(向量库存储的可检索记忆)。对话中Agent产生的新知识会被提炼后写入archival,而用户偏好在初始化时固化到core。这个方案在长期人设稳定、需要跨会话保留用户偏好的场景里,是我见过效果最扎实的。

但代价也明显:Agent自己决策"要不要回忆",会引入额外的工具调用和token开销,而且当检索结果本身不精准时,Agent可能会被带偏。

3.2 LangGraph:状态机视角下的可持久化记忆

LangGraph的哲学不太一样。它把Agent建模成一个图:节点是执行步骤,边是步骤间的转移,而记忆被统一抽象为State(状态)。状态在图里的每一个节点间显式传递,节点执行前读状态、执行后更新状态。这样,记忆不再是"往prompt里塞历史",而是整个执行过程中维护的一份持久化数据。

LangGraph的长期记忆落点主要靠两个机制。第一个是Checkpointer(检查点),它把每个线程的状态快照持久化到外部存储,如SQLite、Postgres、Redis。你给同一个thread_id的会话继续发起请求时,框架会自动加载上一轮的状态,对话就像没断过一样。这种持久化还带来了一个我非常喜欢的能力——time travel:你可以回溯到任意历史检查点,分叉出新的执行路径。调试多轮Agent任务时,这功能能救命。

第二个机制是自定义Reducer。LangGraph允许你对状态的每个字段定义合并规则,比如消息列表用add_messages追加,而某个业务字段可以自定义"后写覆盖先写"。这套机制的好处是,记忆存储可以高度结构化:消息归消息、业务状态归业务状态、长期摘要归长期摘要,互不干扰。生产项目中我见过很多团队直接用Postgres做checkpointer存储,配合一个向量库做语义检索,效果很稳。

LangGraph的短板在于它不替你解决"记忆内容怎么组织"的问题。它给你存储骨架和状态管理,但什么是值得长期保留的、什么时候该压缩历史,都得开发者自己设计。比起MemGPT,LangGraph少了一点"智能",但多了一大截可控性。

3.3 AutoGPT:向量检索式"外部大脑"

AutoGPT是早期自主Agent浪潮的代表项目。它当时的长期记忆设计很简单直接:对话过程中产生的关键信息被向量化后存入外部向量数据库(早期常用Pinecone),需要时根据当前问题做相似度检索,把相关片段拉回来注入prompt。这个思路现在大家都很熟了,但在当时是相当超前的。

AutoGPT的Memory组件里有两个核心方法:pin和recall。pin是把某条记忆标记为重要,始终保留或优先注入;recall则是给定一个查询,从向量库里检索最相关的记忆片段。这个"pin/recall"两级模式很有启发性:pin负责保住确定的、关键的信息,recall负责按需探索。可惜AutoGPT在实际实现中,既没有对写入内容做重要性过滤,也没有对检索结果做相关度阈值控制。结果就是它经常把一堆相关性不高、价值有限的碎片文本全塞进prompt,反而稀释了模型注意力。这也是后来很多人对"向量检索式记忆"失望的原因——检索到了不代表用得上。

不过AutoGPT的意义在于验证了一条路线:外部向量库 + 相似度注入,这个组合简单、可落地,而且特别适合知识型Agent(比如需要从文档库里找答案的助手)。它的问题不在路线本身,而在实现精细度。

3.4 CrewAI:多智能体场景的分级记忆

CrewAI是个聚焦多Agent协作的框架。在单个Agent的记忆设计上,它把记忆分成了四类,这是我看过的框架里对"记忆"切分最细的:

  • 短期记忆(Short-term Memory):当前任务执行过程中的上下文,相当于工作区的临时数据。
  • 长期记忆(Long-term Memory):跨会话持久化下来的经验,存储在向量数据库中。
  • 实体记忆(Entity Memory):从对话中抽取的实体及其属性关系,比如"客户A的偏好是X"这样的结构化信息。
  • 工作记忆(Working Memory):Agent当前任务的中间产出物,比短期记忆更偏向于"正在处理的数据"。

这个分类的逻辑是:不同性质的记忆,生命周期和存取频率完全不同。实体记忆适合结构化存储和快速查改,长期记忆适合向量化后按语义检索,工作记忆则跟随任务结束时清理。默认情况下CrewAI使用ChromaDB做向量索引,也支持配置本地文件或远程向量库。

CrewAI多Agent场景里还有一个非常现实的点:记忆隔离。每个Agent默认有自己的记忆空间,Agent A踩过的坑不会自动让Agent B学会。要想团队级共享,你得设计专门的知识同步机制,或者在实体记忆层面做共享存储。这意味着多Agent的记忆管理不只是"单个大脑的记忆",还有"组织知识库"的维度。很多团队在这里栽过跟头,以为每个Agent配个向量库就完事了,结果Agent之间信息断层严重。

3.5 OpenAI Assistants API:托管线程里的轻量记忆

Assistants API是OpenAI官方提供的一套Agent托管方案。它的记忆设计看起来最"偷懒":你用Thread管理一轮会话,所有消息历史都由服务端托管保存,每次运行时会话历史自动作为上下文传给模型。开发者几乎不用碰存储。如果你需要让Agent具备知识检索能力,可以再挂上File Search工具,它会把你上传的文件做向量化,供模型按需检索。

这套方案的好处非常明显:落地成本几乎为零,不需要自己搭数据库、不用写记忆读写代码,会话天然跨请求连续。对于快速验证产品原型、做单会话客服机器人,它是最省事的选择。

但它的硬伤也很清晰。第一,Thread内的消息会持续累积,它没有内建的遗忘机制——上下文成本会随时间线性增长。第二,Thread之间的记忆是完全隔离的,用户今天在Thread A里确定的偏好,明天Thread B里Agent一无所知。第三,记忆内容不可控,你没法主动告诉系统"这条信息要长期保留、那条可以扔掉"。所以我的判断是:Assistants API适合做短生命周期会话的托管方案,但如果目标是"一个长期陪伴用户、持续积累个性化知识的Agent",它明显不够。

3.6 Claude Code:文档化记忆与可审计偏好

Claude Code走的路线非常特别,可以说反直觉。别的项目都在想方设法把记忆藏进数据库和向量库,它却把记忆直接写成人类可读的Markdown文件。在项目根目录放一个CLAUDE.md,在用户主目录放一份全局的~/.claude/CLAUDE.md,Agent每次会话启动时自动读取,运行中也可以读取、更新这些文档。子目录里还能放更细粒度的CLAUDE.md,形成一套层级记忆体系。

这套设计的优势在于记忆的可见性和可审计性。你可以打开CLAUDE.md,清楚看到Agent"记得"你的哪些偏好和项目约束,可以直接改文档来修正Agent的行为。这种透明性,是向量库里几百个embedding给不了的。我在用它做编码Agent时感受很深:你只要把项目的架构决策、代码风格规范写进CLAUDE.md,Agent在后续所有会话里都会严格遵守,基本不会跑偏。

它的问题在于:文档适合承载规范、偏好、决策这类高密度稳定信息,但不适合承载高频细粒度的交互事实。比如"用户昨天问了三次某个API的用法",这种临时性、碎片化的信息没必要写进CLAUDE.md。因此Claude Code的文档记忆,在我眼中更偏向"长期稳定偏好层",而非完整的记忆方案。它如果要覆盖高频记忆,还得搭配向量检索或会话历史工具来补。

4. 横着看:六种记忆方案的关键差异

把六个项目看完,我把它们放在同一张表里对比,方便你抓核心差异。

维度MemGPT/LettaLangGraphAutoGPTCrewAIAssistants APIClaude Code
存储位置内存块+向量库数据库Checkpointer外部向量库向量库+文件服务端Thread文件系统
记忆粒度记忆块+条目消息+状态字段文本片段文本+实体关系整条消息记录整篇文档
写入触发Agent主动调用函数程序显式写入自动存入框架自动采集API自动保存Agent写文件
读取方式Agent工具检索换入全量加载状态相似度检索注入场景化注入历史全量入上下文启动注入全文
遗忘机制上下文压缩/归档手动截断/压缩较弱有清理策略无自动遗忘人工改写文档

4.1 写入口径与遗忘机制

六个方案在"什么值得记"这个问题上分成了两派。一派是程序决定论,比如LangGraph和Assistants API,所有消息默认全存,写入几乎不过滤,遗忘靠外部手段(手动压缩、删除)来做。另一派是智能决策论,比如MemGPT让Agent自己决定写什么、什么时候归档,Claude Code则把判断权部分交给了人类。

这个差异直接影响系统复杂度。程序决定论的优点是简单可靠,缺点是存储膨胀很快,要额外做裁剪策略。智能决策论的优点是记忆质量高、噪音少,缺点是你得承担Agent自我管理时的token开销和不确定性。我个人的经验是:生产环境里别完全相信Agent的自我判断,最好在关键节点上设规则兜底,比如固定每N轮强制做一次摘要归档。

4.2 读取时机与上下文占用

在"何时读取记忆"上,方案也分两极。LangGraph和Assistants API是一类,读取时机是固定的:每次运行都把所有相关状态丢给模型,简单粗暴,但上下文占用随历史线性增长。AutoGPT和MemGPT是另一类,读取是主动触发的:有需求才检索,按需取用,上下文占用相对可控,但检索时机和检索质量会直接影响效果。

这里有个很反直觉的点:检索式读取不一定比全量读取更省token。模型在决策"要不要检索"和检索本身都会消耗上下文,如果检索结果又长又没价值,实际开销比全量读取更贵。所以检索式记忆必须在检索质量上下功夫,比如设置相似度阈值、限制返回条数、摘要压缩后再注入。

4.3 一致性与可审计性

记忆的一致性问题,在Agent里容易被忽视。DeepSeek、ChatGPT这类对话产品都有"会话记录"功能,用户可以随时查看历史,但在Agent系统里,记忆往往是黑盒的:你根本不知道Agent记住了什么、什么时候记的、为什么是这个版本。一旦Agent固化了错误认知,你甚至无从下手修正。

六个方案里,Claude Code的可审计性最强,因为记忆就是一份明文文档。其次是CrewAI的实体记忆,实体和关系是结构化数据,可以直接查改。最弱的是Assistants API,消息全在服务端,你想主动干预都没接口。我强烈建议,凡是做长期用户型Agent的团队,一定要为用户提供查看和修改Agent记忆的入口。这既是产品体验问题,也是信任问题。

5. 从选型到落地:怎么把这套对比用在真实项目里

5.1 场景化选型

看了这么多设计思路,最后落到你自己项目上,怎么选?我的建议是按产品形态和团队能力来定位:

  • 如果你在做快速原型验证、单会话客服、短任务助手,起步直接上OpenAI Assistants API或等效的托管方案,别自己折腾存储。
  • 如果Agent任务状态复杂、流程长、需要生产级持久化,优先考虑LangGraph以Checkpointer做核心记忆,再外接一个向量库处理知识检索。
  • 如果Agent需要长期陪伴用户、积累个性化偏好,研究MemGPT/Letta的分层记忆设计,让记忆管理成为Agent自己的能力。
  • 如果你在做多Agent团队协作,参考CrewAI的四类记忆分类,明确区分工作记忆、实体记忆和长期经验。
  • 如果是编码助手、命令行工具这类场景,直接抄Claude Code的文档记忆思路,把规范写进文件。
  • 如果是知识密集型应用,需要从文档库准确找答案,那么在任意框架里都该叠加AutoGPT式的向量检索方案,关键是优化检索质量。

5.2 一套可直接复用的最小实现

如果你现阶段用的是LangGraph,又想让Agent具备长期记忆,我分享一个我自己项目里用着很顺的最小实现组合:消息状态管理 + Checkpointer持久化 + 摘要压缩 + 向量化检索。

from langgraph.graph import StateGraph, MessagesState from langgraph.checkpoint.sqlite import SqliteSaver # 1. 定义状态:消息列表 + 长期记忆摘要 class ChatState(MessagesState): summary: str # 历史对话的压缩摘要 memory_context: str # 从向量库检索到的相关背景 # 2. 每次处理前,先从向量库拉取相关记忆注入上下文 def load_memory(state: ChatState): query = state["messages"][-1].content docs = vector_db.similarity_search(query, k=3, score_threshold=0.75) context = "\n".join(d.page_content for d in docs) return {"memory_context": context} # 3. 消息量超阈值时,调用模型压缩历史,写入向量库 def summarize_if_needed(state: ChatState): if token_count(state["messages"]) > 8000: summary = llm.invoke(SUMMARIZE_PROMPT.format(history=state["messages"][:-1])) vector_db.add_texts([("conversation", summary)]) return {"messages": [summary], "summary": summary} return {} # 4. 图编排 + Sqlite持久化 graph = StateGraph(ChatState) graph.add_node(load_memory) graph.add_node(agent) graph.add_node(summarize_if_needed) graph.add_edge("load_memory", "agent") graph.add_edge("agent", "summarize_if_needed") graph.add_edge("summarize_if_needed", "load_memory") app = graph.compile(checkpointer=SqliteSaver.from_conn_string("agent_memory.db")) # 5. 同一thread_id的多次调用自动维护会话连续性 response = app.invoke( {"messages": [("user", "继续上次的事情")]}, config={"configurable": {"thread_id": "user-123"}}, )

这套组合的关键思路是分层配合:Checkpointer保存原始会话的连续性,摘要机制控制消息膨胀,向量库处理跨会话的知识回忆,memory_context字段给Agent提供长时间线的语义背景。实际跑下来,三四十轮的长任务基本不会丢上下文,成本也比全量历史注入可控得多。

5.3 三个必须避开的坑

方案定好、代码能跑,真正的坑在后续调优里才会冒出来。我踩过的三个,值得单独说一说。

第一个坑:检索结果没有质量门槛,什么都往prompt里塞。我见过很多团队在向量检索上只做top-k截断,不设相似度阈值。结果就是检索出三条相关性只有0.5的边角料,也一股脑注入Context,把真正重要的指令挤到后面。解决办法很简单:设一个保守的相似度阈值(比如0.75),低于阈值的宁可不注入,让Agent基于已有上下文回答。记住,不要为了"用上记忆"而强行用上记忆。

第二个坑:原始消息和记忆摘要混在一起,没有时间/来源标注。Agent记住了一条旧结论,下次提问时把它当成新事实用,这是最隐蔽的错误。给记忆条目加上metadata(时间戳、来源会话、可信度),注入prompt时明确标注"这是历史摘要,请结合当前上下文判断"。这一步能避免大量记忆误用问题。

第三个坑:记忆被污染之后没有修正通路。一旦Agent写入了一条错误记忆,它会在后续所有会话里反复引用这条错误,形成自洽的幻觉。所以记忆系统必须有回滚和编辑机制,就像Claude Code那样,能让用户直接在文档里删改。别把记忆设计成只能读不能改的"铁板一块",否则错误记忆会越滚越强。

我自己做Agent项目最大的体会是:长期记忆的难点从来不在存储,而在读和写。存储方案到处都是,向量库、数据库任你选,但"什么时候该记、什么时候该忘、什么时候该从记忆里翻出某一段、翻出来之后怎么用",才是真正拉开系统体验差距的地方。建议你按这个顺序落地:先跑通Claude Code式的文档记忆,把稳定偏好和规则沉淀下来;再用LangGraph这种状态管理把会话连续性和任务状态管好;最后在需要知识回忆的场景里,叠加一个带质量门槛的向量检索。三层记忆各司其职,比把宝押在某一套"完美方案"上靠谱得多。

返回列表