Agent Mentor Learn
状态管理与持久化:让长任务经得起中断 · 第 1 / 6 节

第 1 课:记忆之外还有状态

学习目标:

  • 用一句话区分「记忆」(喂给模型的上下文)与「执行状态」(harness 攥着的运行现场),并各举一个例子
  • 说出至少三条支撑「崩溃对长任务格外致命」的具体依据,并说清它们分别指向哪个环节
  • 给定一份运行中的信息清单,能判断每一项属于记忆、执行状态还是磁盘产出,以及进程崩溃后它是否还在 前置要求:完成本系列前 8 门课,理解 stop_reason 驱动的 harness 循环(第 7 门课)与上下文管理(第 8 门课) | 下一课 第 2 课 >>

第 23 轮,进程被杀

设想你在第 7 门课写的那个 harness,正跑一个要转 40 轮的长任务:读一批文件、逐个分析、边跑边把结论追加进报告。跑到第 23 轮,进程被杀了——可能是一次部署更新,可能是服务器掉电,也可能只是你手滑按了 Ctrl+C。

你去看磁盘:前 22 轮生成的报告文件都还在;NOTES.md(第 8 门课装的那份任务进展摘要)里也记着「已经分析到第几个文件、结论是什么」。看起来损失不大,重启进程接着跑就是了。

但你重启后才发现,runAgent 里那个 messages 数组是空的——它得从 [{ role: "user", content: userInput }] 重新开始拼。turns 计数器归零了。tokensUsed 也归零了。如果崩溃恰好发生在第 23 轮「模型点名要调工具」和「工具结果塞回 messages」之间,那次工具调用——不管它当时是刚开始跑还是已经跑完——现在也没了痕迹。

任务不是从第 23 轮接着跑,是从第 1 轮重新开始。磁盘上的文件都在,但 harness 自己那份「跑到哪儿了」的记录,一点没留下。

记忆是什么,状态是什么

这门课要讲的东西,得先跟一个容易混的概念划清界限。

记忆,是喂给模型的上下文内容——本系列第 5 门课讲跨会话怎么把它存下来、第 8 门课讲每一轮具体喂给模型看什么。它回答的问题是「模型看见了什么」。NOTES.md 就是记忆的一种载体:它已经写在磁盘上,下一轮、下一次会话,都可以被读回来塞进 prompt。

执行状态,是 harness 进程自己攥着的那份运行现场——messages 数组、turnstokensUsed 这类计数器、还没落账的工具调用。它回答的问题是「harness 记得跑到哪儿了」。

这两者的关键差别不在「内容是什么」,而在「此刻它活在哪儿」。记忆可以已经落盘(NOTES.md 就摆在文件系统里,进程死不死都不影响它还在);执行状态默认只活在进程的内存里——let messages = [...] 这行代码创建出来的东西,没有人专门把它写到磁盘之前,进程一退出就随内存一起被回收。上一节里 messagesturnstokensUsed、悬空的工具调用,没有一样是自动落盘的。

这份 state,本课就是要教你怎么把它变成能落盘、能恢复的东西。

为什么这对长任务是命门

先把账算清楚:Agent 可以长时间运行,跨很多次工具调用维持状态1——这正是第 23 轮那份 messages 越滚越大的原因。可这也意味着 Agent 是有状态的,而错误会复利1:状态越攒越多,一旦哪个环节出岔子,代价不是线性的,是叠加的。

再加一条:没有有效缓解手段的话,一次很小的系统故障,对 Agent 来说都可能是灾难性的1。「小」和「灾难性」这两个词摆在一起,说的正是第 23 轮这种情况——进程被杀本身只是个寻常的运维事件,可它把 22 轮的执行状态一次性清零,代价就被放大了。

出了错之后,不能干脆从头重启:重启是昂贵的,也会让用户沮丧1。所以要构建的是能从出错的地方接着跑的系统1——这也是为什么「记忆之外还有状态」不是一个学院派的概念区分,而是决定长任务能不能扛住中断的地基。

任务越长,这笔账越重:模型可能要连续跑很多轮,而你对它每一步决策的信任是有限度的2;自主运行的本质,天然带着更高的成本和误差累积的风险2。转的轮数越多,攒下的执行状态越厚,一次中断能抹掉的东西也越多。

崩溃不是稀罕事

第 23 轮那次「进程被杀」,听起来像个小概率的意外,但对一个要跑几十轮的长任务来说,中途被打断其实算不上稀奇。就连最正常不过的一次部署更新,都可能撞上运行中的 Agent——所以才需要专门用彩虹部署,把流量从旧版本逐步切到新版本,来避免打断正在运行的 Agent1,原因是:只要一部署,Agent 可能正处在它整个流程的任意一步1

换句话说,连负责运维的人自己都默认「Agent 随时可能被打断」是会发生的事,要专门设计机制去规避它。你的 harness 没有理由假设自己会更幸运。

解药预告:本课的路线图

第 23 轮丢掉的那份执行状态,解法分五步,也是接下来五课的顺序:

  1. 检查点(第 2 课)——把 messagesturnstokensUsed、悬空的工具调用这些执行状态,定期序列化写到磁盘上的一个检查点文件里。
  2. 断点恢复(第 3 课)——进程重启后,从检查点文件里把这份状态读回来,重建 messages,让循环从断的地方接着转,而不是从第 1 轮重来。
  3. 副作用与幂等(第 4 课)——恢复时最麻烦的不是「状态没了」,是「有些工具调用可能已经真的执行过了」:哪些工具重跑一次没关系,哪些必须防止重复执行。
  4. 回退与分叉(第 5 课)——检查点不只是用来救灾的,它还能让你回退到早先的现场重试,或者从某个节点分叉出另一条尝试路线。
  5. 实战(第 6 课)——把前面几课的机制,装进第 7 门课那个 harness 里,亲手跑一次「中途被杀,重启后接着跑到完成」的长任务。

有一份以「harness 工程」为主线的社区开源路线图,把这条路线概括成一句话:给每一步都建检查点,这样才能恢复、回退、分叉3——这只是社区文档对持久化这个组件职责的一句框架性概括,不是本课要逐字照搬的规范,但它点出的顺序,和上面五步基本对得上。

分寸:不是所有任务都需要这套

不是每个 Agent 都要背上检查点这套机制。加复杂度这件事,值得只在它确实能明显改善结果的时候才考虑2——一个几轮工具调用就能收尾的任务,messages 数组统共没几条,进程死了直接重跑,代价也就是重新问一次,犯不上专门设计一套落盘与恢复的机制。

真正用得上本课这套东西的,是像开头第 23 轮那样的场景:任务要跑几十轮、耗时可能是几分钟到几小时、中途累积了大量还没落盘的执行状态。判断要不要引入检查点,先问自己一句:如果现在被杀,重跑一遍的代价你能不能接受?接受不了,才轮到接下来几课登场。

小结

  • Agent 可以长时间运行、跨很多次工具调用维持状态,也正因此是有状态的、错误会复利1——状态攒得越多,一次故障能抹掉的东西也越多。
  • 没有有效缓解手段时,一次很小的系统故障对 Agent 可能是灾难性的1;出错后不能从头重启,因为重启昂贵、也让用户沮丧,所以要构建能从出错处恢复的系统1
  • 崩溃不是稀罕事——连正常的部署更新都要专门用彩虹部署来避免打断运行中的 Agent,因为部署时 Agent 可能正处在流程的任意一步1
  • 自主运行天然带着更高成本和误差累积的风险2,任务越长,这笔账越重,也越需要一套能扛住中断的机制。
  • 不是所有任务都要背上这套机制——加复杂度只在它确实能明显改善结果时才考虑2,几轮就能收尾的任务,直接重跑的代价通常可以接受。
  • 记忆(喂给模型的上下文)和执行状态(harness 攥着的运行现场)是两回事:记忆可能已经落盘,执行状态默认只活在进程内存里——接下来几课,就是把这份执行状态也变成能落盘、能恢复的东西。

>> 第 2 课:检查点:把执行现场落盘

Footnotes

  1. How we built our multi-agent research system — Anthropic Engineering — https://www.anthropic.com/engineering/multi-agent-research-system 2 3 4 5 6 7 8 9 10 11

  2. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2 3 4 5

  3. The 2026 Agent Engineering Roadmap — GitHub (codejunkie99/agent-roadmap-2026) — https://github.com/codejunkie99/agent-roadmap-2026

练习

01

下面是一个正在运行的 harness 涉及的六样「东西」。请把每一样归到「记忆」「执行状态」「磁盘产出」三类之一,并说明:如果进程在这一刻被杀,它还在不在。

Level 1:给六件运行中的「东西」分类
  1. NOTES.md 里已经写好的任务进展摘要(第 8 门课装的那份)
  2. 内存里的 messages 数组
  3. 已经写到磁盘上的报告文件 report.md
  4. turns 计数器(当前第几轮)
  5. 模型刚生成的这一轮响应文字里,对项目结构的一段分析——还没来得及被写进 NOTES.md
  6. 一个模型已经点名要调用、但工具还没跑完、结果也还没塞回 messagestool_use
完成标准 · 本地勾选
02

回到开头的场景:第 7 门课那个 runAgent(用 messages、turns、tokensUsed 这几个变量驱动 stop_reason 循环)正在跑一个 40 轮的任务,第 23 轮进程被杀,而且崩溃恰好发生在「模型点名要调一个工具」和「工具结果塞回 messages」之间。不用写代码,只用文字完成两件事:

Level 2:给第 7 门课的 harness 列一份崩溃损失清单
  1. 崩溃损失清单:这次崩溃具体丢了什么?哪些东西没受影响,还在?
  2. 检查点该存什么:如果这个 harness 装了检查点机制,你觉得检查点文件至少要存哪几个字段,才能把这次损失降到最低?列出字段名,并说明每个字段为什么要存。
完成标准 · 本地勾选