记忆系统
如果状态无法跨越承载进程的生命周期,第 38 章 中的智能体循环就只是一次性过程。模型的回答可能安排后续工作,工具可能改动工作区,后面的轮次也可能需要前面留下的证据。把这些状态统称为“记忆”,会掩盖最重要的工程事实:不同状态有不同的所有者、恢复规则和权限边界。
本章把经常塞进同一个存储的四类状态拆开讨论:执行历史、可变工作区、长期记忆和外部系统。检查点可以重新连接它们,却无法保证外部副作用恰好发生一次。检索索引可以找回旧记录,却不能判断记录是否适合写入、是否获准读取、是否仍然有效,也不能判断它是否完整到足以支撑行动。可靠的记忆系统,首先要把这些边界说清楚。
“记忆”一词,四个所有者
在第 轮,智能体的运行状态可以写成:
其中, 是与第 轮有关的完整状态; 是持久化的执行历史; 是智能体当前可见的工作区版本或快照; 是长期记忆存储; 是智能体对外部系统的记录视图。最后一项只是视图。支付服务、工单系统、Git 远端或生产数据库,仍由各自的外部系统保存权威状态。
| 状态 | 用途 | 权威所有者 | 恢复时要回答的典型问题 |
|---|---|---|---|
| 执行历史 | 输入、已安排步骤、结果、错误、计时器、审批和工作流版本 | 持久化编排存储 | 哪些决定和结果已经被接受? |
| 工作区 | 文件、代码仓库、生成产物和本地工具状态 | 工作区存储层 | 应恢复哪一个内部一致的文件系统版本? |
| 长期记忆 | 经过选择的观测、事实、经验、摘要和产物引用 | 记忆服务及其写入策略 | 哪些内容应保留、修订、检索或删除? |
| 外部状态 | 智能体边界之外各种作用的回执和引用 | 各个外部系统 | 某次尝试是否已经提交,如何证明? |
审计记录并不会自动成为重放日志。重放还需要稳定的步骤标识、已经记录的非确定性结果、兼容的工作流代码,以及工作流所需工作区和产物的持久引用。同样,工作区快照也不能说明哪些 API 调用已经成功。每个存储都应明确自己的保证,不能借用相邻存储的保证。
持久执行保存进度,却无法保证外部副作用只发生一次
假设有一个步骤要创建支持工单。执行日志已经持久记录了待调用请求,服务方创建了工单 8421,但工作进程在服务方的回复写入执行日志之前崩溃。恢复时只能看到一个已经安排、却没有完成结果的步骤。从执行日志内部看,下面两段历史完全相同:
- 请求根本没有到达服务方;
- 请求已经提交,但回复在途中丢失。
此时结果未知。先记录调用意图,可以把不确定性收窄到一个明确步骤,却不能消除它。因此,AWS Lambda 的持久步骤默认采用至少一次执行语义,其文档仍要求被中断的调用具备幂等的业务逻辑 (Amazon Web Services 2026)。Temporal 也明确划出同一条边界:重放不会再次运行已经完成的活动,但没有报告完成的活动可能被重试,也可能执行多次 (Temporal Technologies 2026)。
确定性重放需要记录什么
步骤 的最小历史包含下面两条记录,所有符号随后解释:
下式把重放表示为一个确定性的归约过程:
其中, 是稳定的步骤标识; 是副作用的幂等键; 是请求输入; 是输入指纹; 是工作流版本; 是已接受的结果; 是截至第 轮的历史; 是工作流版本 实现的归约器; 是重建后的工作流状态; 是下一条命令或下一组命令。
重放时, 产生的命令必须与已有历史兼容。对于已有记录的结果,系统直接代入结果,不再调用模型、时钟、随机数生成器或外部 API。这些非确定性操作应放在持久步骤中,把被接受的输出写进历史。如果工作流代码改变了分支或命令顺序,旧历史可能不再能够重放。因此,长期运行的系统会固定或显式迁移工作流版本,而不是悄悄用新代码执行所有旧任务 (Microsoft 2026; Microsoft 2026)。
持久性与副作用交付是两项不同的保证:
| 保证 | 恢复行为 | 代价或风险 |
|---|---|---|
| 最多尝试一次 | 对结果不明的尝试绝不重试 | 副作用可能永远不会发生 |
| 至少尝试一次 | 反复重试,直到观察到完成结果 | 副作用可能执行多次 |
| 带键、等效单次生效的变更 | 始终用同一键重试,由接收方原子地返回第一次变更的结果 | 接收方必须去重,并将键保留足够长时间 |
| 事务耦合的变更 | 在同一事务或事务发件箱中提交日志状态和副作用 | 仅适用于共享事务边界内的状态 |
当执行日志和副作用所有者是两个独立系统时,不加限定地声称“恰好一次”会产生误导。真正可选的办法是幂等、对账、补偿,或者交给操作人员明确决定。
幂等键是一项协议,不是随机字符串
下面的示意代码只有在服务方接受这个键、将它绑定输入指纹,并在重试时返回原结果的前提下才安全:
def run_effect(journal, provider, step_id, tenant, request):
key = journal.stable_key(step_id, tenant, request) # 重试期间保持稳定
if completed := journal.completed(key):
return completed.result
fingerprint = sha256(canonical_json(request)).hexdigest()
journal.schedule(step_id, key, fingerprint)
known = provider.lookup(tenant=tenant, idempotency_key=key)
if known is None:
known = provider.apply(
request,
tenant=tenant,
idempotency_key=key,
input_fingerprint=fingerprint,
)
journal.complete(step_id, key, known.receipt)
return known.result
这个键必须在第一次尝试之前生成并持久化,在重试期间保持不变,限定到租户和操作,并绑定输入指纹,从而拒绝同一键对应不同输入的请求。键的保留期限必须长于最长重放和重试周期。服务方的查询与变更还必须共用同一份去重记录,否则先查询、后变更又会形成一次检查后执行竞态。
如果接收方不提供带键接口,恢复过程就要从对方的权威系统中取得证据,例如查找预期工单、比较 Git 引用、核对账户账本,或者请操作人员判断。补偿可以撤销某些已经提交的副作用,但补偿本身也是一次新的副作用,也有自己的失败方式。事务发件箱可以把数据库变更与待发往其他服务的事件原子地绑定起来,但下游交付仍然需要去重。步骤边界让这些协议可观测,却不能取代协议本身。
步骤粒度同样重要。一个粗粒度步骤如果先读数据库、再调用服务、最后写文件,就可能在恢复时重复一段已经部分完成的序列。更细的步骤可以隔离重试并保存更多中间结果,却会增加历史量和协调开销。步骤边界应落在最小可说明并测试其重试语义的单元上。
检查点必须绑定相互关联的状态
有用的检查点不是简单的“第 27 轮”,而是一组彼此匹配的版本标识:
这份状态包同时绑定执行历史头、工作区版本、记忆版本、工作流版本以及策略和工具目录版本。其中, 是检查点 ; 是执行历史头; 是不可变的工作区快照或版本; 是记忆版本或读取时间戳; 是工作流版本; 是策略和工具目录版本。恢复过程先还原这组状态,再对账所有已经安排但结果未知的步骤。
重放、回退和分叉是三种不同操作:
| 操作 | 历史 | 工作区与记忆 | 外部副作用 |
|---|---|---|---|
| 重放 | 从同一检查点重新计算状态,并复用已有结果 | 恢复检查点指定的版本 | 不重复已经完成的副作用,对账结果未知的副作用 |
| 回退 | 把当前头移到更早的检查点 | 恢复更早的一组自有状态 | 无法自动撤销之后发生的外部副作用 |
| 分叉 | 从共享祖先创建子分支 | 给子分支隔离的可写状态,或者明确共享状态策略 | 外部系统默认仍然共享,除非另行克隆或划分命名空间 |
不可变历史让会话分叉成本很低,因为两条分支都可以引用共享祖先。工作区则不同:如果两个分支都能并发写入,而且写入不能相互泄漏,就必须使用隔离的命名空间。完整复制、写时复制克隆、Git worktree、数据库分支或远程沙箱都可以提供这种隔离。没有版本管理的外部状态不会随之自动分叉。
两条分支确实有合并基点,也就是共同的检查点。如果存储模型保留了这段祖先关系,文件可以进行三方合并。更难的是语义合并:哪些彼此分歧的计划、审批、工具结果和外部副作用应该留下。这属于产品策略,有时还需要人工决定,不能归因于没有共享历史。LangGraph 文档中的时间旅行功能说明了这个区别:重放会重新执行检查点之后的工作,分叉则创建新的检查点分支,不会修改原始历史 (LangChain 2026)。
工作区持久性来自一组叠加的保证
容器文件系统、持久卷、快照和备份回答的是不同问题。容器文件系统通常随容器或 Pod 一起消失。Kubernetes 持久卷可以越过单个 Pod 的生命周期,而临时卷会跟随 Pod (Kubernetes Authors 2026)。但这些事实都不能说明卷能挂载在哪里、是否允许多个节点同时写入、卷声明删除后会发生什么,也不能说明损坏后能否恢复。
这些属性必须分别检查:
| 属性 | 必须回答的问题 |
|---|---|
| 卷的生命周期 | 数据能否跨越容器、Pod、节点和集群更换? |
| 访问模式 | 哪些节点或 Pod 可以挂载卷,分别拥有什么写权限? |
| 拓扑 | 底层存储可以从哪些可用区、区域或主机访问? |
| 回收策略 | 卷声明删除后,底层卷如何处理? |
| 快照一致性 | 快照只保证崩溃一致,还是保证应用一致? |
| 备份独立性 | 主系统中的故障或删除是否也会移除备份? |
| 恢复目标 | 已经实测过怎样的恢复点目标和恢复时间目标? |
ReadWriteOnce 是访问模式,并不表示每个卷都由可用区级块设备支持。拓扑取决于存储后端及其置备策略。同样,删除 Pod 通常不会删除普通持久卷,但通用临时卷声明和明确的保留或回收策略可能有不同结果。设计审查应写明实际的 CSI 驱动、StorageClass、拓扑约束、保留策略和恢复路径,不能只凭 PVC 标签推断。
快照频率只能给出 RPO 上界,不能代替备份
假设系统每隔 分钟瞬时生成一次快照,而且崩溃在两次快照之间各个时点等可能发生。如果 表示最新快照之后累积的工作量,那么:
其中, 是以分钟计的快照间隔; 是可恢复的工作损失; 是均匀崩溃假设下的期望损失; 是下一次快照前的最坏损失; 是每小时请求快照的次数。因此,快照间隔只是恢复点目标(RPO)的简单上界。它不能说明写入字节数、快照耗时、去重、保留期限、恢复时间目标(RTO),也不能证明恢复一定成功。
快照还需要一致性契约。生成应用一致的快照,可能要暂停写入、刷写日志,或记录数据库位置。只有 CSI 驱动和存储后端都支持时,Kubernetes VolumeSnapshot 才能提供标准 API (Kubernetes Authors 2024)。恢复演练必须同时验证工作区、执行历史头以及数据库或记忆版本。看到“快照创建成功”的绿色事件,并不能证明系统可恢复。
克隆成本也取决于底层存储。Kubernetes 可以通过兼容的 CSI 驱动克隆 PVC,但 API 并不承诺具体复制算法或延迟 (Kubernetes Authors 2023)。Btrfs 快照起初共享同一个根,之后通过写时复制隔离变更 (Btrfs Maintainers 2026)。文件级覆盖系统、块级写时复制文件系统、内容寻址清单和完整复制,会产生不同的写时复制与保留成本。写时复制本身也不是完整的性能模型。
微型虚拟机快照进一步说明,为什么必须列出纳入快照的状态。Firecracker 会序列化客户机内存和模拟硬件状态,但磁盘文件需要单独管理;网络连接不保证延续,恢复兼容性也取决于宿主机和快照版本 (Firecracker Maintainers 2026)。克隆虚拟机状态还可能复制随机数生成器状态和标识符,所以恢复路径可能需要重新生成密钥、身份并重连服务 (Brooker et al. 2021)。一份快照是否完整,只取决于它明确纳入了哪些状态。
底层存储决定检查点和分叉究竟是元数据操作还是完整复制。运行框架可以请求分叉,但真正决定隔离性、延迟、变更字节成本和恢复限制的,是 CSI 驱动、文件系统、数据库、对象存储或微型虚拟机层。检查点契约必须暴露这些下层事实,不能把所有分叉都写成同样便宜的操作。
长期记忆是一条受治理的写入、管理与读取闭环
执行历史回答运行时接受了什么。长期记忆回答哪些经过选择的信息应影响后续决定。关键正是选择:原始历史可以作为记忆的输入,但保留每一条事件只是日志记录,不是记忆策略。
两个有影响力的系统展示了不同机制。Generative Agents 保存自然语言形式的经历流,从中综合出更高层的反思,再检索记录供后续规划使用 (Park et al. 2023)。MemGPT 把有界上下文窗口和外部存储视作不同层级,通过明确的数据移动管理它们 (Packer et al. 2023)。两者都并不规定唯一的存储方式。日志、表格、文件、摘要、图、词法索引、向量索引以及混合方案,都能实现闭环中的一部分。
人们常用情景记忆、语义记忆和程序记忆来类比事件、事实和学到的行动方式,但它们并不是互斥的数据库类别 (Squire 2004)。工程设计应把彼此正交的选择分开:
| 维度 | 示例 |
|---|---|
| 内容 | 事件、事实、偏好、过程、经验、产物引用 |
| 范围 | 智能体、用户、项目、租户、组织或共享 |
| 表示方式 | 日志、记录、文件、图、词法索引、向量索引或混合方案 |
| 生命周期 | 追加、修订、取代、过期、隔离、删除 |
| 来源 | 引用的观测、工具结果、用户确认的陈述、派生摘要 |
| 检索控制 | 运行框架预加载、规则触发查询、模型调用工具或混合控制 |
检索增强生成(RAG)是一种检索模式,不是一种所有权类别。RAG 语料库可以私有且可变,记忆存储也可以共享且只读。真正有用的区别是生命周期:记忆通常会随着智能体交互而更新,外部语料库则可能由另一套摄取流程维护。第 44 章(第 44 章)会详细讨论检索本身。
记忆记录需要来源与时间
一条持久记忆记录不能只有文本或嵌入向量,还应带上这些字段:
MemoryRecord {
id, scope, subject, kind, payload,
source, derivation, recorded_at, valid_from, valid_until,
version, confidence, sensitivity, purpose, retention_until,
status, supersedes
}
scope 表示获准读取这条记录的范围,subject 表示记录所涉及的人或实体,两者不是一回事。source 链接原始观测或工具结果,derivation 说明摘要或推断是如何产生的。recorded_at 是系统时间,valid_from 和 valid_until 描述事实在哪段时间内成立。status 区分有效、已被取代、隔离和已设墓碑的记录,supersedes 则把更正链接到被替换的版本。其余字段把保留期限、敏感性、用途和置信度变成可测试的策略输入。
完整闭环包括六步:
- 提出写入。 从观测或结果中提取候选记录。
- 授权并分类。 确认写入者、范围、主体、用途、来源、敏感性和有效期。
- 管理。 去重、检测冲突、插入新版本、取代过期记录、隔离可疑内容,或者拒绝写入。
- 检索。 先根据可信身份和用途限制候选集,再在上下文预算内搜索和重排。
- 作为证据使用。 保留归因,把检索内容放在可信指令之下;记忆本身绝不授予权限。
- 修订或删除。 把更正、到期、保留和删除规则应用到来源及每一种派生表示。
检索也必须受授权约束
对于查询 ,下式先定义授权候选集,然后才允许记录进入模型:
这里还要在预算内完成检索:
其中, 是时刻 的记忆; 是一条记录; 是经过身份验证的主体; 是获准的用途;allow 检查范围和敏感性;live 检查有效期与保留期限; 是获准候选集; 是查询; 是词元或字节预算; 是最终检索集合。retrieve 可以使用词法、向量、图、键值或混合检索,rerank 负责排序,TopBudget 则不断接纳记录,直到用完预算。
这条不变量可以由可信检索服务、物理分区、数据库策略或多层防御共同执行。用户自行提供的租户标签不等于经过身份验证的主体。例如,PostgreSQL 行级安全可以限制普通读写,但表所有者、超级用户和具有 BYPASSRLS 权限的角色需要单独处理并测试 (PostgreSQL Global Development Group 2026)。真正的安全属性是零条未授权候选,而不是很高的相关性分数。
持久记忆还会让投毒内容存活得更久。恶意或错误陈述可能被提取、摘要,并在之后的会话中再次检索;AgentPoison 展示了针对智能体记忆和知识库的这类攻击 (Chen et al. 2024)。系统应保存写入者和来源,区分引用证据与派生推断,隔离可疑写入,并且绝不能把检索到的记录当作高权限指令。会话日志和 RAG 语料库也会携带同类攻击,记忆的特殊之处在于,跨会话复用本来就是默认路径。
删除必须沿着数据血缘传播。如果删掉源记录,却留下对应的嵌入、摘要、图边、缓存或导出副本,线上系统仍然保留这些信息。删除清单应列出所有派生物,记录各项完成状态,防止恢复备份时复活已设墓碑的记录,并说明备份何时自然过期。具体法律义务取决于司法辖区和用途;工程契约则是可发现、可归因的数据血缘,以及在声明期限内可验证的删除。
分阶段评估,不能只看最终答案
不同记忆基准考查不同能力。LoCoMo 使用跨多次会话的长对话,评测问答、事件摘要和多模态对话生成 (Maharana et al. 2024)。LongMemEval 关注信息抽取、多会话与时序推理、知识更新和弃答,并分别分析索引、检索和阅读 (Wu et al. 2025)。MemoryAgentBench 加入增量交互,测试准确检索、测试时学习、长程理解和选择性遗忘 (Hu et al. 2026)。这些基准都无法证明租户隔离、抗投毒能力或删除完整性。
评估必须把故障定位到具体阶段:
| 阶段 | 测量项 |
|---|---|
| 写入与维护 | 有价值写入的精确率和召回率、重复率、来源准确性、冲突解决、投毒准入、陈旧记录存活率 |
| 检索 | Recall@、Precision@、授权召回率、陈旧或冲突记录召回、无证据时弃答、p50/p95 延迟、检索词元数 |
| 使用 | 有依据的回答或任务成功、证据归因、适当弃答、下游行动成功率 |
| 治理 | 跨租户负向测试、导出与更正、删除延迟与完整性、备份恢复后的复活测试 |
| 运营 | 索引与存储增长、写放大、模型调用、词元成本、缓存行为和恢复时间 |
比较系统时,应固定相同的模型检查点、提示词和策略版本、任务划分、写入预算、检索预算、工具权限、裁判和评分标准、硬件和缓存状态,以及重复运行策略。只要适合任务,还应加入没有记忆、完整历史、词法、向量和混合基线。否则,更强的模型、更大的上下文预算或更宽松的授权边界,都可能被误认为更好的记忆系统。
不存在普遍最优的记忆表示方式或基准。对话问答会奖励召回,却未必说明记忆是否改善了后续行动。完整历史可能准确但昂贵;摘要可能便宜却抹掉来源;向量检索可以找到同义改写,却可能漏掉精确标识符;图可以保留关系,却依赖容易出错的抽取。除非模型、数据划分、预算、裁判和运行边界一致,否则不能比较不同供应商公布的分数。应把记忆质量当作一项需要实测的系统属性,而不是存储品牌排行榜。
运行契约
上线持久智能体状态之前,应写明下面这份契约:
| 领域 | 必须说明的内容 |
|---|---|
| 重放 | 哪些事件持久保存、由哪个代码版本重放,以及非确定性结果记录在哪里 |
| 副作用 | 交付语义、键的范围与保留期限、对账证据、补偿和升级处理 |
| 检查点 | 绑定的执行历史头、工作区版本、记忆版本、策略版本和恢复顺序 |
| 工作区 | 卷的生命周期、访问模式、拓扑、快照一致性、RPO、RTO、保留期限和经过测试的恢复路径 |
| 分叉 | 克隆哪些自有状态、哪些状态仍然共享、如何隔离命名空间、怎样合并以及何时过期 |
| 记忆写入 | 来源、范围、有效期、冲突、敏感性和确认规则 |
| 记忆读取 | 可信主体和用途、授权边界、检索预算和证据格式 |
| 生命周期 | 检查、更正、到期、删除血缘、备份处理和验证 |
| 评估 | 分阶段质量、成本、恢复、投毒、跨租户和删除测试 |
这份契约把运行时恢复与记住的知识分开,同时明确二者如何耦合。下一章把问题收窄到一个特定所有者:用于跨会话个性化的持久用户状态。
延伸阅读
- Amazon Web Services, “Idempotency for AWS Lambda Durable Functions,” 2026. docs.aws.amazon.comLambda 持久步骤默认采用至少一次执行;中断的工作可能重复,因此有副作用的业务逻辑仍需要幂等键或其他去重协议。
- Temporal Technologies, “Activity Definition,” 2026. docs.temporal.ioTemporal 会记录已完成活动以供重放,但未能报告完成的活动可能被重试并执行多次。
- LangChain, “Use Time Travel,” 2026. docs.langchain.comLangGraph 区分重放与分叉:重放重新执行检查点之后的节点,分叉则创建新的检查点分支而不修改原历史。
- Kubernetes Authors, “Volumes,” 2026. kubernetes.ioKubernetes 区分随 Pod 生命周期结束的临时卷与超越单个 Pod 生命周期的持久卷;拓扑、访问、回收与备份仍是独立属性。
- Firecracker Maintainers, “Snapshotting Support,” 2026. github.comFirecracker 快照序列化来宾内存与模拟硬件状态,而磁盘文件、连接、兼容性及未纳入的运行数据需要另行处理。
- Park et al., “Generative Agents: Interactive Simulacra of Human Behavior” (后来一切记忆系统都在回响的记忆流与反思循环), 2023. arXiv:2304.03442按新近度、重要性与相关性打分检索的记忆流,加上定期把经验综合成高层记忆的反思机制:后来所有记忆系统都在回响的概念模板。
- Packer et al., “MemGPT: Towards LLMs as Operating Systems” (记忆的操作系统式表述), 2023. arXiv:2310.08560MemGPT 提出虚拟上下文管理方案,借鉴操作系统分层存储思想,在大语言模型(LLM)固定上下文窗口与外部存储之间分页调度数据,实现文档分析与多轮对话中的无限上下文。
- Chen et al., “AgentPoison: Red-Teaming LLM Agents via Poisoning Memory or Knowledge Bases” (针对智能体记忆与知识库的定向投毒), 2024. proceedings.neurips.ccAgentPoison 评估对智能体长期记忆或检索知识库实施投毒的定向后门攻击。
- Maharana et al., “Evaluating Very Long-Term Conversational Memory of LLM Agents” (LoCoMo,记忆初创公司们争夺的基准), 2024. aclanthology.org横跨多次会话、数百轮的超长期对话,配问答与事件摘要任务:站在记忆系统之争中心的那个基准。
- Wu et al., “LongMemEval: Benchmarking Chat Assistants on Long-Term Interactive Memory” (评估信息抽取、多会话推理、时序推理、知识更新和弃答), 2025. proceedings.iclr.ccLongMemEval 使用可扩展对话历史与 500 个问题测试五种记忆能力,并区分索引、检索与阅读阶段。
- Hu et al., “Evaluating Memory in LLM Agents via Incremental Multi-Turn Interactions” (提出 MemoryAgentBench), 2026. arXiv:2507.05257MemoryAgentBench 在增量交互中评测准确检索、测试时学习、长程理解与选择性遗忘。
评论
登录后评论