Agent 长期记忆的六种设计流派:从 MemGPT 到托管 Memory API

发布时间:2026/10/31 18:39:27
Agent 长期记忆的六种设计流派:从 MemGPT 到托管 Memory API

老读者都知道,我这些年一直在折腾 Agent 相关的项目,从最早的 AutoGPT 脚本到现在的多 Agent 工作流,踩过的坑比写过的代码还多。今天想聊一个被问得最多、也是我反复栽跟头的问题:给 Agent 加上长期记忆,到底应该怎么设计?

先说个反直觉的结论:长期记忆不等于向量数据库,更不等于把聊天记录存下来。很多人一上来就搞个 Pinecone 或 Chroma,把用户对话全塞进去,结果真正跑起来的时候,该忘的还是忘,该错的还是错。原因很简单——存储不是记忆,检索才是。而检索背后,还有更新、冲突处理、遗忘机制这一整套工程问题。

我最近系统梳理了六个主流项目在这方面的设计思路,分别是 MemGPT(Letta)、LangGraph 的 Checkpointer、Mem0、Zep、AutoGPT 的文件式记忆,以及 OpenAI 官方的托管 Memory API。这几个方案分别代表了不同的技术流派,有做虚拟内存调度的,有做图式时序记忆的,有做提取式记忆的,也有干脆把整个记忆服务外包的。这篇文章会把每一套的设计逻辑、适(和)用场景、以及我用下来觉得真正有价值(或真正坑)的地方,掰开揉碎讲清楚。

1. 先别急着接向量库:Agent 记忆到底在记什么

很多教程会把 Agent 记忆分成工作记忆、情景记忆、语义记忆三层,听着玄乎,其实完全可以类比成人的记忆机制。

  • 工作记忆:就是当前上下文窗口,Agent 正在思考的内容,对话中断或任务结束就丢了。对应代码里的 context window。
  • 情景记忆:具体发生过的事,比如“上周用户问过如何部署 Docker 容器”,对应聊天记录、任务日志。
  • 语义记忆:从经历中提炼出的规则和事实,比如“用户的孩子有花生过敏”“用户偏好简洁回复”。这是最高价值、也最难做的部分。

绝大多数人理解的“长期记忆”,其实指的是情景记忆和语义记忆。但市面上很多方案只做到了“情景记忆的存储”,也就是把聊天记录/事件文本存进向量库,检索时把相似片段塞回上下文——完全没有提炼语义,更谈不上更新和遗忘。

我踩过最惨的一次坑是这样的:做一个客服 Agent,把用户原始对话存入向量库。用户在第四天说“我改主意了,现在工作日晚上八点以后才有空”,Agent 依然把第一天聊的“每晚八点后有空”当成事实,回复了一个让人哭笑不得的预约时间。问题的根源不是向量检索不准,而是这套记忆系统根本没有“更新”和“冲突处理”的概念。

所以在对比六个方案之前,先用一张表把记忆系统的核心能力维度列清楚,后面每个方案都可以拿这些维度去衡量:

能力维度含义对应问题
存储结构记忆以什么形式保存(文本块、结构化实体、图)存下来会不会变成“死数据”
写入机制新信息如何进入记忆(原样存、提炼后存)垃圾信息会不会污染记忆库
更新策略新旧信息冲突时如何处理改了偏好,Agent 是否知道
检索方式如何取回相关记忆(向量召回、关键词、图遍历)该想起来的时候能不能想起来
遗忘机制记忆是否会失效或删除过期信息会不会误导决策
集成成本接入项目需要多少改造团队能不能消化

带着这六个维度去看方案,很多宣传语里的“永久记忆”就站不住脚了。下面一个个说。

2. MemGPT / Letta:给大模型装上"虚拟内存",像操作系统一样管理上下文

先说最早的明星项目 MemGPT,现在改名 Letta。它的核心洞察非常漂亮:大模型的上下文窗口相当于物理内存(RAM),而外部存储相当于磁盘。既然操作系统可以用虚拟内存把放不下的数据换进换出,为什么大模型不能?

2.1 分页调度:遗忘是主动设计出来的

MemGPT 把记忆分成几个层级。核心记忆(Core Memory)常驻上下文,包括用户信息、人格设定等必须随时可见的内容;外部记忆(Archival Memory)存放在数据库里,只有需要时通过函数调用检索。

这个设计最妙的地方在于自我编辑。Agent 在对话中发现自己需要新的关键信息,会主动调用memory_insert/memory_replace工具,更新核心记忆块。也就是说,记忆的写入和更新,不是开发者用代码硬编码的,而是模型自己决策的。

用一段伪代码说明它的工作循环:

# 简化版MemGPT循环,实际项目用Letta时逻辑类似 while True: event = receive_user_message() response = agent.llm_call( system=agent.core_memory, # 上下文中的核心记忆 messages=agent.recall_memory, # 对话历史中的滚动窗口 tools=agent.external_memory_tools # 检索/编辑外部记忆 ) if response.tool_calls: for tool in response.tool_calls: if tool.name == "memory_replace": agent.core_memory.update(tool.arguments) elif tool.name == "archival_search": agent.context.append(agent.archival_memory.search(...))

这里的关键是,archival_search结果只在他需要的那个 agentic turn 存在,下一次对话又重新检索。这就避免了“什么记忆都往上下文里塞,窗口很快爆炸”的困境。

2.2 这套方案的实际体验与局限

我在一个长篇小说写作助手里用过 Letta,体验是:它对“会话型 Agent”相当友好,模型自己决定要记住什么,写出的性格一致性明显比纯向量库检索好。但有两个问题必须说。

第一,memory_replace依赖模型的判断力,而模型的判断并不总是可靠。实测中,模型经常把“用户今天心情不好”这种瞬时状态写进核心记忆,导致几天后还在影响回复风格;反而把“用户是某公司 CTO”这种长期事实漏掉。需要开发者提前在工具描述里写清楚每条核心记忆的字段。

第二,当核心记忆块过大(超过 1500 token)时,模型每次决策都要重读一遍,成本和延迟都上去了,而且越改越散。我一度把用户信息全都塞进去,结果每条回复都带出来一堆无关的“记忆”,上下文被稀释得很厉害。

MemGPT 这套“操作系统式”的调度,最大的产业价值其实是证明了一件事:长期记忆不应放在一个无限增长的上下文里,而应该有换页机制。这个理念影响了后面很多方案,但真要做好,坦白说要调教的东西不少。

3. LangGraph 的 Checkpointer:外置状态不等于长期记忆,但它是家族基石

LangGraph 是 LangChain 团队推出的 Agent 编排框架。和 LangChain 早期的ConversationBufferMemory那种“把所有对话塞进一个 memory 对象”的做法不同,LangGraph 把状态管理提升到了图执行层。

3.1 Checkpointer 到底存了什么

在 LangGraph 里,一个 Agent 被建模成一张图,节点是工具调用或 LLM 调用,边是控制流。Checkpointer 的作用很简单:在每个节点执行之后,把整个图的 State(状态)持久化到外部存储,比如 SQLite、Postgres 或 Redis。

from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.graph import StateGraph, START, END # 每个thread_id对应一条独立的记忆会话线 config = {"configurable": {"thread_id": "user-123-conversation-1"}} graph = StateGraph(ConversationState) graph.add_node("agent", call_agent) graph.add_node("tools", call_tools) graph.add_edge(START, "agent") graph.add_edge("agent", "tools") graph.add_edge("tools", "agent") # 循环直到agent决定结束 graph.add_edge("agent", END) app = graph.compile(checkpointer=SqliteSaver.from_conn_string("checkpoints.db")) result = app.invoke({"messages": [{"role": "user", "content": "帮我部署一个Nginx"}]}, config)

这段代码里,每次invoke之后,State 里的一切——包括消息列表、中间变量、工具调用结果——都会写进 SQLite。下次用同一个thread_id继续对话,Agent 能恢复现场。

注意,这里的“记忆”本质是会话状态恢复。用户说“刚才叫你部署 Nginx,结果报错了”,Agent 打开同一个 thread_id,能翻出上下文接着排查。这解决的是“工作记忆跨轮次不丢”的问题,和“用户长期偏好是什么”完全是两回事。

3.2 和传统记忆方案的本质区别

LangChain 早期的 memory 方案(ConversationBufferMemory、ConversationSummaryMemory)我也用过。问题很明显:Buffer 模式会把对话无限加长,几千轮之后光历史消息就把上下文塞满;Summary 模式虽然用摘要压缩,但每轮都要重新生成摘要,既有延迟也会丢细节。

LangGraph 的 Checkpointer 用“状态持久化”替代了“记忆对象”,思路更干净——它不试图“理解”记忆,而是把整个计算过程的状态整块存下来。这样有几个实打实的好处:

  • 崩溃恢复能力极强:进程挂掉、网络抖动,重放一遍 checkpoint 就能续跑。
  • 多轮复杂任务稳定:比如多步骤规划,中途换人接管,也能看到前置状态。
  • 调试方便:每个节点的状态都可以单独检查、回滚。

但同样有一个必须认清的边界:LangGraph 本身不提供长期语义记忆。你说“我上次在这里存过用户过敏信息”,它不会帮你记住,因为那是你业务代码和向量库的责任。

我在实际项目里的组合拳是:LangGraph 管对话状态和工作流编排,另外再配一个记忆服务(比如后面的 Mem0)做长期画像。两者分工明确,前者保证“聊到一半不掉线”,后者保证“下次来还记得你是谁”。如果只接 Checkpointer,很容易把状态恢复误当成真正的长期记忆,上线后用户发现“换新对话 ID 之后就失忆了”。

4. Mem0:像人脑一样"提炼"记忆,而不是"存储"记录

如果让我选一个最符合“长期记忆”直觉的方案,我会选 Mem0。它是从对话里提炼高价值信息,而且设计了更新和删除机制——这才是字母 M 背后真正的功夫。

4.1 提取式记忆:从流水账里挑出值得留的

Mem0 这个名字熟悉的人可能知道,它脱胎于 GPTCache 作者的后续项目。它的设计理念是:不是所有对话内容都值得成为记忆。比如用户说“今天天气真不错”,这是瞬态信息,不值得长期留存;但“用户是产品经理,偏好周报用中文写”,这是稳定画像,值得记忆。

它的核心流程分三块:

  1. ADD:从对话中抽取潜在记忆,用 LLM 判断是否为长期事实,并检查是否与已有记忆重复/冲突。
  2. UPDATE:如果新信息与旧信息矛盾(比如用户换了手机号),自动标记旧记忆失效,写入新值。
  3. DELETE:当用户明确要求“忘掉这件事”时,从存储中移除。

用一个简化伪代码表达 ADD 阶段:

def add_memory(messages, user_id): # 1. 用LLM把对话提炼成候选记忆条目 candidates = llm_extract_facts(messages) # 2. 向量检索已有的相关记忆 existing = vector_store.search(user_id, candidates)) # 3. LLM整合:哪些是新增,哪些要更新旧事实 actions = llm_reconcile(candidates, existing) for action in actions: if action.type == "create": vector_store.insert(user_id, action.memory) elif action.type == "update": vector_store.mark_outdated(action.old_id) vector_store.insert(user_id, action.memory)

这套流程的执行效果,我实测过几轮。模拟用户说:“我儿子今年 6 岁,对花生过敏。明年要上小学了。”持续几轮对话后,查询“孩子多大了、有什么忌口”,Mem0 能返回结构化条目,比如:

[ { "memory": "用户的儿子6岁,对花生过敏", "type": "fact", "created_at": "2025-..." }, { "memory": "用户计划2026年让儿子上小学", "type": "plan", "created_at": "2025-..." } ]

这种结构化、可解释的记忆,比一坨对话文本片段更好用。原因是检索时,LLM 可以直接消费这些事实性条目,而不是从大段的原始聊天记录里现找。我的实际体验是:用户的长期偏好类查询,准确率比“检索原始对话”高出非常多。

4.2 适合场景与要注意的坑

Mem0 特别适合做“用户画像型”Agent——比如个性化推荐、智能客服、CRM 助手这类需要记住用户长期属性的场景。我拿它做客服系统时,用户一提“我这个月已经退过两次货了”,Agent 直接查 Mem0 返回用户近 3 个月的退货次数和原因,处理起来稳准狠。

但要特别提醒几个坑:

  • OpenAI 发布的托管 Memory API 实际上采用了非常相似的研究思路(提取 + 整合 + 删除),这从侧面验证了“提取式记忆”是当前业界的主流判断方向。
  • 更新逻辑依赖 LLM 的二次判断,如果对话内容本身含糊(比如客户说“我要用的是企业版,不,还是专业版吧”),LLM 可能把版本信息越改越乱。建议在触发更新的 prompt 里强调“以最近一次用户明确声明为准”。
  • 异步写入是必须的。我一开始同步调用提取 + 写入,用户每发一条消息都要等 2-3 秒。后来改成消息进队列,异步处理记忆提取,主流程零感知。因为记忆提取本来就不要求实时——真正回答用户问题时,用的是上几轮提炼好的记忆,而不是“实时提炼”。
  • 删除要当真删除。当时为了省事,DELETE 只做了软标记,后来用户投诉隐私问题,我才意识到记忆系统的合规能力不是可选项,是做这个方向必须要覆盖的基础工程能力。在生成了错误记忆时,Mem0 还允许通过deleteAPI 手动干预,这在生产调试里能救命。

5. Zep 的时序记忆图:时间线 + 实体关系,回答"当时发生了什么"

聊完提取式记忆,再说一个方向截然不同的方案:Zep。如果说 Mem0 更注重“提炼用户的稳定特征”,那 Zep 更注重“还原事件的时间线和实体关系”。

5.1 时序知识图谱:将对话变成一张会生长的图

Zep 的核心是 Graphiti 引擎——一个时序知识图谱。普通向量库只存文本块,Graphiti 则把记忆建模为图:节点是实体(人、公司、产品、地点),边是实体间的关系,每条边和节点都带有时间属性。

举一个实际例子。用户在对话里说:“3 月份我到杭州拜访了供应商 A,谈了一批货。”Zep 会异步地把这条信息抽取成:

  • 节点:用户(Person)、供应商A(Organization)、杭州(Location)、货物(Product)
  • 边:用户_拜访了_供应商A(时间:2025-03)、供应商A_位于_杭州

下次用户说:“那家杭州供应商后来交货准时吗?”Zep 图遍历能找到供应商 A 节点,再把后续相关对话中关于交货的信息串起来。

这套设计解决的核心问题是:跨时间片段的推理。普通向量检索的短板是“找相似”,但很难回答“后来怎么样了”。Zep 的时序图天生适合追踪变化——一件事情发生、发展、结束,沿着时间线能把整个故事拼出来。

5.2 接入方式和实际收益

Zep 的接入基本是当独立服务用。客户端把用户消息发给 Zep,Zep 异步处理后,通过 Memory API 查询。和 Mem0 一样,它也支持摘要生成和事实提取,但底层存储和查询逻辑完全不同。

# 伪代码:Zep客户端用法,示意其作为独立记忆服务接入 from zep_client import ZepClient zep = ZepClient(base_url="http://localhost:8000") # 写入:把对话交给Zep异步处理,内部会抽取事实并更新时间线 zep.upsert_session("user-42", { "messages": [ {"role": "user", "content": "我会在下周一上线新版本"}, {"role": "assistant", "content": "好的,我记下了。"} ] }) # 查询:按时间范围/实体过滤 results = zep.search_memory( session_id="user-42", query="供应商A的交付情况", start_date="2025-03-01", end_date="2025-06-01" )

我主要把 Zep 用在业务流程型 Agent 上,比如客户项目跟进。客户问“我们上次讨论的报价方案后来改了什么”,Zep 能把“9 月 12 日讨论了 A 方案,9 月 20 日改为 B 方案,理由是客户预算收紧”这一整条时间线拉出来。这在纯向量库里很难做到——向量检索按语义相似度找回的是片段,而不一定是事件链。

Zep 的代价是运维成本。Graphiti 需要 Neo4j 或类似图数据库支撑,数据量大时要关心图更新的延迟和一致性。自托管与云版本之间手感差异也大,前期探索建议直接试云版,确认流程走通再评估自建。

6. 文件、向量库与托管服务:从 AutoGPT 到 OpenAI Memory API 的演进路线

前面聊的四个方案都是工程师自己搭的。但业界还有另一条路线:从一开始用最朴素的文件存储,到今天直接把记忆能力外包给托管 API。这条演进史本身就是很好的选型教材。

6.1 AutoGPT 时代的原始方案:文件里的记忆只是“日志”

2023 年 AutoGPT 刚火的时候,我给一个内部调研项目跑过。它的记忆管理非常原始——任务状态保存在json文件里,对话历史写进sqlite或纯文本,向量库(当时常用 Weaviate 或 Pinecone)里存的也是整段对话记录。

这种“把原始记录当记忆”的方案,问题非常多。最明显的是:没有提炼、没有更新,也没有遗忘机制。它就相当于你把所有购物流水都记在一个本子上,但从不整理成通讯录,也不画销账——需要的时候,只能一页一页翻。你翻到第一页还写着“用户喜欢晚间 8 点沟通”,但其实人家第四天就改成晚上九点了。

AutoGPT 最大的历史价值,是让整个社区意识到:记忆必须是一个独立的设计模块,而不是顺手存个文件。当年跑一个 AutoGPT 实例,任务稍微长一点,文件越滚越大,检索越来越慢,最终只能停掉重来。

同期还有个 BabyAGI,它更极端,直接把任务列表当作“记忆”,本质上只能用 task 队列管当前目标——别谈长期画像了。这一代方案现在基本被淘汰,但它们的教训构成了一条清晰的判断标准:如果一个记忆方案没有提取和更新机制,它就不是长期记忆,只是日志。

6.2 托管 Memory API:把记忆工程变成“调用一个服务”

到了 2025 年,我观察到一个明显趋势:记忆能力正在被封装成托管服务。最具代表性的是 OpenAI 发布的 Memory API——它面向开发者,目标就是让 Agent 直接拥有一套开箱即用的记忆后端。

Memory API 的使用方式非常“舒服”。开发者把需要记住的内容(用户偏好、业务事实、上下文)作为字符串传给服务,它内部完成提炼、存储、索引、检索,并支持记忆删除。对中小团队来说,这意味着不需要自己搭向量库、不用调 LLM 提取流程、不用处理冲突更新——全部外包。

# 伪代码:托管Memory API的简化示意 memory_client = MemoryAPIClient(api_key=...) # 写入:传入原始文本,由服务端提炼为可检索记忆 memory_client.store("用户偏好用中文回复,工作日在上海办公") # 查询:服务端自动做向量检索+重排序 result = memory_client.search("用户在哪里工作", top_k=3)

托管方案的优点不必多说:省事、自带隔离(按用户粒度管理)、有合规删除接口。但该意识到的问题也不少:

  • 数据主权:业务数据出境和存储合规,这是企业级项目绕不开的考量点。
  • 不可控性:模型内部怎么提取、怎么更新,对开发者是黑盒。你真的信任它能处理好那些“用户上午说 A 下午说 B”的冲突吗?我持保留态度。
  • 隐性成本:按调用计费,高频 Agent 场景下写入 + 检索 + LLM 提取都是钱。测试期看不出,真正跑 10 万用户时账单会让你重新做选型。

我现在的态度是:托管 API 适合快速验证和中小体量项目;一旦业务发展到对隐私、成本、行为可解释性有硬性要求,还是得回到 Mem0/Zep 这种自控方案。两套方案不冲突,甚至可以在不同阶段切换。

7. 六套方案横向对比:按项目的实际需求选型

前面每套方案都讲了设计思路,现在把它们放在一张表里横向对比。我自己做选型时,就是这么一张表贴墙上看。

方案记忆类型存储结构更新策略检索方式遗忘/删除适用场景主要成本
MemGPT/Letta上下文调度+外部档案核心记忆块+向量库模型自我编辑,不稳定函数调用按需查可覆盖核心记忆,外部库不自动清长会话角色扮演、研究型 Agent上下文 token 占用、模型判断力依赖
LangGraph Checkpointer会话状态/工作记忆Graph State 序列化整块重写,无语义级更新按 thread_id 恢复无自动遗忘多步任务的崩溃恢复、人机协作存储膨胀需要清理策略
Mem0语义记忆/用户画像向量库+LLM提炼条目冲突检测+自动更新向量召回+LLM过滤支持主动删除客服、个性化推荐、CRM异步流水线、LLM 调用成本
Zep时序情景记忆+实体关系时序知识图谱沿时间线追加/更新图遍历+向量混合可按时间范围删除项目跟进、审计追溯、复杂业务 Agent图谱运维成本高
AutoGPT 式文件记忆原始日志文件/json/sqlite无关键词/向量,质量差无极早期原型、学习交流检索弱、易膨胀
托管 Memory API全合成记忆服务端黑盒服务端自动服务端自动提供删除接口快速验证、中小体量数据主权、按量计费

六套方案不一定要分出高下,更重要的是按项目形态对号入座。我常用的决策路径是:

  • 我要做的是多轮对话 + 用户画像(比如 AI 客服、AI 健身教练):优先 Mem0。它的提取式记忆最贴合“记住用户”的诉求。
  • 我要做的是复杂任务编排,中途可能崩溃/中断(比如 Agent 处理一个长流程的订单):LangGraph Checkpointer 是地基,先保证状态恢复。至于“记住用户的偏好”这种语义记忆,用 Mem0 叠加。
  • 我要做的是项目跟进/业务审计类 Agent(比如工程项目管理机器人):Zep 的时序图是唯一能回答“后来怎么样了”的方案。
  • 我只需要快速做一个演示/验证(比如三天后要汇报):直接用托管 Memory API 或 Letta,先跑通,别纠结。
  • 我想理解 Agent 记忆的底层机制(比如在学习):亲手拆一遍 MemGPT 的调度逻辑,比什么都快。

这里有一个核心观点:大部分项目的“长期记忆”需求,其实是多层混合需求——既要有能恢复的工作记忆,又要有能调用的语义记忆,偶发还要有能回溯的事件记忆。没有一套方案能全覆盖。这也是为什么后来出现了“记忆中间件层”的思路——把 LangGraph(管状态)、Mem0/Zep(管语义/事件)组合起来用。组合后的效果,单项方案根本比不了。

8. 接上记忆以后真正会踩的坑:写入时机、冲突与遗忘

上面聊完选型,但没真正跑过记忆系统的朋友可能意识不到:方案选好只是被坑的开始。我把这段时间踩过的坑集中放在这一节,按严重程度排序。

8.1 同步写入会拖垮主流程,异步才是常态

记忆写入如果放在用户请求链路里同步执行,延迟会立刻暴露。我当时用 Mem0 时,把“用户发消息 → 提取记忆 → 写入向量库”放在同一个请求里,结果每条消息的响应时间从 800ms 飙到 2.5 秒。原因很简单:提取要用 LLM 做实体识别和去重判断,它的延迟天然高。

正确做法是把记忆写入丢进后台任务。用户答复照常返回,记忆在后台异步沉淀。

# 伪代码:异步写入记忆,避免阻塞主链路 def handle_user_message(message, user_id): answer = agent.generate(message) # 主流程只处理回答 background_tasks.add( memory_service.extract_and_store(message, user_id) ) return answer

注意异步要有幂等设计,任务队列重试时不能让同一句话被写两次。我用了一个简单的指纹 ID(消息哈希 + 用户 ID),重复投递直接丢弃。

8.2 冲突更新是高频事故区:谁来判定“以新为准”

用户偏好改了,Agent 还在用旧信息,这类事故我上面已经讲过。但更隐蔽的问题是:新旧信息同时存在,检索时哪条被召回。

比如用户说“我家孩子 8 岁了”,然后半年后说“孩子今年要过 9 岁生日了”。向量库里可能存在两条记忆,一条“孩子 8 岁”,一条“孩子 9 岁”。如果只是插入新条目,不加失效标记,检索召回时可能两条都返回,LLM 就懵了。

Mem0 的处理是让 LLM 在“整合阶段”识别出这是同一事实的更新,给旧条目打上invalidated标记。但实际运行告诉我:LLM 的整合判断不是百分之百稳定。最可靠的兜底方式,是在业务层设置“事实生命周期”——比如生日、地址、联系方式这类信息,以最新声明时间戳为准,检索时直接按“时间戳倒序 + 去重”处理,不把选择权全交给模型。

8.3 遗忘不是可选项,是记忆系统的“刹车”

没有遗忘机制的记忆系统,用久了会很可怕。一是旧记忆误导决策,二是隐私合规风险,三是存储成本无限上涨。

我在一个场景里体会特别深:用户一天内反复修改一个需求,Agent 的记忆池子里积累了七八条互相矛盾的版本。后来我们加入了“会话内加权”——同一 session 的近期记忆权重提高,远期记忆自动降权,超过 30 天的长期记忆如果没有被主动查询过,就会被压缩成概要。这套“时间衰减 + 概要化”的机制,比单纯依赖删除请求更能维持系统的可用性。

8.4 评估记忆系统:错误记忆比没有记忆更危险

最后说一个很多人不做的动作:给记忆系统建评测集。

我当时用的方法是,把 50 个典型对话砍成两半:前半段给 Agent 学习,后半段考察它是否记得关键事实。比如:

  • 记忆准确率:回答里提到的事实是否与用户早期陈述一致。
  • 记忆召回率:该用到的记忆是否被主动调用,还是被无视了。
  • 冲突处理能力:用户修正信息后,Agent 是否不再使用旧信息。

测下来才发现,有些模型在“是否调用工具检索记忆”这一步就开始摆烂——明明系统里有,它就是懒得查。后来只能在 prompt 里强制要求:“回答涉及用户个人信息前,必须调用 search_memory 工具”。这种问题,不做评测集根本发现不了。

结尾:回到我自己的选择

聊了这么多,如果要我总结一句真实的实操体会,那就是:Agent 长期记忆没有银弹,它是一个需要根据项目阶段持续调整的工程模块。

我现在的标准动作是,新项目一律先用 LangGraph 保证状态恢复,然后看业务形态决定叠加 Mem0 还是 Zep;探索原型阶段则直接用托管 API 快速验证。等某一天发现记忆检索的准确率或成本出问题了,再回到自建方案调优。没有哪套方案是买回来装上去就一劳永逸的——选型只是开始,真正花时间的地方在写入逻辑、冲突处理、遗忘策略和评测体系,这些东西才决定你的 Agent 到底是真的在“记住”,还是在“假装记住”。

最后分享一个小技巧:无论用哪套记忆服务,都要保留一个可人工干预的后门。我在生产环境里留着一条隐藏接口,可以直接查某个用户的记忆条目、手动修正或删除。你以为用不上的时候,它往往会救你一命——尤其是当你发现模型把一条错误记忆写成了“用户明确表达过”的时候。