Agent Memory 系统

为什么 Agent 需要记忆、五种记忆类型、MemGPT 虚拟内存、以及实现策略

18 min read Part of Agent · Ch. 7
← 上一层级:学习路径 · Part 03 · Agent 设计与工程

延伸阅读

Agent Memory 系统

flowchart TD
  A[Agent 运行时] --> B[短期 / 工作记忆
当前步骤与上下文]
  A --> C[长期记忆
用户偏好 / 项目背景]
  A --> D[情景记忆
过往执行案例]
  A --> E[语义记忆
知识库 / RAG]
  A --> F[程序记忆
技能 / 模板 / SOP]
  B --> G[当前决策]
  C --> G
  D --> G
  E --> G
  F --> G

如果说工具决定 Agent 能做什么,记忆决定的就是 Agent 能不能持续地把事情做下去

一个没有记忆的 LLM,本质上更像“每次都从零开始的即时推理器”。它当然可以在一次调用里表现得很聪明,但只要任务跨越多步、跨越较长时间、跨越多个会话,它就会暴露出一个根本限制:它默认不会保留状态。 这也是为什么 OpenAI 在面向开发者的 Agent 指南里,直接把 state / memory 与 model、tools、orchestration 并列为 Agent 的核心组成之一。(developers.openai.com)

对普通聊天机器人来说,这个限制有时还能靠“把最近几轮对话重新塞进上下文”勉强缓解;但对 Agent 来说,这通常远远不够。因为 Agent 要面对的,不只是“你上一句说了什么”,还包括:

  • 当前任务做到哪一步了;
  • 哪些工具已经调用过;
  • 上次失败发生在哪;
  • 哪些用户偏好应该长期继承;
  • 哪些知识应该按需检索;
  • 哪些成功做法值得沉淀成可复用经验。

所以,Agent Memory 系统的本质,不是“让模型记住更多聊天记录”,而是:给一个本来默认无状态的推理系统,补上一层可读、可写、可整理、可检索的状态能力。

为什么 Agent 需要记忆

先看定义:LLM 默认不携带持久状态,而 Agent 要完成多步、长程、跨会话任务,必须依赖外部记忆系统。

这个问题可以用一个很朴素的方式理解:

如果一个系统连“刚才做过什么”都记不住,它就很难真正称得上在“执行任务”;它更像是在不断重新回答眼前这个局部问题。

下面几个场景最能说明这一点:

场景没有记忆会怎样有记忆会怎样
多轮任务用户说“继续”,系统不知道该继续哪个步骤能恢复到上一次的任务状态
长链路执行第 10 步时忘了第 3 步的中间结果能复用已有结果,避免重复计算
跨会话协作用户说“按上次那个风格改一下”能检索长期偏好和历史约束
失败恢复明明上一轮试过 A 路径且失败了,系统又再试一遍能保留失败经验,换一条路径
个性化交互每次都像第一次认识用户能继承偏好、项目背景和历史决策

这意味着,Agent 需要记忆,不只是因为“上下文窗口有限”,更因为任务本身是有历史的。 一个真正可用的 Agent,必须有某种能力把过去的信息带到现在,把现在的结果带到未来。

记忆不是越多越好,而是要分层

很多人第一次设计 Agent Memory,会本能地想: “那就把所有历史都存下来,需要时再搜。”

这当然比完全不存强,但如果没有分层,系统很快会遇到两个问题:

  1. 检索噪声太大:存得越多,无关信息越多,真正有用的状态反而更难被找出来。
  2. 语义混乱:用户偏好、任务中间结果、失败经验、知识库事实、技能模板,这些东西本来就不是同一种记忆,硬塞进同一桶里,后续很难稳定使用。

因此,理解 Agent Memory 最重要的一步,不是“选哪个向量库”,而是先明确: 系统到底在记哪几类东西。

五种记忆类型:把“记忆”拆开看,很多设计问题就会清楚

原文把 Agent 记忆分成五类,这个思路是合理的,而且与认知科学和近年的 Agent 研究都能形成很自然的对应。Generative Agents 论文就明确展示了一种非常有代表性的模式:系统会记录经历、在时间中生成更高层的 reflections,再在需要时动态检索这些记忆来规划行为。(arxiv.org)

从工程角度看,可以把 Agent Memory 理解为五类能力。

类型英文它主要回答什么问题常见实现
短期 / 工作记忆Short-term / Working Memory“当前这件事做到哪了?”上下文窗口、任务状态对象、会话缓存
长期记忆Long-term Memory“跨会话我需要长期记住什么?”数据库、向量库、用户档案
情景记忆Episodic Memory“我以前经历过什么?哪些路径成功或失败过?”执行日志、事件存储、案例库
语义记忆Semantic Memory“我知道哪些事实和知识?”RAG、知识库、文档检索
程序记忆Procedural Memory“我会怎么做某类任务?”技能库、模板、可复用子流程

这五类记忆的价值,不在于把术语说得更学术,而在于它们能帮助你避免一个常见误区: 把所有状态问题都误解成“要不要上向量数据库”。

很多时候,真正需要的不是更多语义召回,而是更清楚的任务状态对象,或者更结构化的经验日志。

1. 短期 / 工作记忆:当前任务的“现场状态”

这是最基础、也最直接影响 Agent 行为的一层。

它通常包含:

  • 当前会话里的用户消息;
  • 最近几步工具调用结果;
  • 当前计划或当前子任务;
  • 已完成步骤与待完成步骤;
  • 错误信息;
  • 当前是否处于“等待确认”“等待工具”“执行失败”等状态。

为什么短期记忆比很多人想象中更重要

因为大量 Agent 的失败,并不是因为“忘了用户三个月前说过什么”,而是因为它在当前任务里丢了状态:

  • 忘了哪个文件已经读过;
  • 忘了某个步骤已经失败过;
  • 忘了用户刚刚否决了一个方案;
  • 忘了当前正在处理哪个子任务;
  • 忘了应该停止还是继续。

这些都不是长期知识问题,而是工作记忆问题

实现上最常见的几种方式

方式一:直接依赖上下文窗口

把最近几轮完整历史带进模型。

优点是简单。 缺点是:

  • 成本随历史增长;
  • 很快混入无关信息;
  • 对复杂长任务不够稳。

方式二:滑动窗口 + 早期摘要

保留最近 N 轮完整记录,更早历史压缩成摘要。 这是当前非常常见、也很实用的折中。

方式三:结构化任务状态对象

例如:

{
  "goal": "完成产品对比报告",
  "current_step": "整理三家产品的成本维度",
  "completed_steps": ["收集厂商资料", "统一比较维度"],
  "pending_steps": ["撰写建议", "生成最终摘要"],
  "last_error": null
}

这种做法的好处是: 它把“任务做到哪了”从长文本对话里抽离出来,变成一个更稳定、更低噪声的状态层。

先看定义:短期记忆不是“最近聊过什么”的简单堆叠,而是“当前运行时最该知道什么”。

2. 长期记忆:跨会话保留的稳定信息

长期记忆解决的是另一个问题:

什么信息即使会话结束了,也值得以后继续用?

典型内容包括:

  • 用户偏好;
  • 长期项目背景;
  • 常用格式要求;
  • 组织或团队约定;
  • 持续任务的阶段性总结;
  • 值得长期保留的重要决策。

长期记忆最容易写错的地方:存太多“看起来也许有用”的东西

一个常见失败模式是: 系统把几乎所有对话都压成摘要,全部写进长期记忆,然后每次新会话再从中召回。

这样做的结果通常是:

  • 记忆库越来越大;
  • 无关摘要越来越多;
  • 模型被过时或低价值偏好干扰;
  • 检索命中率下降;
  • 真正重要的记忆被埋没。

所以,长期记忆的核心不是“持久化”,而是筛选

哪类信息更适合写入长期记忆

更适合写入的,通常是这些相对稳定、会在未来复用的内容:

  • “用户喜欢先给结论,再给细节”
  • “这个项目优先考虑开源方案”
  • “团队约定所有 SQL 输出都要先检查 WHERE 条件”
  • “该用户长期关注某产品线”
  • “这项持续任务已经完成前两个阶段”

不太适合长期记忆的,通常是:

  • 一次性中间结果;
  • 临时工具返回;
  • 当场就会失效的小状态;
  • 噪声很高的对话碎片。

常见实现

  • 结构化数据库:适合明确字段的偏好、配置、用户画像;
  • 向量库:适合语义检索型长期摘要和经验片段;
  • 混合方案:结构化字段 + 语义索引并存。

OpenAI Agents SDK 当前文档里,也提供了基于 SQLite 的 session 存储实现,至少说明在工程实践中,“会话记忆 / 状态持久化”首先是一个非常具体的存储问题,而不是抽象概念。(openai.github.io)

3. 情景记忆:记住“发生过什么”

情景记忆和长期记忆有重合,但重点不同。

长期记忆更像“长期稳定信息”; 情景记忆更像“具体发生过的经历”。

例如:

  • 上次为了修这个 bug,试过三种方式,前两种失败了;
  • 某个用户曾对一版输出给过负反馈;
  • 某类任务上,方案 A 成功率显著高于方案 B;
  • 某次部署在某一步失败,失败原因是权限不足。

Generative Agents 的架构里,一个非常关键的点就是:系统会保留完整经历记录,并把这些经历在时间中抽象成更高层的 reflection,再用于后续行为规划。(arxiv.org)

这其实非常接近情景记忆的价值: 不是只知道某个事实,而是知道“我以前怎么走过一条路,以及结果如何”。

为什么情景记忆对 Agent 特别有价值

因为 Agent 的很多改进,并不来自“记住更多事实”,而是来自“少走重复的弯路”。

例如代码修复场景里:

  • 如果系统能记住“这个项目里的测试失败经常来自空输入边界”,它下次会更快聚焦;
  • 如果系统能记住“某工具在该环境里经常因权限失败”,它就不会反复盲试。

更合适的实现方式

情景记忆通常不宜只做成大段自然语言堆叠。 更适合的是结构化记录,例如:

  • 任务类型;
  • 输入摘要;
  • 执行路径;
  • 成功 / 失败;
  • 失败原因;
  • 关键决策点;
  • 用户反馈。

必要时再为这些记录补一层向量索引,以便做相似案例检索。

先看定义:情景记忆的核心,不是“我知道这个知识”,而是“我记得自己做过这件事,而且知道结果”。

4. 语义记忆:Agent 的“知识面”

语义记忆就是最容易和 RAG 关联起来的那一层。 它主要存放和检索的是:

  • 事实性知识;
  • 文档内容;
  • 领域术语;
  • 组织规则;
  • 产品说明;
  • 外部资料。

这类记忆通常回答的问题是:

“关于这个主题,我应该知道哪些事实?”

为什么语义记忆本质上就是一种 RAG 能力

因为它的核心流程通常就是:

  1. 把知识存放在外部存储里;
  2. 在需要时按查询召回相关片段;
  3. 再把结果注入当前上下文。

这意味着,Agent 的语义记忆和 RAG 没有本质冲突,很多时候它们就是同一个系统在不同语境下的不同叫法。

区别更多在于使用方式:

  • RAG 往往强调“回答问题时补充知识”;
  • 语义记忆强调“Agent 在执行任务时按需调取知识”。

语义记忆最常见的误区

误区不是“要不要做”,而是“把所有 Memory 问题都理解成 RAG 问题”。

例如:

  • 忘了当前步骤做到哪了,这是工作记忆问题,不是语义记忆问题;
  • 重复犯同样的错,这是情景记忆问题,不是语义记忆问题;
  • 用户喜欢简洁风格,这是长期偏好问题,不是知识库问题。

所以,语义记忆很重要,但它不是全部。

5. 程序记忆:Agent 的“做事套路”

程序记忆讨论得往往最少,但它在实践里很有价值。

它关注的不是“记住什么内容”,而是:

“我学会了怎么做某类事。”

例如:

  • 写竞品分析时,先统一比较维度,再收集证据,再写建议;
  • 修测试失败时,先读错误栈,再定位最小变更,而不是重写全部逻辑;
  • 写 SQL 时,先确认过滤条件,再生成最终语句;
  • 做文档总结时,先列出必须覆盖的要点,再压缩表述。

这类东西并不完全等同于知识,也不完全等同于经历。 它更像是一类可复用的执行模式,也可以理解成 Agent 的“技能”。

为什么程序记忆值得单独提出来

因为在很多系统里,真正让 Agent 越用越稳的,不只是记住更多信息,而是逐渐把成功策略抽象出来。

这也是为什么很多“自我改进 Agent”或技能库研究,都会强调把成功子流程沉淀为可复用单元,而不只是保留原始日志。

常见实现

  • 模板库;
  • 成功任务抽象出来的 skill;
  • 可复用 prompt / workflow 片段;
  • 子任务级 SOP;
  • 结构化“最佳做法”集合。

先看定义:程序记忆关注的不是“我记得什么”,而是“我会怎么做”。

Read–Write–Reflect:Memory 系统不是仓库,而是循环

原文提到一个很重要的架构视角:Memory 不是“存进去、取出来”这么简单,而更像一个持续运行的管理循环。这个方向和 MemGPT、Generative Agents 的思路都很一致:记忆系统的重点从来不只是容量,而是管理策略。MemGPT 论文就明确把其方法描述为 hierarchical memory system / virtual context management,通过不同记忆层级的调度来突破有限上下文窗口。(arxiv.org)

把这个过程概括成:

Read -> 决策 / 执行 -> Write -> Reflect

是非常合适的。

Read:读什么进当前上下文

系统不会把所有记忆都搬进来,而是应该按当前任务挑选最相关的部分。

这一步决定了: 当前上下文里出现的是“有用状态”,还是“噪声集合”。

Write:什么值得写进去

不是每一次工具调用、每一句对话、每一个中间想法都值得写。 写入本身就应该有策略。

Reflect:如何整理、压缩、去重、归档

这一步很关键,也是很多 Memory 系统最容易忽略的地方。 如果只有读写,没有整理,记忆库会越来越脏,最终拖垮检索质量。

先看定义:Memory 系统真正难的不是存储容量,而是信息生命周期管理。

记忆管理:记什么、忘什么,比“能不能都存下来”更重要

记忆系统最容易犯的错,就是把“长期保留”误认为默认更好。

其实,好的 Memory 系统一定会主动忘记。 因为如果不忘,问题会越来越明显:

  • 过时信息继续被召回;
  • 无关偏好影响当前任务;
  • 检索成本和噪声同步上升;
  • 早期的低质量摘要压制后期更高质量状态。

记忆管理的几种常见策略

策略作用
重要性过滤只写入对未来可能有价值的信息
摘要压缩把长对话、长任务整理成更高层摘要
去重合并重复偏好、重复经验、重复结论
版本化让新信息覆盖旧信息,而不是一味累加
TTL / 衰减让临时信息自动过期
热冷分层高频短期可见,低频归档

一个很实用的判断标准

信息若满足下面至少一个条件,通常更值得写入:

  • 会在未来复用;
  • 会影响后续决策;
  • 是用户稳定偏好;
  • 是高价值成功经验;
  • 是需要避免重复踩坑的失败经验;
  • 是某个长期任务的重要阶段结果。

否则,大概率不值得长期保留。

MemGPT / Letta:把上下文窗口当“主存”,把外部记忆当“虚拟内存”

MemGPT 是这一方向里最有代表性的工作之一。论文的核心说法非常明确:它借鉴传统操作系统的层级内存思想,把有限上下文窗口的问题重述为“虚拟上下文管理”问题,并通过不同记忆层级之间的数据移动,给 LLM 提供“看起来更大”的可用记忆空间。(arxiv.org)

这个比喻之所以重要,是因为它改变了一个常见思路:

  • 不是一味追求更长上下文;
  • 而是接受上下文窗口有限,并设计一个系统来决定什么该留在前台,什么该放到后台,什么时候读回来

这个类比为什么有启发性

因为它让我们意识到:

  • 上下文窗口像“高速但很贵的主存”;
  • 外部数据库 / 向量库 / 归档存储像“更慢但容量更大的外存”;
  • 真正决定系统表现的,是两者之间的调度,而不只是某一层的大小。

Letta 现在更适合被理解成什么

今天的 Letta 官方文档已经明显把自己描述为一个面向 stateful agents 的 memory-first 平台和 API,而不只是论文附带的演示项目。文档首页明确强调其 API 是 “a lower-level API for managing your agent’s memory and context”,并将 Letta Code 描述为 memory-first coding agent。(docs.letta.com)

所以,更准确的写法不是“MemGPT 就是某篇论文里的某个 demo”,而是:

MemGPT 提供了分层记忆 / 虚拟内存的关键思想;Letta 则把这种 memory-first agent 的思路进一步产品化和工程化。

实现策略:先分清问题,再选存储

很多文章一讲 Memory,就直接跳到“用什么向量数据库”。 更现实的顺序其实应该反过来:

  1. 先分清你要记哪类东西;
  2. 再决定用什么存;
  3. 再决定什么时候读写;
  4. 最后才是检索和管理策略。

一个更工程化的映射方式

记忆层更合适的实现
短期 / 工作记忆会话缓存、状态对象、短期数据库、上下文压缩
长期记忆关系型 DB、文档 DB、向量库、用户档案
情景记忆事件日志、任务记录、结构化案例库,必要时加向量检索
语义记忆RAG 管道、文档索引、知识库
程序记忆技能库、模板库、可复用子流程注册表

一个重要原则:不要把所有 Memory 都做成向量召回

因为并不是所有记忆都适合“语义相似度搜索”。

例如:

  • 用户偏好中的布尔配置,更适合结构化字段;
  • 当前任务状态,更适合显式状态对象;
  • 失败计数或最后一次错误,更适合键值或事件流;
  • 工具结果缓存,更适合精确键控。

向量检索很有用,但它最适合的是:

  • 语义记忆;
  • 部分长期摘要;
  • 部分情景经验召回。

不是 Memory 的万能底座。

实践建议:从最小可用 Memory 开始,而不是一开始造“总记忆系统”

这是非常关键的一点。

很多团队一做 Agent Memory,就想一步到位:

  • 长期偏好
  • 向量记忆
  • 情景日志
  • 技能库
  • 自动摘要
  • 遗忘机制
  • 反思写回
  • 记忆评分

结果往往是:系统很复杂,但其实连“当前任务做到哪一步”都没稳定管理好。

更稳妥的演进顺序通常是:

第一步:先把短期 / 工作记忆做稳

  • 当前状态对象
  • 最近步骤历史
  • 基本会话持久化
  • 必要的摘要压缩

这一步不稳,后面越加越乱。

第二步:再做长期偏好和会话摘要

适合:

  • 个性化助手;
  • 长期项目;
  • 用户持续交互场景。

第三步:按需加入语义记忆

也就是知识库 / RAG 层。 如果 Agent 需要依赖外部知识,这是很自然的一步。

第四步:再考虑情景记忆和程序记忆

这通常是“让系统越用越好”的进阶层,而不是最初的必备层。

先看定义:Memory 系统应该按问题真实出现的顺序增长,而不是按概念图一次性建满。

Memory 与个性化:没有记忆,Agent 很难真正“认识你”

个性化是 Memory 最直观的价值之一。

一个没有长期记忆的 Agent,哪怕当下表现不错,也更像“高质量通用工具”。 它无法稳定地:

  • 记住你偏好的输出风格;
  • 记住你所在项目的背景;
  • 记住你反复强调的约束;
  • 记住过去哪些方案你已经否定过;
  • 记住哪些知识域对你更相关。

但也要克制一点: 个性化不等于“什么都记住”。真正有价值的个性化,通常来自少量高质量、稳定、可复用的偏好信息,而不是海量聊天碎片。

一个更克制的结论:Agent Memory 的本质不是“让模型永远不忘”,而是“让系统在合适的时候拿到合适的过去”

这是本文最重要的判断。

很多关于 Memory 的想象,都容易滑向一种误区: 仿佛好的 Agent Memory,就应该像人类一样“什么都记得”。

现实工程里,更有用的目标其实是:

  • 当前步骤需要的状态,别丢;
  • 对未来有价值的经验,别白白消失;
  • 不相关或过时的信息,不要回来捣乱;
  • 需要时能找回来,不需要时别占上下文。

所以,Agent Memory 的关键不在于“记忆总量”,而在于:

选择、组织、检索、压缩、遗忘。

核心概念速查

概念一句话
短期 / 工作记忆当前任务运行时需要立即可见的状态与上下文
长期记忆跨会话持续保留的偏好、摘要与长期背景
情景记忆对过去执行经历、成功与失败案例的记录
语义记忆外部知识和事实,通常通过 RAG 按需检索
程序记忆可复用的做事套路、技能和子流程
Read–Write–ReflectMemory 的核心循环:读取、写入、整理
MemGPT用分层记忆 / 虚拟内存思想管理有限上下文窗口的代表性方案
Letta将 memory-first、stateful agent 的思路进一步工程化的平台

小结

Agent 之所以需要 Memory,不是因为“多记一点聊天记录会更贴心”,而是因为一个真正执行任务的系统,本来就必须有状态:要记得当前做到哪了、过去试过什么、哪些知识需要按需调用、哪些经验值得沉淀下来。

从工程角度看,Memory 也不是一个统一大桶,而是至少包含五类不同能力:短期 / 工作记忆负责当前状态,长期记忆负责跨会话稳定信息,情景记忆负责经历与结果,语义记忆负责知识获取,程序记忆负责可复用的做事模式。把这些层次分开,你才会知道哪些问题该用状态对象解决,哪些该用数据库,哪些该用向量检索,哪些需要反思与技能沉淀。

MemGPT 提供了一个非常有启发性的视角:不要把有限上下文当作唯一记忆空间,而要像操作系统管理内存一样,接受“前台有限、后台更大”的现实,并为 Agent 设计一套读写与调度机制。真正好的 Memory 系统,也从来不是“永远不忘”,而是在合适的时候,把合适的过去带回当前决策之中。