AI Agent 长任务总在中断后跑偏?一份 workflow-state.md 把它变成可恢复流程
做 AI Agent 长任务时,最浪费时间的场景往往不是某一步做错,而是:资料查完、脚本写到一半、部分结果已经出来;会话一中断,下一轮又从头搜索,甚至把已验证的步骤重做一遍。
这通常不是模型“记忆力不好”。真正的问题是,任务事实散落在对话、终端输出和临时文件里,没有一份能让下一轮直接接手的状态。
本文给出一个不依赖框架的办法:用一份 workflow-state.md,把长任务从“靠上下文续命”变成“按状态恢复”。它适合 Codex、Claude Code、Cursor,也适合你自己写的自动化脚本。
1. 哪些信息必须留下来
不要把整段对话复制进状态文件。真正有用的只有四类事实:
| 目标 | 最终要交付什么 | 一份可运行的导入脚本和校验报告 |
| 已验证项 | 已通过、无需重做的步骤 | 100 条源数据已完成格式校验 |
| 当前卡点 | 精确停在何处、为什么停 | import-003 超时,结果未知 |
| 下一动作 | 恢复时的第一件事 | 先检查目标表是否已有 import-003 |
其中最重要的是“结果未知”。网络超时、页面断开、模型生成超时,都不等于失败。没确认前立刻重试,最容易造成重复写入、重复扣费或覆盖已有成果。
2. 一份可以直接复制的状态文件
在项目根目录新建 workflow-state.md:
# 任务状态
## 目标
– 将 `source/` 中的 CSV 导入本地 SQLite,并输出 `reports/check.md`。
## 不可违反的约束
– 不修改源 CSV。
– 已成功导入的批次不得重复写入。
– 只在校验通过后生成最终报告。
## 已完成
– [x] `source/` 文件清单已固定:12 个 CSV。
– [x] `import-001`、`import-002` 已校验通过。
## 当前状态
– `import-003`:请求超时;结果未知。
– 已知输出位置:`data/app.db`。
## 恢复步骤
1. 查询 `data/app.db` 是否已有 `import-003` 的批次记录。
2. 有记录则执行行数和哈希校验;无记录才重试导入。
3. 通过后更新“已完成”,再处理 `import-004`。
这份文件的标准不是漂亮,而是一个没参与前文的人能否在 30 秒内知道接下来该做什么。
3. 恢复前,先做一次只读核对
长任务恢复时,最危险的动作不是“继续”,而是“盲目继续”。先检查日志、落盘文件和目标系统,再决定是否重试:
#mermaid-svg-Ol4FhyrlxtoDeTIc{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ol4FhyrlxtoDeTIc .error-icon{fill:#552222;}#mermaid-svg-Ol4FhyrlxtoDeTIc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ol4FhyrlxtoDeTIc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .marker.cross{stroke:#333333;}#mermaid-svg-Ol4FhyrlxtoDeTIc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ol4FhyrlxtoDeTIc p{margin:0;}#mermaid-svg-Ol4FhyrlxtoDeTIc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .cluster-label text{fill:#333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .cluster-label span{color:#333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .cluster-label span p{background-color:transparent;}#mermaid-svg-Ol4FhyrlxtoDeTIc .label text,#mermaid-svg-Ol4FhyrlxtoDeTIc span{fill:#333;color:#333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .node rect,#mermaid-svg-Ol4FhyrlxtoDeTIc .node circle,#mermaid-svg-Ol4FhyrlxtoDeTIc .node ellipse,#mermaid-svg-Ol4FhyrlxtoDeTIc .node polygon,#mermaid-svg-Ol4FhyrlxtoDeTIc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ol4FhyrlxtoDeTIc .rough-node .label text,#mermaid-svg-Ol4FhyrlxtoDeTIc .node .label text,#mermaid-svg-Ol4FhyrlxtoDeTIc .image-shape .label,#mermaid-svg-Ol4FhyrlxtoDeTIc .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ol4FhyrlxtoDeTIc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ol4FhyrlxtoDeTIc .rough-node .label,#mermaid-svg-Ol4FhyrlxtoDeTIc .node .label,#mermaid-svg-Ol4FhyrlxtoDeTIc .image-shape .label,#mermaid-svg-Ol4FhyrlxtoDeTIc .icon-shape .label{text-align:center;}#mermaid-svg-Ol4FhyrlxtoDeTIc .node.clickable{cursor:pointer;}#mermaid-svg-Ol4FhyrlxtoDeTIc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .arrowheadPath{fill:#333333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ol4FhyrlxtoDeTIc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ol4FhyrlxtoDeTIc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ol4FhyrlxtoDeTIc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ol4FhyrlxtoDeTIc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ol4FhyrlxtoDeTIc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ol4FhyrlxtoDeTIc .cluster text{fill:#333;}#mermaid-svg-Ol4FhyrlxtoDeTIc .cluster span{color:#333;}#mermaid-svg-Ol4FhyrlxtoDeTIc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ol4FhyrlxtoDeTIc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ol4FhyrlxtoDeTIc rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ol4FhyrlxtoDeTIc .icon-shape,#mermaid-svg-Ol4FhyrlxtoDeTIc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ol4FhyrlxtoDeTIc .icon-shape p,#mermaid-svg-Ol4FhyrlxtoDeTIc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ol4FhyrlxtoDeTIc .icon-shape .label rect,#mermaid-svg-Ol4FhyrlxtoDeTIc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ol4FhyrlxtoDeTIc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ol4FhyrlxtoDeTIc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ol4FhyrlxtoDeTIc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
存在
不存在
不确定
状态为 generating 或 timeout
检查日志、落盘文件和目标系统
结果是否存在?
校验后标记 passed
标记 failed,再决定是否重试
停止写入,继续定位
这一步尤其适合外部 API、批量图片生成、数据导入和线上发布。它把“我觉得应该失败了”改成“我已经确认没有结果,可以安全重试”。
4. 给每个昂贵步骤一个稳定 ID
“再跑一次第 3 批”不够可靠。更好的做法是为任务分配稳定 ID:
research-001
import-003
render-012
review-012
ID 一旦创建就不改。日志、输出文件、状态文件都引用同一个 ID。这样无论是人工接手、Agent 恢复,还是隔天回查,都不需要猜“第 3 批”具体是哪一次。
5. 从一个小任务开始
第一次不必给整个项目建状态系统。选一个会跨两轮完成的任务:写清目标和三条约束,为批次分配稳定 ID,每完成一批更新“已完成 / 当前 / 下一步”,再主动中断一次,只靠状态文件恢复。
如果能顺利恢复,说明你得到的不是一份日报,而是一套可复用的任务记忆。
结语
上下文窗口适合思考,不适合承担项目记忆。把可验证的事实写进状态文件,Agent 才能在会话中断、模型切换或人工接手后继续向前,而不是反复从起点出发。
你在长任务中最常丢失的是进度、文件位置,还是“为什么当时没有继续做”?欢迎留言,我会把高频问题整理成下一篇的恢复检查清单。


![[Android] ChatGPT 最新 -全球顶级AI大模型-171主机测评](https://www.171host.com/wp-content/uploads/2026/11/20261102155225-6ae8b1b96af4c-220x150.jpg)