第 14 章:持久化不是顺手存一下,而是让 Agent 真正拥有长期状态
本章目标:讲清 Agent 系统为什么迟早要面对状态落盘问题,以及持久化到底在保存什么、解决什么、又会带来什么新的复杂度。
本章对应总纲:docs/ebook-outline.md中“第 14 章正文写作提纲(2026-03-31 归档)”。
14.1 为什么“把聊天记录存起来”远远不等于持久化
很多人一提 Agent 持久化,第一反应就是:
- 把消息存数据库
- 把对话历史落盘
- 下次再读回来
这当然算一部分,但远远不够。
这里先把边界说死:这一章讲的是系统需要把哪些关键状态稳定保存下来,不是讲系统怎样长期维护。 “能不能长期活着”是后面第 19 章要处理的工程闭环问题;这一章只回答一个更基础的问题:哪些东西一丢,系统就没法连续地当成同一个系统来运行。
因为一个真实 Agent 系统里,真正需要跨进程、跨时间保留下来的,往往不只是几段文本,而是:
- 会话现在走到哪了
- 哪些步骤已经执行过
- 哪些工具已经调用过
- 哪些外部副作用已经发生
- 哪些产物已经生成
- 哪些确认还在等待用户处理
所以持久化不是“顺手存一下聊天记录”,而是:
把系统继续运行所需要的关键状态,从易失内存里拿出来,变成可恢复、可追踪、可复用的长期表示。
14.2 为什么服务化之后,持久化几乎会变成必选项
前一章已经讲过,一旦 Agent 走向服务化,很多任务都不再是短平快的一次调用。
这会马上带来几个现实问题:
- 服务重启后任务怎么办
- 客户端断开后结果去哪找
- 多个终端怎么共享同一个会话
- 长任务怎么继续推进而不是整段重来
- 人工审核怎么回看之前发生过什么
如果这些问题都答不上来,系统本质上还是一次性脚本,只是外面套了个服务壳。
所以很多 Agent 一旦进入多用户、长任务、异步执行阶段,持久化就不再是优化项,而是地基。
14.3 Agent 系统里,通常到底要持久化什么
14.3.1 会话本身
最基础的是会话容器。
它至少要回答:
- 这是哪个任务上下文
- 属于哪个用户或工作空间
- 当前处于什么阶段
- 最后一次更新时间是什么
如果连会话都没有稳定身份,后面所有恢复都无从谈起。
14.3.2 消息与事件
很多系统会保存消息,但更成熟的系统通常还会保存事件。
因为真正有用的往往不只是“说过什么”,还包括:
- 调用了什么工具
- 收到了什么错误
- 发生了什么状态切换
- 何时进入等待确认
- 何时被取消或完成
消息是表层叙事,事件才更接近运行轨迹。
14.3.3 执行状态
这部分最容易被低估。
例如:
- 当前计划执行到第几步
- 哪些子任务已完成
- 哪些失败路径已经证伪
- 当前是否等待外部输入
- 当前是否允许自动继续
如果这些状态不单独存,你就只能靠回读长历史去猜。这既慢又不稳。
14.3.4 产物与副作用记录
Agent 做事不只会产出文字,还会产出各种 artifact:
- 修改过的文件
- 生成的报告
- 执行日志
- 外部请求结果
- 审批记录
而且有些动作不是“建议”,而是真正改变了世界。
这些副作用如果不留记录,系统恢复时就很容易出现最蠢的问题:
- 该做的已经做过了,却又做一遍
- 不该重放的动作被重放
- 产物还在,但上下文忘了它怎么来的
14.4 持久化真正解决的,不只是恢复,还有一致性
很多人会把持久化理解成“系统挂了还能接着干”。
这当然重要,但还不够。
更深的一层是:持久化在帮系统维持一致性。
因为 Agent 系统里经常同时存在这几样东西:
- 内部状态
- 用户看到的进度
- 外部副作用
- 生成产物
- 等待中的授权
如果这些东西彼此不同步,系统就会变得很荒唐。
例如:
- 数据库里说任务已完成,但产物还没落好
- 系统说正在等待授权,但实际上动作已经执行了
- 用户界面显示失败,但后台已经改了文件
所以持久化不只是“存下来”,而是要让系统各层对“现在到底发生了什么”达成一致。
14.5 为什么持久化会逼你重新定义状态边界
一旦决定落盘,你就必须把一个以前可以含糊带过的问题说清楚:
到底哪些东西算状态,值得被保存?
这会逼你重新切开几类东西:
应该持久化的
- 会话身份
- 关键执行位置
- 明确的任务阶段
- 外部副作用记录
- 最终产物指针
- 审计和授权记录
不该直接持久化的
- 临时推测
- 一次性中间草稿
- 只对当前推理有用的局部噪音
- 可以从别处可靠重建的缓存
如果什么都存,系统会越来越脏;如果什么都不舍得存,系统又永远无法恢复。
好品味在这里不是“存得最全”,而是“只存真正构成运行连续性的东西”。
14.6 持久化之后,系统会立刻多出三种新成本
14.6.1 模型和存储模型的映射成本
脑子里觉得“任务在第 3 步等待确认”很直观,落到数据库里就没那么直观了。
你必须决定:
- 用什么数据结构表示阶段
- 怎么表示子任务依赖
- 怎么表示一条工具调用的生命周期
- 怎么表示已经发生的副作用
这其实是在把运行时世界翻译成存储世界。
14.6.2 一致性成本
任何涉及外部副作用的系统,一旦持久化,就会碰到经典难题:
- 先写库还是先执行动作
- 动作成功但落库失败怎么办
- 落库成功但动作其实失败怎么办
- 重试时怎么避免重复副作用
这不是文档问题,这是系统正确性问题。
14.6.3 演化成本
一旦状态结构上线,它就会被后续版本、后续任务、后续恢复逻辑依赖。
所以持久化模型不是随便改着玩的。设计烂了,后面每次升级都会付出代价。
14.7 记忆系统和持久化不是一回事
这里特别容易混。
前面讲过记忆系统,那是从“系统该记住什么事实”来看的。
而持久化更像是在问:
- 当前会话怎么恢复
- 当前任务怎么延续
- 已发生动作怎么留痕
- 结果怎么被别的系统再利用
所以两者虽然有交集,但不是同一个层。
你可以这么记:
- 记忆系统 更偏“未来判断还要不要用这条信息”
- 持久化系统 更偏“运行连续性和审计连续性要不要依赖这条信息”
混写以后,设计就会很别扭。
14.8 用 Claude Code 反着看什么该持久化、什么不该
Claude Code 这种本地 Agent 环境很适合用来反着理解持久化边界。
从当前仓库就能看到几个很具体的分层信号:
- 根级
package.json只定义了docs:dev、docs:build、docs:preview这类文档站脚本,说明这个仓库里的很多“长期信息”并不靠应用数据库维持,而是靠文件系统中的文档与配置来承载 docs/下面同时存在ebook-chapter-*、part*/chapter*.md、ebook-outline.md这些内容文件,说明内容产物本身就是一种需要长期保存、反复回看的 artifact.vitepress/config.ts把导航、侧边栏、搜索、srcExclude等站点行为单独放在壳层配置里,说明“如何展示”也是独立状态,而不是散落在正文里的临时信息examples/settings/settings-strict.json这类配置样板把ask、deny、allowManagedHooksOnly、sandbox这些约束显式落盘,说明权限边界如果要复用、审计和部署,就不能只活在某次会话里
你能明显看到它把不同东西分在不同层:
- 当前对话流有自己的上下文
- 任务和计划有独立追踪机制
- 持久记忆保存长期协作信息
- 项目文档、配置和工作流文件又保存了另一类可以被复用和审计的长期状态
- 某些会话级状态又明显不该被长期保留
这恰好说明一个成熟 Agent 系统不会把所有东西都塞进同一个“历史记录”。
真正关键的是分层:
- 哪些只属于当前推理
- 哪些属于运行状态
- 哪些属于长期记忆
- 哪些属于配置与策略状态
- 哪些属于可审计的产物与副作用
14.9 什么时候你真的该认真做持久化
不是所有本地原型都值得一上来就设计一整套状态存储。
但只要出现下面这些信号,就说明不能再糊了:
- 任务会持续很久
- 任务可能跨进程或跨机器继续
- 用户稍后还要回来补看结果
- 多个入口要访问同一会话
- 系统需要审计或回放执行轨迹
- 外部副作用不能靠“记得差不多”来处理
一旦走到这里,再把状态只放在内存里,基本就是在赌系统别重启。
14.10 持久化不是让系统更重,而是让系统开始像系统
很多人会觉得,一做持久化,系统立刻变重、变慢、变麻烦。
这话不完全错。
但另一面是:如果你的 Agent 已经承担了长任务、异步流程、外部副作用和结果复用,那没有持久化,它就根本还不像个成熟系统。
所以问题不是“持久化会不会带来复杂度”,而是:
当前这套复杂度,到底是系统真实需求,还是你还没走到那一步。
如果还没走到,不必硬上;如果已经走到,不做才是装傻。
14.11 本章小结
这一章真正想讲清的是:持久化解决的不是“顺手存点历史”,而是把系统继续运行所依赖的关键状态,从易失内存里拿出来,变成可恢复、可追踪、可复用的长期表示。
它回答的是“哪些状态不能丢”,不是“系统怎样长期维护”;后者属于部署、测试、评估、观测与工程纪律这一整圈问题。
你现在应该记住六件事:
- 聊天记录只是持久化的一小部分,不是全部。
- 服务化、多用户、长任务和外部副作用,会自然把系统推向持久化。
- 真正该持久化的是会话、事件、执行状态、产物和副作用记录。
- 持久化的深层价值不只是恢复,还有系统一致性。
- 记忆系统和持久化系统有关,但不是一回事。
- 一个想长期运行、可恢复、可审计的 Agent,迟早要认真处理状态落盘。
下一章我们继续往下走,讲交互承载层:不是单纯讨论 UI 长什么样,而是为什么 Agent 的界面本质上是在承载任务状态、流式反馈和人类介入点。