Agent Runtime 与持久执行 ​
阅读路径:首次建立认知,建议依次阅读《Agent Runtime 与 Durable Execution 到底是什么》、《为什么需要 Durable Execution》、《运行时怎样推动任务向前执行》和《运行时的关键可靠性机制》;做架构选型和采购评审,重点阅读《主流运行时怎样设计》和《企业级运行时怎样设计》;处理线上故障,直接使用《运行时问题怎样治理》的证据与排查路径。
资料时点:文中产品事实核验至 2026-09-13,产品状态以当前官方文档为准;本文基于这些公开实现抽象出的企业架构结论,会明确使用“本文推导”表述。
本篇边界:Harness(执行外壳)推进 Agent 行为,并在模型之外强制执行门禁、验证和循环控制;安全或业务 Policy Owner(策略责任方)定义规则,Policy Service(策略服务)做出或签发授权决策,Harness 只负责消费并强制执行该决策。Runtime(运行时)负责 Run 跨时间、故障和等待的耐久状态与恢复;Execution(执行基础设施)负责真正重建 Browser、Shell、Sandbox 或 VM 并产生现实副作用。Runtime 能恢复任务进度,不能据此假定外部动作没有发生;恢复前必须根据操作账本和外部证据对账。
一、Agent Runtime 与 Durable Execution 到底是什么 ​
1.1 Agent Runtime 与 Durable Execution 分别负责什么 ​
核心结论 ​
Agent Loop(智能体循环)解决:
Agent 下一步做什么。
Harness(执行外壳)解决:
Agent 应该怎样工作,并在模型之外如何被门禁、控制和验证。
Runtime(运行时)解决:
这次 Run(运行实例)怎样可靠地活着。
当一个 Agent 任务只需要几秒钟,而且失败后可以直接重跑时:
进程内存保存状态
+
一个普通 while 循环
可能已经够用。
但一旦任务出现:
运行几分钟 / 几小时 / 几天
人工审批
异步 Tool
进程重启
Worker(执行进程)迁移
模型限流
外部事件等待
多个并发 Run
副作用不能重复
系统发布升级
问题就发生了质变。
这时需要:
Durable Execution(持久执行):让一个任务在进程崩溃、节点迁移、长时间等待、服务升级之后,仍然能够从正确状态继续运行,而且不重复产生错误副作用。
可以先用一张图建立全局认知:
整篇的核心不是:
如何把 Python 对象存数据库
而是:
如何让“状态、控制流、副作用、等待和版本”在跨进程、跨时间、跨故障后仍然保持正确。
Runtime 术语很多,但它们都可以归到“状态、等待、副作用、Ownership(执行所有权)、版本”这几类系统责任。
正文会尽量遵循:
先看到真实问题
→ 再理解为什么简单方案失效
→ 最后才引出正式工程术语
例如先理解“Tool(工具调用)已成功但进程崩溃”的故障场景,再引出 Idempotency(幂等);先理解“两个 Worker 同时恢复同一任务”的故障场景,再引出 Lease(租约) 与 Fencing Token(栅栏令牌)。
Runtime 和相邻模块怎样划边界 ​
这一组概念最好一次横向看清。
| 概念 | 核心问题 | 主要职责 | 什么时候最需要 | 不主要负责什么 |
|---|---|---|---|---|
| Harness(执行外壳) | Agent 怎么干活? | 上下文组装、能力暴露、循环控制、门禁执行、验证 | 需要控制 Agent 行为 | 业务权限规则的定义与最终授权决策 |
| Policy Owner / Service(策略责任方 / 服务) | 这个动作在当前身份和情境下是否允许? | 定义规则,做出或签发授权决策 | 企业身份、数据、安全和业务规则 | Agent 循环推进和长任务恢复 |
| Runtime(运行时) | Run 怎么可靠地活着? | 状态、恢复点、等待、重试调度、并发所有权、恢复 | 长任务、等待、副作用、恢复 | 是否应该业务重试、具体 Prompt 和领域 Tool |
| Execution(执行环境) | 动作在哪里发生? | Shell、Browser、Sandbox、Container、VM | 需要真实执行代码 / 浏览器 / 命令 | Run 生命周期 |
| Workflow Engine(工作流引擎) | 多步骤流程怎么可靠执行? | 流程状态、Timer、Retry、Signal、Durable Step | 明确多步骤、等待和长事务 | Agent 的推理智能本身 |
需要特别注意:
Runtime
≠
Workflow Engine
但二者经常重叠。
例如:
- LangGraph 同时承担 Agent Graph Runtime 和 Checkpoint;
- Temporal / Dapr / Restate 更偏通用 Durable Workflow Runtime;
- OpenAI Agents SDK
RunState解决特定 Agent Run 的暂停 / 恢复; - Microsoft Agent Framework Workflow 同时提供 Agent Workflow、Checkpoint 和 HITL(Human-in-the-loop,人在回路)。
所以本文不会强行给所有产品贴唯一标签,而是分析:
它们分别承担了哪些 Runtime 语义。
Durable Execution 到底持久什么 ​
很多人第一次理解 Durable Execution,会认为:
“定期把 State 存数据库就可以了。”
不够。
一个长 Run 真正需要持久化的至少包括:
| 类别 | 典型内容 | 为什么必须持久 |
|---|---|---|
| 逻辑状态(Logical State) | 当前计划、步骤、预算 | 恢复后知道做到哪 |
| 控制状态(Control State) | 正在运行 / 等待 / 计划重试 | 决定下一步 |
| 待处理工作(Pending Work) | 未完成 Tool、审批、消息 | 防止请求丢失 |
| 副作用引用(Side-effect Reference) | 操作 ID、权威账本引用、外部资源 ID | 恢复时能查询真实操作状态,防止盲目重试 |
| 版本状态(Version State) | Agent、Tool、Workflow 版本 | 避免旧 Run 被新代码错误恢复 |
| 产物引用(Artifact Pointer) | 文件、结果集、Sandbox 引用 | 恢复工作环境 |
| 时间状态(Timing State) | Deadline、定时器、退避时间 | 支持等待、超时、重试 |
| 追踪 / 审计状态 | 事件、决策、证据 | 调试和审计 |
因此更准确的模型是:
Durable Runtime
=
可持久的状态
+
可恢复的控制流
+
可持久的等待
+
可追踪的副作用
+
能够感知版本的恢复
1.2 Run、会话与工作流实例怎样区分 ​
这些词在不同产品里经常混用。
本文采用一套工程约定:
| 概念 | 本文含义 | 例子 |
|---|---|---|
| Run(运行实例) | 一次具体任务执行 | “修复这一个 Bug” |
| Session / Thread(会话 / 线程) | 多轮交互容器 | 用户与 Agent 的持续会话 |
| Workflow Instance(工作流实例) | 一个工作流定义的某次执行 | 一次审批流程 |
| Job(任务) | 调度系统中的后台执行任务 | 一次数据处理任务 |
| Turn(对话轮次) | 一次用户输入到 Agent 返回或暂停之间的交互边界 | 用户要求修复 Bug 后的这一轮处理 |
| Step / Task(步骤 / 子任务) | Run 内可独立记录结果的逻辑执行单元 | “运行测试” |
| Attempt(尝试) | 同一 Step 因重试或 Worker 迁移而产生的一次物理执行 | 测试 Step 的第二次重试 |
| Operation(外部操作) | 对真实外部系统的一次动作 | “创建工单” |
为什么要区分?
例如:
Session
├─ Run 1:定位问题
├─ Run 2:用户补充信息后继续修复
└─ Run 3:最终发布验证
如果 Session 和 Run 混在一起:
- Budget 难归属;
- Checkpoint 难定义;
- Trace 根节点不清晰;
- Approval 难恢复。
这里还有一个不能省略的关系:
Run
└─ Turn 1..n
└─ Step 1..n
└─ Attempt 1..n
└─ Operation 0..n
Runtime 持久化的是 Run 和 Step 的可恢复事实,而不是把某个 Attempt 的进程内存原样保存。同一 Step 可以有多个 Attempt,但它们必须共用同一个逻辑步骤身份,并通过操作账本确认外部动作是否已经发生。否则,“重试 Step”会被错误实现为“再创建一次业务资源”。
1.3 什么情况下才值得引入持久运行时 ​
不是所有 Agent 都应该上 Temporal / Dapr / LangGraph Checkpointer。
可以按复杂度判断:
| 场景 | 是否需要 Durable Runtime | 推荐 |
|---|---|---|
| 3 秒问答,无 Tool | 低 | 进程内执行即可 |
| 30 秒单 Tool Agent | 低 | 简单 Run State |
| 5 分钟 Coding Agent | 中 | 数据库 Checkpoint 可选 |
| 30 分钟 Research Agent | 中高 | 持久 Run State |
| 人工审批可能等 2 天 | 高 | 持久等待 + Resume |
| 跨多个系统有副作用 | 高 | 操作账本 + 幂等 / 补偿 |
| 数千并发长 Run | 高 | 调度 + 租约 + 背压 |
| 一失败可以从头重跑 | 低 | 不要过度工程 |
表中的秒数、分钟、等待天数和并发量只是用于对比失败成本的示意场景,不是可直接套用的通用阈值。真正的硬触发条件是外部写入无法安全重做、审批或信号必须跨进程存活、合规要求可审计恢复等风险语义;任务时长和并发量是辅助评估成本的连续变量。
可以粗略记成:
Runtime 复杂度
≈
任务持续时间
×
副作用风险
×
恢复价值
×
并发规模
可以进一步分成四档:
| 层级 | Runtime 形态 | 适用 |
|---|---|---|
| L0 | 进程内状态 | 秒级、失败可重跑 |
| L1 | 数据库持久化 Run State | 分钟级、中等任务 |
| L2 | Checkpoint + 持久等待 + 操作账本 | 长任务、审批、副作用 |
| L3 | Durable Workflow Runtime | 大规模、长事务、强恢复 / 审计 |
这四档不是成熟度排名,而是:
Runtime 复杂度应匹配 Failure Cost。
1.4 为什么运行时会回到分布式系统问题 ​
Agent Runtime 看起来是一个 AI 新问题,但只要任务跨出一次请求 / 一个进程的生命周期,它就会重新进入经典分布式系统世界。
演进链可以理解为:
Single Request
→ 进程内内存足够
Long-running Run
→ State 必须脱离进程生命周期
External Wait / HITL
→ Wait / Signal 必须持久化
External Side Effect
→ 需要 Idempotency / Operation Ledger / Reconciliation
Multiple Workers
→ 需要 Lease / Fencing / Ownership
Large Scale
→ 需要 Queue / Backpressure / Fair Scheduling / Capacity
Long-lived Versions
→ 需要 Version Pin / Migration / Replay
Cross-system Workflow
→ 需要 Saga / Compensation / Failure Domain
因此:
Agent Runtime 的新鲜之处在于“控制者是非确定性的 Agent”;但它面对的持久化、并发、恢复、副作用、版本和灾备问题,本质上仍然是分布式系统问题。
这带来一个非常重要的架构判断:
不要让 Agent 自己重新发明分布式可靠性
模型可以决定“下一步做什么”,但:
状态是否丢失
副作用是否重复
谁拥有执行权
等待是否持久
恢复是否一致
版本是否兼容
这些必须由确定性的 Runtime / Software Infrastructure 保证。
Agent Runtime 和传统 Workflow Runtime 有什么不同 ​
不能因为很多机制来自 Temporal / Restate / DBOS,就认为 Agent Runtime 与传统 Workflow Runtime 完全一样。
传统 Durable Workflow 通常更强调:
已知 Control Flow
+ Deterministic Replay
+ Durable Activity
Agent Runtime 还额外面对:
Model Decision 非确定
Context 每轮变化
Tool Set 可动态变化
Session / History 会影响后续决策
Agent 可能 Re-plan / Handoff / Spawn Subagent
Verification 结果可能把任务打回继续
所以 Agent Runtime 的额外难点是:
恢复的不只是“程序执行位置”,而是“一个还能继续正确决策的语义世界”。
这也是本文强调 Semantic Recovery(语义恢复)的原因。
二、为什么需要 Durable Execution ​
2.1 为什么请求生命周期承载不了长任务 ​
一次网络请求为什么不够 ​
传统 Web 服务常假设:
请求到达
→ 完成计算
→ 返回响应
几十毫秒到几十秒结束。
但 Agent Task 可能:
用户任务
→ 模型决策
→ 调用 Tool
→ 等待
→ 人工审批
→ 恢复
→ 再调用 Tool
→ 子 Agent
→ 再次等待
→ 最终验证
持续:
几分钟
几小时
甚至几天
如果 Run 生命周期绑定一个进程 / HTTP Request:
进程崩溃
=
任务状态一起丢失
这是最基本的 Runtime 问题。
为什么失败后重新跑一遍不够 ​
对于纯计算:
算失败了
→ 再算一次
通常问题不大。
但 Agent 和普通计算最大的区别之一是:
Agent 会真的改变外部世界。
例如它可能:
- 发一封邮件;
- 创建一张工单;
- 发起一笔支付;
- 合并一个代码变更;
- 执行一次部署。
真正危险的是下面这种情况:
Agent 请求“创建工单”
↓
工单系统已经创建成功
↓
Runtime 所在进程突然崩溃
↓
“创建成功”还没来得及写进 Runtime State
这中间形成了一个非常关键的 Crash Window(崩溃窗口):
两分钟后任务恢复,Runtime 面临一个现实问题:
刚才那张工单到底创建成功没有?
如果直接重跑:
再次创建
→ 可能得到两张工单
如果直接跳过:
直接认为成功
→ 万一第一次其实失败了呢?
所以 Durable Runtime 必须解决的核心,不是简单的:
“失败以后要不要 Retry(重试)?”
而是:
我如何知道外部动作到底有没有真实发生?
《运行时的关键可靠性机制》会深入推导三个生产机制:
Operation Ledger(操作账本)
+
Idempotency(幂等)
+
Reconciliation(对账)
这一节的根问题是:
只要 Agent 会改变真实世界,恢复就不能等同于简单重跑。
2.2 等待和系统升级为什么会改变恢复语义 ​
外部等待为什么是一等状态 ​
真实 Agent 会等待:
等待人工审批
等待 Webhook 回调
等待异步任务完成
等待用户上传文件
等待支付回调
等待浏览器事件
等待 Tool 完成
等待限流窗口结束
等待期间:
CPU = 0
但 Run 仍然“活着”。
因此必须存在:
WAITING_APPROVAL
WAITING_EXTERNAL
WAITING_TIMER
WAITING_TOOL
而不是只有:
RUNNING / SUCCESS / FAILED
真实框架也是这样处理的。
OpenAI Agents SDK 的 RunState 可以把等待审批所需的运行状态序列化保存,等审批完成后再恢复 Run。[^openai-runstate]
Microsoft Agent Framework 的 Checkpoint 也会把“正在等待的外部请求”一起保存;恢复后,这个等待请求会重新进入工作流。[^ms-checkpoint][^ms-hitl]
这说明:
“正在等待”不是页面 UI 状态,而是 Runtime 必须持久保存的真实运行状态。
系统发布为什么会变成 Runtime 问题 ​
一个 Run:
周一 10:00
→ 进入审批等待
周三 15:00
→ 用户批准
这两天之间:
Agent Prompt 更新了
Tool 参数结构变了
Skill 更新了
Workflow 代码发布了新版本
SDK 也升级了
恢复时到底用哪个版本?
这不是边缘问题,而是所有 Long-running Run 最终都会遇到的问题。
OpenAI Agents SDK 当前官方也明确建议:如果 Pending Task 可能等待很久,应把 Agent Definition / SDK 的 Version Marker(版本标记)和序列化状态一起保存,以避免模型、Prompt、Tool 定义变化造成恢复不兼容。[^openai-runstate]
2.3 规模为什么会把 Runtime 变成调度系统 ​
10 个 Run:
一张数据库表
+
一个 Worker
可能够。
10 万个 Waiting Run:
10 万个状态对象
10 万个定时等待
大量审批通知
过期清理
索引
版本兼容
集中恢复
CPU 可能几乎为零,但:
状态存储
定时器索引
通知系统
恢复队列
都会成为成本。
因此:
Durable Runtime 的规模不等于 CPU 规模。
等待中的 Run 规模可能远大于真正正在执行的 Run。
第一性问题最终是什么 ​
最终可以压缩成五个问题:
1. 状态(State)
→ 我现在在哪里?
2. 控制流(Control Flow)
→ 下一步该执行什么?
3. 副作用(Side Effect)
→ 哪些外部动作已经真实发生?
4. 等待与外部信号(Wait / Signal)
→ 我在等谁?什么时候继续?
5. 版本(Version)
→ 恢复时应该使用哪一版逻辑?
只要一个 Runtime 能可靠回答这五个问题,它才真正接近 Durable Runtime。
三、运行时怎样推动任务向前执行 ​
这里的“运行时”不是模型推理层,而是负责 Run 状态、等待、恢复、Ownership、版本和调度的持久生命周期系统。
这一章用一个贯穿案例理解 Runtime:
Coding Agent 修改支付代码,测试通过以后,需要等待 Senior Engineer 审批两小时,再继续 Merge PR。
整个过程:
开始执行
→ 修改代码
→ 跑测试
→ 保存状态
→ 等待审批
→ Worker 释放
→ 两小时后收到批准
→ 重新恢复
→ Merge PR
→ 验证成功
后面出现的 Queue、Claim、Checkpoint、Wait、Signal、Resume,都映射回这个案例。
3.1 Run、状态和恢复点怎样形成主链路 ​
一个 Run 怎样被 Runtime 推着往前走 ​
Claim / Load / Persist 只是实现动作,真正重要的是它们分别解决 Ownership、State Loading 和 Durable Commit。
把它们放进刚才的 Coding Agent:
1. 用户发起任务
→ Runtime 创建 Run R123
2. Run 进入队列
→ 等待 Worker
3. Worker A 获得 R123 的执行权
→ Claim(领取)
4. Worker A 读取之前保存的状态
→ Load(加载)
5. Agent 修改代码并执行测试
→ Execute(执行)
6. 测试结果和当前进度被保存
→ Persist(持久化)
7. 任务需要人工审批
→ Runtime 把状态变成 WAITING_APPROVAL
8. Worker A 被释放
→ 不需要陪 Run 等两小时
9. 两小时后审批系统返回“通过”
→ Signal(外部信号)到达
10. Run 重新进入队列
→ 新 Worker B 可以继续执行
11. Worker B 读取 Checkpoint
→ Resume(恢复)
12. 执行 Merge PR 并验证
→ SUCCEEDED
再把它抽象成 Runtime 主链路:
因此一次 Runtime Iteration(运行迭代)可以理解成:
拿到执行权 → 读取真实状态 → 执行一小段工作 → 保存结果 → 决定继续、等待、重试还是结束。
Run 状态机 ​
生产 Run 不应该只有:
running
success
failed
推荐至少区分:
不同产品状态名可以不同,但必须能够表达:
- 正在运行;
- 正在等什么;
- 是否计划重试;
- 是否正在取消;
- 是否已经终态。
State / Checkpoint / History / Operation 有什么区别 ​
| 对象 | 是什么 | 主要用途 |
|---|---|---|
| Run State | 当前真实状态 | 当前执行 |
| Checkpoint | 某一时刻的状态快照 | Resume |
| Event | 状态发生过什么变化 | Audit / Replay / Debug |
| Operation Reference | Runtime 保存的 operation_id 与权威账本引用 |
等待 / 调度 / 恢复查询 |
| Operation Ledger | Execution Control Plane 或 Operation Service 持有的外部副作用权威账本 | Idempotency / Reconciliation / Audit |
例如:
Event:
TOOL_SUCCEEDED(operation_id=123)
Checkpoint:
current_step = VERIFY
latest_tool_result = ref://123
Run State:
status = RUNNING
Operation:
operation_id = 123
external_ticket_id = T-999
status = SUCCEEDED
不能把四者混成一个 JSON。
3.2 Checkpoint 与 History 怎样设计 ​
Checkpoint 应该保存什么 ​
Checkpoint 不是:
pickle(agent)
至少要考虑:
Logical State
Workflow Cursor / Next Step
Pending Messages
Pending Requests
Pending Tool / Operation
Approval State
Artifact References
Budget / Deadline
Retry Metadata
Version Manifest
Execution Environment Requirement
Microsoft Agent Framework 当前 Checkpoint 会保存 Executor State、下一 Superstep 的 Pending Messages、Pending Requests / Responses、Shared State。[^ms-checkpoint]
LangGraph 当前在每个 Superstep Boundary(超级步边界)保存 StateSnapshot,并在并行 Node 中记录 Pending Writes,使某个 Node 失败时已成功 Node 不必重新执行。[^langgraph-persistence]
Checkpoint 多久保存一次 ​
不是越频繁越好。
频率高:
Recovery Loss 小
但
Storage / QPS(Queries Per Second,每秒请求数)/ Latency 高
频率低:
成本低
但
Crash 后重做更多
可以把 Trade-off 表达成:
Checkpoint Cost
vs
Recovery Rework Cost
对于:
Model Call
Tool Side Effect
Human Approval
这些高价值边界通常值得持久化。
对于:
纯内存格式转换
不一定需要。
为什么有些 Runtime 还要保存 History(历史)
Snapshot-first(快照优先) ​
只保存当前状态:
run_state = {...}
优点:
- 实现简单;
- Resume 快。
缺点:
- 难知道为什么变成当前状态;
- Time Travel / Audit 弱。
Event + Snapshot(事件日志与快照) ​
记录事件:
RUN_CREATED
STEP_STARTED
MODEL_COMPLETED
TOOL_REQUESTED
TOOL_SUCCEEDED
APPROVAL_REQUESTED
APPROVAL_GRANTED
CHECKPOINT_CREATED
再定期 Snapshot。
优点:
- Audit;
- Replay;
- Time Travel;
- Debug;
- 指标计算。
缺点:
- 存储、版本和重放更复杂。
LangGraph 的 Persistence / Time Travel 就是很有代表性的 Event + Checkpoint 思路:可以查看历史 StateSnapshot、Replay 旧 Checkpoint、Fork 出新分支。[^langgraph-persistence][^langgraph-time-travel]
3.3 等待、恢复和核心对象怎样建模 ​
如何等待却不占 Worker ​
Runtime 必须把:
sleep 1 hour
和:
Thread.sleep(1h)
区分开。
真正 Durable Timer:
Persist wake_at
→ Worker 释放
→ Timer Service 等待
→ 到期后重新 Queue
同样:
Wait for Approval
应该:
Persist Pending Request
→ Release Worker
→ Human Response
→ Signal Run
→ Resume
而不是让 HTTP Connection / Worker 一直挂着。
一张图理解“暂停 → 等待 → 恢复” ​
最重要的认知:
等待两小时的是 Run,不是 Worker。
Worker 可以在等待期间完全不存在。
暂停后从哪里恢复 ​
一个真正可恢复的 Interrupt,需要:
Where to resume
What data to wait for
What state is already committed
What side effects already happened
What code version should resume
LangGraph 当前 interrupt() 会持久化图状态,并可无限期等待;恢复时 Node 会从头重新执行,所以 Interrupt 之前的副作用必须设计成 Idempotent(幂等)。[^langgraph-interrupt]
这是一个非常重要的坑:
Resume 并不一定等于从某一行代码继续。
很多 Runtime 实际是:
重新进入 Step / Node
+
利用 durable state 跳过已完成部分
Runtime 最核心的数据对象 ​
参考设计:
interface Run {
runId: string;
sessionId?: string;
agentId: string;
agentVersion: string;
status: RunStatus;
currentStep?: string;
createdAt: string;
startedAt?: string;
deadline?: string;
budget: RunBudget;
checkpointId?: string;
stateVersion: number;
runtimeVersion: string;
versionManifest: VersionManifest;
outcome?: RunOutcome;
}
interface Checkpoint {
checkpointId: string;
runId: string;
stateVersion: number;
stateRef: string;
pendingRequests: PendingRequest[];
pendingOperations: PendingOperation[];
artifactRefs: string[];
environmentManifest?: string;
createdAt: string;
}
interface Operation {
operationId: string;
runId: string;
capability: string;
idempotencyKey?: string;
status:
| "PLANNED"
| "STARTED"
| "SUCCEEDED"
| "FAILED"
| "UNKNOWN";
externalResourceId?: string;
resultRef?: string;
}
字段名不是标准。
重要的是三个一等对象:
Run
Checkpoint
Operation
3.4 外部副作用和恢复语义怎样保持一致 ​
外部动作和 Runtime 状态怎样提交 ​
Durable Runtime 最容易出错的地方,不是“有没有 Checkpoint”,而是:
Step 的业务结果、外部副作用和 Runtime State 到底以什么顺序提交?
最基本的 Crash Window:
execute side effect
→ crash
→ persist result
如果 Crash 发生在中间,恢复时无法判断外部动作是否已经成功。
所以一个 Step 通常需要显式 Commit Protocol:
1. Reserve Intent / Operation ID
2. Persist PLANNED / STARTED
3. Execute
4. Persist Result / External Resource ID
5. Advance Run State
6. Create / Advance Checkpoint
关键原则:
外部副作用的 Durable Record 不能晚于“Run 已经继续往后走”的事实。
同一数据库内可以做到更强原子性 ​
如果:
Business Data
+
Workflow State
都在同一个数据库事务里,就可以把:
业务写入
+
Durable Step Record
一起提交。
DBOS 的 Transaction Step 就是代表:业务 Postgres 写入和 Durable Workflow Record 可以在同一事务里提交,因此数据库边界内能获得更强的 Exactly-once 语义。[^dbos-outbox]
跨外部系统时只能缩小 Crash Window,不能幻想消失 ​
例如:
Runtime DB
+
GitHub API
不可能用一个普通数据库事务原子提交两边。
只能通过跨 Owner 协作缩小不确定窗口:
Execution / Operation Service 权威账本
+
Idempotency Key
+
Reconciliation
Restate 则通过 Journal Commit 定义 Durable Step 的完成:Step Result 先写入并复制提交到 Journal,之后重试时从 Journal 读取历史结果并跳过已完成动作。[^restate-architecture]
不同实现路径不同,但共同目标是:
让“这一步是否已经发生”拥有唯一、可恢复的 Durable Truth(持久事实)。
生产级 Runtime 最重要的系统性质 ​
Runtime 的目标不是“有状态”这么简单。
可以按“运行时系统性质优先级”分成 P0 / P1 / P2 三层。这不是投资阶段或成熟度等级,而是在冲突时应先保证哪一类系统性质。
P0:先保证状态正确、不会重复执行 ​
| 性质 | 核心问题 | 失败后果 |
|---|---|---|
| State Consistency | State 与真实执行是否一致? | 错误恢复 |
| Single Ownership | 同一 Run 是否只有一个合法执行者? | 双执行 |
| Side-effect Safety | 外部动作是否重复? | 重复付款 / 工单 |
| Signal Consistency | 外部 Signal 是否丢失 / 重复? | 错误 Resume |
P0 先保证:
恢复以后不能把世界搞错。
P1:再保证任务不会丢、能够继续推进 ​
| 性质 | 核心问题 |
|---|---|
| Durability | Crash 后状态是否还在? |
| Recoverability | 是否能从正确位置继续? |
| Liveness | Run 是否最终能继续推进? |
| Wait Durability | Timer / Approval / Signal 是否能跨进程存活? |
P1 关注:
任务不能因为基础设施故障永久消失或永远卡住。
P2:最后优化恢复速度、成本和运维效率 ​
| 性质 | 核心问题 |
|---|---|
| Resume Latency | 多久能恢复? |
| Checkpoint Efficiency | Checkpoint 成本是否合理? |
| Capacity Efficiency | Waiting Run 是否低成本? |
| Economics | 每个成功 Run 成本是否可接受? |
| Operability | 是否可 Debug / Replay / Migrate / Rollback? |
最终优先级:
P0
先保证不重复、不丢、不乱
↓
P1
再保证能恢复、能继续
↓
P2
最后优化延迟、成本、运维
这和普通 Web 服务的差异很大:
对 Durable Runtime 来说,“恢复正确性”比“恢复得快”优先级更高。
恢复时应该 Retry、Reuse 还是 Reconcile ​
这是 Runtime 最重要的恢复判断之一:
这张图把后面很多术语压缩成一个判断:
已确认成功
→ 复用
已确认失败且可重试
→ 重试
状态未知
→ 先对账
不可安全判断
→ 人工 / 业务兜底
四、运行时的关键可靠性机制 ​
这一章开始进入真正的生产机制。
但需要先明确:
不是每一种机制都值得同样深入。
副作用正确性会讲到故障、数据模型和恢复路径;持久推进和版本恢复讲清适用条件与实现;规模治理和灾备机制则由实际并发、故障域与合规风险触发,不抢基础正确性的主线。
这些机制同样有优先级,不应该同时“全上”。
| 语义分组 | 机制 | 解决什么 |
|---|---|---|
| 副作用正确性 | Operation Ledger(操作账本)、Idempotency(幂等)、Reconciliation(对账)、Saga(长事务) | 弄清外部动作是否发生,避免重复副作用,处理跨系统局部失败 |
| 状态耐久性 | Checkpoint(检查点)、Durable State(持久状态)、History | 让 Run 跨进程和节点故障仍可恢复 |
| 控制流推进 | Wait / Signal / Retry / Cancel(等待、信号、重试与取消) | 让任务能暂停、唤醒、继续或终止 |
| 版本与所有权 | Version / Replay(版本 / 重放)、Single Ownership(单一执行权)、Lease / Fencing | 使旧 Run 能安全恢复,拒绝过期 Worker 写入 |
| 规模治理 | Backpressure / Fairness / Retention(背压、公平调度与保留) | 并发和等待规模扩大后仍可运营 |
| 高风险故障域 | DR(Disaster Recovery,灾难恢复)、Multi-region(多地域) | 在 RPO / RTO、合规或地域故障成本明确触发时,扩大耐久保证边界 |
原则:
先保证恢复正确性,再增加复杂调度与平台能力。
4.1 如何保证“恢复以后不会重复做事” ​
Exactly-once(恰好一次)为什么通常是危险幻觉 ​
真实分布式系统中:
Request Sent
→ Remote System Executes
→ Response Lost
调用方只看到:
timeout
它无法确定:
执行失败
or
执行成功但响应丢失
所以:
“我只调用一次”不等于“副作用只发生一次”。
真正工程目标通常是:
At-least-once Delivery
+
Idempotent Effect
+
Durable Operation Record
Restate 当前文档会用 “exactly-once semantics” 描述它的可靠通信能力,但即使底层 Runtime 提供强语义,连接外部非事务系统时仍然需要考虑外部系统本身的 Idempotency / Side Effect Contract。[^restate]
“消息只处理一次”和“业务动作只发生一次”不是一回事 ​
工程讨论里最容易混淆的是:
Message Delivery
vs
Business Effect
例如消息系统说:
exactly-once delivery
并不自动等于:
业务副作用 exactly-once
可以用这张表理解:
| 层 | 能保证什么 | 仍然需要什么 |
|---|---|---|
| Queue Delivery | 消息不丢 / 去重 | Handler 幂等 |
| Workflow Step | 某 Step 的 Durable Result | 外部 Side Effect 对账 |
| DB Transaction | 数据库内原子提交 | 外部 API 仍需幂等 |
| External API | 取决于对方契约 | Idempotency Key / Reconcile |
DBOS 是一个很好的边界案例:在 Transaction Step 中,业务数据库写入与 Workflow Checkpoint 可以在同一个 Postgres 事务里原子提交,因此数据库内部 Step 可以真正做到 Exactly-once;但对外部消息 / API,官方仍明确指出它们可能是 At-least-once,因此仍应该 Idempotent。[^dbos-outbox]
所以最稳妥的思维方式是:
先问“哪个边界内 Exactly-once”,再问“跨出这个边界以后怎么保证业务效果”。
可以把 Guarantee Boundary(保证边界)压缩成:
| 保证边界 | 可以依赖的强语义 | 跨出边界后怎么办 |
|---|---|---|
| 同一数据库事务 | Atomic / Exactly-once DB Effect | 不自动覆盖外部 API |
| Runtime Journal / Workflow History | 已完成 Step 可 Replay / Reuse | 外部系统仍需业务幂等 |
| Queue / Message Delivery | 去重 / 投递语义 | Handler 仍需幂等 |
| 外部 SaaS / API | 取决于对方 Contract | Idempotency Key + Reconciliation |
所以以后看到:
Exactly-once
Atomic
Transactional
Durable
都不能只问“支不支持”,还必须问:
这个保证到底在哪个边界内成立?
Operation Ledger(操作账本) ​
一个外部副作用应拥有独立的 Operation 记录和状态机。Operation 的业务语义、幂等契约、状态推进和对账方法由 Execution / Capability(执行 / 能力)定义;Execution Control Plane(执行控制面)或独立 Operation Service(操作服务)是权威账本。Runtime 只持久保存 operation_id、等待 / 调度状态和一致引用,并在恢复时查询权威账本。即使两类数据物理上位于同一数据库,逻辑 Owner 也不会改变。
权威账本中的状态机可以是:
PLANNED
→ STARTED
→ SUCCEEDED
or FAILED
or UNKNOWN
其中最关键的是 UNKNOWN(状态未知)。
它表示:
Runtime 已经发出了请求,但无法确认外部世界到底发生了什么。
例如:
operation_id = OP123
action = 创建工单
status = UNKNOWN
这时绝不能直接把它当 FAILED。
推荐至少记录:
| 字段 | 作用 |
|---|---|
| operation_id | 这次外部动作的唯一身份 |
| run_id | 属于哪个 Run |
| action / capability | 做了什么 |
| request_hash | 请求内容是否相同 |
| idempotency_key | 外部系统去重键 |
| status | PLANNED / STARTED / SUCCEEDED / FAILED / UNKNOWN |
| external_resource_id | 外部真实资源 ID |
| result_ref | 成功结果引用 |
| attempt | 第几次尝试 |
| started_at / finished_at | 时间证据 |
这样恢复时不是“猜”,而是 Runtime 根据权威账本的读取结果决定复用、等待还是触发对账。
推荐协作顺序:
Harness 生成 Action Proposal
→ Execution / Operation Service 保留 operation_id 并写入 PLANNED
→ Runtime 保存 operation_id 引用与等待状态
→ Execution 执行 Tool 并推进权威账本
→ Runtime 收到结果引用
→ Harness 组装 Observation 并推进 Run
如果 Crash:
Operation = SUCCEEDED
Run Checkpoint = old
恢复时,Runtime 通过 operation_id 读取 Execution Control Plane 或 Operation Service 的权威状态:
不再执行 Tool,而是读取 Ledger 中已成功 Result。
如果:
Operation = STARTED
但结果未知:
进入 Reconciliation(对账)流程,而不是直接 Retry。
Idempotency(幂等)怎么真正设计 ​
Tool Contract 最好声明:
side_effect
idempotency_mode
idempotency_key
reconcile_method
compensation
例如:
| Tool | 是否幂等 | 恢复策略 |
|---|---|---|
| read_file | 是 | 直接重试 |
| query_sql SELECT | 是 | 直接重试 |
| create_ticket | 取决于 key | 用 idempotency key |
| send_email | 通常否 | 查询 / 记录 Message ID |
| charge_payment | 必须业务幂等 | Payment Idempotency Key |
| merge_pr | 需检查状态 | Read-before-write |
Retry(重试)必须分类 ​
| Failure 类型 | 例子 | 策略 |
|---|---|---|
| Transient(瞬时) | 429、短暂网络失败 | Backoff + Jitter |
| Deterministic(确定性) | SQL 语法错误 | 返回 Agent Re-plan |
| Permission | 权限不足 | Ask / Escalate |
| Side-effect Unknown | Timeout 后状态未知 | Reconcile,不直接重放 |
| Model Format | JSON 不合法 | Limited Retry / Constrained Output |
| Dependency Outage | 外部系统宕机 | Circuit Breaker / Durable Timer |
重试策略至少包含:
max_attempts
backoff
jitter
deadline
retry_budget
error_classifier
4.2 如何让任务暂停、等待,再安全地继续 ​
Cancellation(取消)必须传播到底 ​
用户点击:
Cancel
不代表任务真的停止。
取消链路应该:
User Cancel
→ Run = CANCELLING
→ Cancel Queued Steps
→ Signal Running Worker
→ Cancel Model Request if possible
→ Cancel Tool / Process
→ Cancel / Reclaim Sandbox
→ Mark CANCELLED
如果 Runtime 只改数据库状态:
status = CANCELLED
但后台 Shell 继续:
rm -rf ...
就是典型生产事故。
人工审批本质上是一种可持久等待 ​
审批至少包含:
request_id
run_id
request_type
requested_action
evidence
requester
approver_policy
deadline
decision
decision_at
必须考虑:
- 谁能批;
- 审批多久过期;
- 多人审批;
- 审批回来后 Environment 是否还存在;
- Tool / Agent Version 是否变化;
- 用户是否能在等待期间补充输入。
OpenAI Agents SDK 当前明确允许 RunState 序列化后长期等待审批,并建议 Pending Task 与 Agent / SDK Version 一起保存。[^openai-hitl]
Microsoft Agent Framework 则把外部等待建模为 RequestPort / RequestInfoEvent,Checkpoint 会把 Pending Request 一起保存,恢复时重新发出。[^ms-hitl]
一个执行跑很久时,Runtime 怎么知道它还活着 ​
某些 Tool / Activity 本身可能执行几十分钟:
Large Data Job
Long Browser Automation
Code Build
Video Processing
如果 Runtime 只看:
请求还没返回
无法区分:
真的在工作
vs
Worker 已经死了
所以 Long-running Activity 常需要:
heartbeat
last_progress
progress_payload
heartbeat_timeout
Runtime 可以:
heartbeat 超时
→ 标记 Activity Lost
→ Retry / Reconcile
如果 Heartbeat Payload 能保存进度:
processed_rows = 3,000,000 / 10,000,000
新 Worker 还可以从更接近当前位置继续,而不是从头开始。
4.3 服务已经升级,旧任务怎么继续跑 ​
旧任务恢复时,到底使用旧版本还是新版本 ​
长 Run 最大的隐性风险之一:
运行逻辑变了,但状态还停留在旧世界。
Run 创建时建议记录:
agent_version
prompt_version
tool_version
skill_version
workflow_version
runtime_version
checkpoint_schema_version
恢复策略通常有三种:
| 策略 | 做法 | 适合 |
|---|---|---|
| Pin Old Version | 旧 Run 继续旧代码 | 高兼容要求 |
| Migrate State | 旧 Checkpoint 显式升级 | Schema 可迁移 |
| Restart / Reconcile | 无法安全恢复时重新起 Run | 低风险 / 可重做 |
不要默认:
deserialize old checkpoint
→ run latest code
Determinism(确定性)与 Replay ​
Durable Workflow Runtime 经常依赖:
同一个历史记录重新执行控制逻辑时,应得到相同控制路径。
因为 Replay 不是简单:
load Python stack frame
而是:
重新执行 Workflow Logic
+
从历史读取已完成外部结果
因此 Workflow Code 中:
- Random;
- Current Time;
- Network Call;
- LLM Call;
不能随意直接执行,通常需要被 Runtime 包装成 Durable Activity / Task。
LangGraph Functional API 同样强调:恢复特定 Run 时应该沿相同已记录步骤前进,并通过持久化 Task / Subgraph Result 避免重复非确定性工作。[^langgraph-functional]
Temporal 的核心 Durable Execution 模型也建立在持久化 Workflow History 与 Replay 上。[^temporal]
恢复正确,不只要恢复 Run State,还要恢复 Session / History 一致性 ​
这是 Agent Runtime 和普通 Workflow Runtime 很容易不同的地方。
假设:
Tool 已经执行成功
↓
准备把 Tool Result append 到 Session
↓
Session 写入失败
↓
进程崩溃
恢复以后如果简单:
重新执行 Tool
+
重新 append
就可能产生:
- Tool 重复执行;
- Session 历史重复;
- 模型看到两份相同 Observation;
- 后续决策被污染。
OpenAI Agents SDK 当前已经专门处理这一类场景:如果 Resume 之后 Tool Work 已完成,但 Session Append 失败,这个 Pending Batch 会随 RunState 一起继续持久化;下次恢复时先精确 Reconcile(对齐)这个 Batch,而不是重新执行 Tool。官方同时要求恢复时继续使用原 Session Backend / Session ID,并对该历史保持 Exclusive Access(排他访问),避免两个独立恢复过程同时修改同一 Session。[^openai-runstate]
因此企业 Runtime 最好把:
Run State
+
Session / Thread State
+
Pending Append
作为一个一致性问题考虑。
关键原则:
“Run 恢复成功”不代表“Agent 历史恢复正确”。
对于 Agent Runtime,History / Session Corruption(历史污染)同样会改变后续 Model Decision。
因此真正的 Semantic Recovery(语义恢复)至少要同时检查:
Run State 正确
+
Control Flow 正确
+
External Side Effect 正确
+
Session / History 正确
+
Version 正确
+
Execution Environment 正确
+
Pending Work 正确
只有这些同时成立,系统才真正回到一个:
可以继续安全决策的真实世界状态。
Outbox 与 Inbox 分别保护哪个方向 ​
Runtime 还需要区分“我准备发出什么”和“我已经收到什么”。Transactional Outbox(事务外发箱)先把待发事件与本地状态放进同一数据库事务,再由投递器至少一次地发送;Durable Inbox(持久收件箱)则用 signal_id 和状态前置条件接收审批、Webhook 或回调,先落盘去重,再推进 Run。
这两个机制不提供跨系统“全局恰好一次”。Outbox 解决本地提交后消息不丢,接收方仍需幂等;Inbox 解决重复信号不重复推进,但不能证明上游业务动作只发生了一次。如果回调与外部资源状态冲突,Runtime 仍必须回源对账。
4.4 多个 Worker、海量任务同时运行时怎么不乱 ​
Distributed Concurrency(分布式并发) ​
最典型问题:
Worker A claim Run
→ GC Pause
Lease Expire
Worker B claim Run
→ resume
Worker A 恢复
→ 继续写状态
这就是 Split Brain(脑裂)。
常见机制:
lease_owner
lease_expire_at
state_version
fencing_token
所有状态写入:
write(
run_id,
expected_state_version,
fencing_token
)
过期 Worker 写入必须失败。
任务突然暴增时,怎么避免整个系统被拖垮 ​
Agent 很容易 Fan-out:
1000 Parent Runs
×
5 Subagents
×
3 Browser Tools
=
15000 Execution Slots
Runtime 不能等下游:
429
再被动处理。
至少要有:
global concurrency
tenant quota
model quota
tool quota
execution quota
subagent budget
priority queue
并避免:
大客户 / 一个爆量 Tenant
→ 饿死所有其他 Run
所以需要 Fair Scheduling(公平调度)。
一个永远恢复失败的 Run 怎么处理 ​
某些 Run 因代码 Bug:
Crash
→ Retry
→ Crash
→ Retry
会无限消耗资源。
必须设置:
max_recovery_attempts
dead_letter_state
manual_review
DBOS 当前 Workflow 就提供 max_recovery_attempts,达到上限后进入终态,避免一个 Buggy Workflow 无限拖垮系统。[^dbos-recovery]
因此 Runtime Failure 不只分:
retryable / non-retryable
还要考虑:
这个 Run 是否应该退出自动恢复路径,进入人工处理。
4.5 跨多个系统执行失败后,怎么恢复真实世界 ​
Saga / Compensation(长事务 / 补偿) ​
Agent 经常跨系统:
Create Ticket
→ Provision Resource
→ Update CMDB
→ Notify Owner
第三步失败后不可能:
database rollback
整个世界一起回滚。
因此采用 Saga 思想:
| Action | Compensation |
|---|---|
| create resource | delete resource |
| create ticket | close ticket |
| merge PR | revert commit |
| send email | 通常不可逆 |
不可逆操作:
应该提高 Verification / Approval 门槛,而不是幻想 Compensation。
Runtime Recoverability(运行时可恢复性) ​
Recoverability 不只是 Checkpoint 存在。
还需要:
State Recoverable
+
Operation Reconciliable
+
Environment Reconstructable
+
Code Version Available
+
Pending Signal Recoverable
例如 Browser Agent:
逻辑 Checkpoint 还在
但 Browser Session 已过期
Runtime 必须知道:
重新创建 Browser
→ 重放到安全状态
or
重新登录
or
人工介入
这也是 Runtime 与《Agent 安全执行与结果验证》的边界:
Runtime 定义“需要什么环境才能恢复”;Execution 负责真正重建环境。
4.6 运行历史到底应该保留多久 ​
Run 完成以后,下面这些数据留多久?
Checkpoint
Journal
Operation Ledger
Idempotency Key
Artifact
Approval Evidence
Trace
不同数据的 Retention 目标不同。
| 数据 | 保留目的 | 太短风险 | 太长成本 |
|---|---|---|---|
| Idempotency Key | 防重复请求 | 重复副作用 | 存储 |
| Journal | Debug / Audit | 无法复盘 | 存储增长 |
| Checkpoint | Resume | 无法恢复 | State Cost |
| Artifact | Evidence | 无法验证 | Object Storage |
| Approval | Compliance | 审计缺失 | 合规存储 |
Restate 当前就把 Workflow Retention、Idempotency Retention 和 Journal Retention 作为独立可调配置。[^restate-retention]
所以:
Retention 不只是数据库 TTL,而是 Durability / Audit / Cost 的业务契约。
核心机制如何组合成完整可靠性边界 ​
前面机制可以按系统责任压缩成五组:
| 责任 | 核心机制 | 解决的问题 |
|---|---|---|
| 副作用正确性 | Operation Ledger、Idempotency、Reconciliation | 外部动作到底成功没有,恢复时会不会重复 |
| 持久等待 | Wait / Signal、Checkpoint | 任务怎样暂停几小时后继续 |
| 版本恢复 | Version Pinning、Replay、Migration | 旧任务遇到新代码怎样继续 |
| Ownership | Lease、Fencing、Heartbeat | 多个 Worker 怎样避免双执行 |
| 大规模治理 | Backpressure、Retention、Dead Letter、Multi-region | 规模扩大后怎样保持可恢复与可运营 |
这些机制不是按“基础 / 高级”分层,而是由 Failure Cost、任务持续时间和副作用风险决定是否需要。
五、主流运行时怎样设计 ​
这一章只承担行业证据职责:验证前面推导出的机制在主流 Runtime 中怎样落地。
评价实现时不比较 Feature 数量,而是回答:
这些 Runtime 用什么机制实现前面的问题?
为什么这样设计?
代价是什么?
企业应该吸收什么?
5.1 主流 Runtime 怎样验证 Durable Execution 机制 ​
先用统一维度比较 ​
统一比较 Runtime 时,需要回答九个问题:
- State Model(状态模型):Run 的真实状态怎样表示;
- Checkpoint Boundary(检查点边界):什么时刻才算状态安全持久化;
- Replay Model(重放模型):恢复是从 Snapshot 继续,还是依据 History Replay;
- Retry Semantics(重试语义):失败时哪些步骤可以安全重试;
- Interrupt / HITL(中断 / 人在回路):等待外部输入怎样成为 Durable State;
- Side-effect Semantics(副作用语义):DB / API / Deploy 已经发生时怎样避免重复;
- Concurrency(并发):多个 Worker 怎样争夺 Run Ownership;
- Versioning(版本):旧 Run 遇到新代码怎样继续;
- Execution Coupling(执行耦合):Runtime 是否绑定具体 Shell / Browser / Sandbox。
机制对照
| 实现 | 核心状态 | Checkpoint / Persistence | Resume 模型 | HITL | 最值得研究 |
|---|---|---|---|---|---|
| OpenAI Agents SDK | RunState | Serialize RunState | 继续 Agent Run | Tool Approval | Agent 原生 Pause / Resume |
| LangGraph | Thread State | Superstep Checkpoint | Replay / Fork | interrupt() | Agent Graph Persistence |
| Microsoft Agent Framework | Workflow State | Superstep Checkpoint | Resume Workflow | RequestPort | Workflow + Pending Request |
| Temporal | Workflow History | Durable History | Deterministic Replay | Signal / Update | 通用 Durable Execution |
| Dapr Workflows / Agents | Workflow State | Workflow Checkpoint | Workflow Recovery | Workflow Event | Agent + Workflow Durability |
| Restate | Journal / Durable State | Durable Steps | Journal Replay | Signal / Promise | Journal + Epoch Fencing |
| DBOS | Postgres Workflow State | Step Checkpoint | Resume Last Completed Step | Durable Queue / Event | Postgres-native Durability |
OpenAI:RunState 怎样解决暂停和恢复 ​
OpenAI 当前 RunState 是:
可序列化的 Agent Run Snapshot,包含 Context、Usage、Interruptions、Model Response、Approval State 等。[^openai-runstate]
重点能力:
to_json / to_string;from_json / from_string;- Pending Approval;
- Nested
Agent.as_tool()Resume; - Pending Session Append Reconciliation;
- Version Marker 建议。
它最值得学习的是:
一个 Agent SDK 内部怎样把 HITL 中断变成真正可以跨请求 / 跨进程恢复的 Run State。
但它不是完整分布式 Workflow Scheduler。
LangGraph:为什么把 Checkpoint 放在中心 ​
LangGraph 当前把 Graph State 持久化成 Checkpoint,并按 Thread 组织。[^langgraph-persistence]
特点:
- 每个 Superstep 保存 StateSnapshot;
- Pending Writes 保留已经完成的并行 Node;
- Interrupt;
- Replay;
- Fork / Time Travel;
- 可从历史 Checkpoint 重新执行。
特别值得研究的是:
Checkpoint 不只是容灾能力,还同时成为 HITL、Debug、Time Travel、Fork 的统一基础。
LangGraph 当前还提供 DeltaChannel,以减少长 Thread 中 append-heavy State 的 Checkpoint 存储成本,但处于 Beta。[^langgraph-persistence]
Microsoft:怎样把外部等待建模进 Workflow ​
Microsoft Agent Framework 的 Workflow Graph 把不同责任拆开:
- Executor(执行节点):真正处理一段业务逻辑;
- Edge(边):定义消息 / 控制流怎样在节点间移动;
- Event(事件):表达 Runtime 中发生的状态变化;
- Shared State(共享状态):保存多个 Executor 协作时需要的持久信息。
这种拆分的价值是把“谁执行、怎样连接、发生了什么、共享什么状态”分成不同对象。[^ms-workflow]
Checkpoint 在每个 Superstep 完成后创建,并保存:
- Executor State;
- Pending Messages;
- Pending Request / Response;
- Shared State。[^ms-checkpoint]
HITL 则通过 RequestPort 实现外部请求 / 响应,恢复 Checkpoint 时 Pending Request 会重新发出。[^ms-hitl]
最值得学习的是:
“等待外部输入”被建模成 Workflow Runtime 的一等机制,而不是业务 UI 特例。
Temporal:Durable History 怎样支撑长期运行 ​
Temporal 的定位非常明确:
Workflow 在 Crash、Network Failure、Infrastructure Outage 后能够继续,并可以运行几秒、几天甚至几年。[^temporal]
它最值得研究的不是 Agent Feature,而是 Durable Execution 的经典机制:
Workflow History
Deterministic Replay
Activity
Retry Policy
Timer
Signal
Versioning
适合:
- 长事务;
- 跨系统业务流程;
- 高可靠审批;
- 不可丢失任务。
代价:
- Runtime 模型更强;
- 代码需要遵循 Determinism;
- 团队学习成本更高。
Dapr:怎样把成熟 Workflow Runtime 带进 Agent ​
截至 2026-09-13,Dapr Agents v1.0 已经 GA(Generally Available,正式可用于生产),官方将其定位为面向生产的 Durable / Resilient Agent Framework,并建立在 Dapr 的状态、消息与 Workflow 能力之上。[^dapr-agents]
它的关键特点不是“能存聊天记录”,而是:
LLM Call
Tool Call
Agent State
Workflow Progress
都会进入 Durable Workflow 的持久执行语义。
官方当前示例明确展示:
- 启动 Agent Run 后立即返回 Workflow ID;
- 中途杀掉 Agent Process;
- 重新启动以后继续同一个 Workflow;
- 已完成步骤由 Durable Workflow History / State 负责恢复,目标是避免因进程重启而从头丢失进度。[^dapr-agents]
Dapr 还明确区分两种控制流:
| 形态 | 下一步由谁决定 | 适合 |
|---|---|---|
| Workflow | 开发者定义 | 确定性业务流程 |
| DurableAgent | Agent 动态决定 | 开放式 Agent 任务 |
因此它很适合研究:
如何把 Agent 自主决策放在一个成熟 Durable Workflow Runtime 上,而不是让 Agent 自己重新实现分布式可靠性。
另外,Dapr 当前还强调 Durable / Verifiable Execution 与可审计执行历史,这对于企业合规型 Agent 很有价值。[^dapr-docs]
Restate:Journal 和 Replay 怎样做 Durable Process ​
Restate 当前把自己定位为:
将 AI Agent、Workflow、Backend Service 变成 Durable Process。[^restate]
它把几个传统上分散的可靠性问题放进同一个 Durable Process 抽象:
- Durable Execution(持久执行):步骤失败后可以根据 Journal 恢复;
- Built-in State(内建状态):状态和执行历史处于同一可靠性边界;
- Reliable Communication(可靠通信):服务调用不只是一条易丢失的网络请求;
- Durable Timer(持久定时器):长时间等待不依赖一个活着的 Worker;
- Signal / External Event(信号 / 外部事件):外部输入可以唤醒等待中的 Process;
- Workflow(工作流):这些能力可以组合成长期业务控制流。
Restate 当前实现很值得研究:每个 ctx.run 等 Durable Step 的结果先写入 Journal;Journal Entry 达成复制提交后才认为这一步真正发生。失败重试时,新的 Attempt 会带着完整 Journal,SDK 根据历史结果跳过已经完成的 Step。Runtime 还用递增 Epoch 拒绝旧 Attempt 的迟到写入,避免 Zombie / Split-brain。[^restate-architecture]
这说明:
Durability
=
Journal
+
Replay
+
Ownership / Epoch
而不只是“存 State”。
适合研究:
Durable Runtime 是否一定要使用重量级 Workflow DSL,还是可以尽量保持普通代码体验。
DBOS:怎样用 Postgres 承载 Durable Execution ​
DBOS 代表另一种很值得企业研究的路线:
不引入独立 Workflow Cluster,而直接利用 Postgres 作为 Durable Workflow / Queue 的持久底座。
当前 DBOS 官方架构会:
- Workflow / Step Checkpoint 写入 Postgres;
- Crash 后从最后完成 Step 恢复;
- Durable Queue 支持 Global / Worker Concurrency 和 Rate Limit;
- 事务 Step 可以让业务 DB Write 与 Durable Step Record 同事务提交。[^dbos][^dbos-queue][^dbos-outbox]
最值得学习的是:
Durability 不一定等于独立大集群
对于本来就重度依赖 Postgres 的团队,这种架构可能显著降低运维复杂度。
代价是:
- Durable State 与 Postgres Scale 绑定;
- 高吞吐 / 大量 History 仍然需要容量设计;
- 跨数据源副作用仍需要 Idempotency。
5.2 主流路线共同收敛到什么,真正分歧在哪里 ​
问题不是:
“哪个 Runtime 最先进?”
而是:
当前任务到底需要多强的 Durability / Replay / Workflow Control。
主流 Runtime 共同收敛的机制 ​
跨 OpenAI RunState、LangGraph、Microsoft Agent Framework、Temporal、Dapr、Restate 和 DBOS,可以看到:
- Run / Workflow Instance 必须是一等持久对象;
- Waiting 不占 Worker,Timer / Human / External Event 都应被建模成 Durable State;
- Checkpoint / History 不只是容灾,还支撑 Debug、Fork、HITL 和恢复;
- 副作用必须有 Idempotency / Ledger / Reconciliation,Runtime 自己无法魔法实现 Exactly-once;
- Ownership 需要 Lease / Epoch / Fencing 等确定性机制;
- 版本升级必须进入恢复语义,旧任务不能假设新代码永远兼容。
真正的架构分歧 ​
行业没有统一答案的主要是:
- Snapshot-centric 还是 History / Replay-centric;
- Agent Loop 和显式 Workflow 谁拥有主控制流;
- Runtime 是否要求 Deterministic Code;
- Durable State 使用独立 Runtime Cluster 还是现有 Database;
- Runtime 与 Execution Provider 耦合多深。
因此企业选择 Runtime 的核心不是“谁 Feature 多”,而是 任务失败成本、等待时长、副作用风险、恢复语义和运维复杂度 是否匹配。
六、企业级运行时怎样设计 ​
企业级运行时的目标不是把所有 Agent 逻辑搬进 Workflow Engine,而是把“必须可靠”的状态、等待、副作用和 Ownership 语义放进稳定基础设施。
6.1 企业运行时应该统一什么 ​
Runtime 不会消灭复杂度,只会重新分配复杂度 ​
Durable Runtime 解决的是“任务不能因为基础设施故障就丢失”,但它不会让复杂度消失。
| 设计选择 | 解决的问题 | 新复杂度进入哪里 |
|---|---|---|
| Checkpoint | Crash 后可恢复 | State Schema、Storage、Migration、Checkpoint Cost |
| Event History / Replay | 可审计、可重放 | Determinism、History Growth、Version Compatibility |
| 权威 Operation Ledger 与 Runtime 引用 | 避免副作用重复 | Execution / Capability 负责 Operation State、Reconciliation、Retention;Runtime 负责 operation_id 和等待语义 |
| Lease / Fencing | 避免双执行 | Ownership、Clock / Expiry、Zombie Writer |
| Durable Wait | Worker 不必长时间占用 | Timer、Signal、Pending Request、Resume Burst |
| Saga / Compensation | 跨系统部分失败可处理 | Compensation Logic、Irreversible Action |
| Shared Runtime Platform | 多 Agent 复用可靠性底座 | Platform Ownership、Migration、Multi-tenant Capacity |
因此评审 Runtime 时不能只问:
“能不能 Resume?”
还必须问:
“为了获得 Resume,我们新增了哪些状态、版本、容量、调度和治理复杂度?这些复杂度是否值得?”
Runtime 升级应该由 Trigger 驱动 ​
Runtime 最容易被过度设计。升级建议统一看五类 Trigger:
Failure Trigger
→ Crash 后任务丢失、重复副作用、History 污染
Scale Trigger
→ 大量 Waiting Run、Resume Storm、并发 Worker、Fan-out
Risk Trigger
→ Payment / Deploy / Production Write 等高价值副作用
Reuse Trigger
→ 多个 Agent 重复实现 Queue / Timer / Checkpoint / Recovery
Governance Trigger
→ 多版本、多租户、长 Retention、审计与合规
典型演进:
In-process State
→ DB-backed Run State
→ Checkpoint + Wait / Signal
→ Runtime 保存 Operation Reference,Execution / Operation Service 提供权威账本与 Idempotency
→ Lease / Backpressure / Versioning
→ Durable Workflow Runtime
统一决策格式:
Trigger
→ 当前 Runtime 为什么开始失效
Decision
→ 增加什么可靠性机制
Trade-off
→ 新增什么复杂度 / 成本
Evidence
→ 用什么 Recovery / Duplicate / Resume 指标证明值得
6.2 运行时契约与状态怎样协作 ​
设计原则、职责边界与 Runtime Contract ​
企业 Runtime 最核心的原则:
把 Run 当成一等对象,而不是一个函数调用。
一个 Run 应拥有:
- ID;
- 状态;
- Version;
- Deadline;
- Budget;
- Checkpoint;
operation_id与权威操作账本引用;- Trace;
- Outcome。
这些字段不能因为都被 Runtime 保存,就都由 Runtime 定义语义。企业级契约应把 Owner(责任方)和恢复时的证据分开:
| 对象 / 契约 | 语义 Owner | Runtime 负责什么 | 恢复时依据 |
|---|---|---|---|
| Action Proposal(动作提案) | Harness 与业务 Agent | 保存引用、等待审批或调度 | 提案版本、参数摘要、风险等级 |
| 授权决策 / 执行票据 | Policy Owner / Policy Service | 持久等待、过期时间和票据引用 | 策略版本、决策证据、票据时效 |
| Operation(外部操作) | Execution / Capability 定义并推进业务语义;Execution Control Plane 或 Operation Service 持有权威账本 | 持久 operation_id、等待 / 调度状态和一致引用 |
权威操作账本、外部资源 ID、回源结果 |
| Raw Result(原始结果) | Execution / Tool Adapter | 持久或保存引用 | 执行器原始回包、状态码、时间戳 |
| Observation(观测结果) | Harness | 将其作为后续 Step 的输入引用 | 规范化版本、来源 Raw Result |
| Artifact / Evidence(产物 / 证据) | 业务域与验证机制 | 保存不可变引用和保留策略 | 内容哈希、版本、验证结果 |
Runtime 不能根据自己的数据库状态重新解释业务授权,也不能把 Raw Result 直接当成 Observation 或最终 Evidence。它的责任是让这些由不同 Owner 定义的对象在跨故障后仍保持身份、版本和引用关系。
为什么任务生命周期和真实执行必须分开
Runtime 最好区分:
运行时控制平面 ​
负责:
Run State
Scheduler
Queue
Timer
Lease
Retry
Deadline
Version
Checkpoint
Signal
Execution Plane(执行平面) ​
真正执行:
Model
Tool
Shell
Browser
Sandbox
VM
SQL
这样:
Runtime 不需要自己变成 Browser / Sandbox 系统;它只管理这些执行的生命周期。
Runtime Contract(运行时契约) ​
参考接口:
interface Runtime {
createRun(input: RunInput): Promise<Run>;
claim(runId: string): Promise<Lease>;
load(runId: string): Promise<RunState>;
checkpoint(
runId: string,
state: RunState
): Promise<Checkpoint>;
wait(
runId: string,
condition: WaitCondition
): Promise<void>;
signal(
runId: string,
signal: RuntimeSignal
): Promise<void>;
scheduleRetry(
runId: string,
retry: RetrySpec
): Promise<void>;
cancel(runId: string): Promise<void>;
complete(
runId: string,
outcome: RunOutcome
): Promise<void>;
}
这只是工程参考抽象,不是行业统一 API。
运行时状态和数据怎样存
Runtime Store 怎么选 ​
常见选择:
| Store | 优点 | 风险 | 适合 |
|---|---|---|---|
| Relational DB | 强事务、查询方便 | 高 QPS 下需设计 | 企业通用 |
| KV Store | 快、简单 | 查询能力弱 | 高频状态 |
| Event Store | Audit / Replay 强 | 复杂 | 强审计 |
| Workflow-native Store | 与 Runtime 语义集成 | 平台绑定 | Temporal / Dapr 等 |
通常不是只选一种。
例如:
Run Metadata
→ Postgres
Hot Lease
→ Redis / KV
Artifacts
→ Object Store
Events
→ Log / Event Store
Checkpoint 是存完整状态还是只存变化 ​
Full Snapshot:
每次保存完整 State
简单,但长 Run 状态不断增长。
Delta:
只保存变化
存储更省,但恢复要重放。
LangGraph 当前 DeltaChannel 就是针对 append-heavy State 的优化:减少每个 Superstep 写完整累积值,但读取恢复会增加合并成本。[^langgraph-persistence]
所以:
Checkpoint 也存在 Storage vs Recovery Latency 的 Trade-off。
企业 Runtime 参考架构 ​
把前面的对象放到同一张图里:
这张图强调四个边界:
Run Registry
→ 管任务身份和状态
Scheduler / Timer / Lease
→ 管什么时候、由谁执行
Operation Ledger Reference
→ Runtime 管副作用身份和等待引用;Execution Control Plane 或 Operation Service 管权威副作用事实
Execution Adapter
→ 管如何调用真实执行环境
企业 Runtime 的核心不是把这些都做成一个“大服务”,而是保证它们的 Contract 清晰。
6.3 规模、成本和多地域怎样改变运行时 ​
规模上来以后,哪些简单方案会失效 ​
| 小规模 | 大规模 | 架构变化 |
|---|---|---|
| 1 Worker | 1000 Worker | Lease / Fencing |
| 100 Run | 100 万 Run | Sharding / Index |
| 10 Waiting Run | 10 万 Waiting Run | Durable Timer / Efficient Wait |
| 1 Tool | 大量异步 Tool | Operation Ledger |
| 单 Agent | Subagent Fan-out | Quota / Backpressure |
| 手工 Retry | Retry Storm | Retry Budget / Circuit Breaker |
| 单版本代码 | 多版本 Run 共存 | Version Pin / Migration |
Scale 最大的反直觉:
Waiting Run 可能比 Running Run 多几个数量级。
所以容量规划不能只看 CPU。
Runtime 的成本主要花在哪里 ​
Runtime 成本不只来自 Compute。
Total Runtime Cost
=
State Storage
+ Checkpoint IO
+ Queue / Timer
+ Scheduler
+ Retry Waste
+ Idle Environment Cost
+ Artifact Storage
+ Operations / Human Cost
特别关注:
Cost per Successful Run
以及:
Cost per Waiting Run / Day
例如:
10 万 Waiting Approval Runs
即使 CPU 几乎为 0,仍然有:
- DB;
- Timer;
- Notification;
- Index;
- Compliance retention。
6.4 企业运行时什么时候应该自建或采购 ​
贯穿案例与跨领域验证
贯穿案例:Coding Agent 跨小时审批 ​
任务:
“修改支付代码,测试通过后等待 Senior Engineer Approval,再 Merge PR。”
Run 创建 ​
run_id = R123
agent_version = 12
tool_version = git_v3
status = RUNNING
修改 / 测试 ​
Tool:
edit_file
run_test
都完成后:
checkpoint C1
请求审批 ​
status = WAITING_APPROVAL
approval_id = A100
Worker 释放。
两小时后 ​
期间:
- 服务发布新版本;
- 原 Worker 已销毁;
- Sandbox TTL 已过期。
Runtime 恢复:
load R123
→ agent_version = 12
→ reconstruct execution environment
→ restore artifact / diff
→ receive A100 approval
Merge(合并) ​
生成:
operation_id = OP900
idempotency_key = pr_123_merge
调用 Git Provider。
如果 Merge 成功后进程 Crash:
Execution / Operation Service 权威账本 = SUCCEEDED
Runtime 保存 operation_id = OP900
Checkpoint = old
恢复时:
Runtime 使用
operation_id查询权威账本,读到SUCCEEDED后复用结果,不再 Merge 第二次。
这个案例串起来:
Checkpoint
Wait
Version Pin
Environment Rehydrate
Operation Ledger
Idempotency
Resume
跨领域案例
不同 Agent 需要 Runtime,不是因为它们都使用模型,而是因为它们跨越了不同的时间、故障和副作用边界。下表先说明场景特征和简单方案为什么失效,再推导最小机制:
| 场景 | 本质特征 | 简单方案怎样失效 | Runtime 最小机制 | 恢复证据与验收 |
|---|---|---|---|---|
| Data Agent | 查询可能长时运行,结果大且应与查询快照绑定 | Worker 阻塞等待会耗尽并发;盲目重提交会重复消耗计算资源,并可能读到不同数据快照 | 持久 Job ID、等待信号、结果产物引用、截止时间 | 回调与主动查询结果一致;恢复后仍指向原查询快照 |
| Coding Agent | 跨多轮修改、构建和审批,工作区会被回收,动作具有文件与进程副作用 | 只保存对话会丢失 Diff、依赖和运行进度;从头重跑可重复提交或使用已过期工具 | Checkpoint、环境清单、产物引用、版本锁定、审批等待和权威操作引用 | 在新 Worker 上恢复后 Diff 哈希一致;构建和测试可重放;Merge 不重复 |
| Ops Agent | 诊断状态持续变化,动作直接修改生产,存在影响面和回滚时限 | 用旧 Checkpoint 直接重放生产写入,可能在环境已变化时扩大故障 | 时效化授权票据、执行前重读、权威操作引用、补偿计划和健康验证 | 恢复前对账生产实际状态;执行后重新读取健康指标;超过风险门槛转人工 |
共享 Runtime 因此只应统一 Run 身份、耐久状态、等待、调度、版本、operation_id 和证据引用等稳定语义。“数据结论是否正确”、“代码是否可合并”、“服务是否恢复”仍由各业务域的 Verification(验证)判定,Runtime 只保存结果和证据引用。
Data Agent(数据智能体) ​
长 SQL / Notebook Job:
submit_query
→ WAITING_EXTERNAL
→ query callback
→ Resume
不要 Worker 阻塞等待。
Browser Agent(浏览器智能体) ​
Browser Session TTL 到期:
Checkpoint
→ environment manifest
→ recreate browser
→ restore authenticated / navigable state
Runtime 定义恢复需求,《Agent 安全执行与结果验证》负责实际环境恢复。
Ops Agent(运维智能体) ​
执行 Production Change:
Approval
→ Execute
→ Health Check
→ If Failed
→ Compensation / Rollback
Execution Control Plane 或 Operation Service 的权威操作账本必须完整,Runtime 保存 operation_id 与等待引用。
什么时候自己做,什么时候使用成熟 Runtime
不要默认自研 Durable Workflow Engine。
简单场景 ​
Postgres Run Table
+ Queue
+ Worker
可能足够。
此时最重要的不是“买 Workflow Engine”,而是先把:
Run ID
State Machine
Operation ID
Cancellation
做正确。
中等复杂度 ​
Application Checkpoint
+ Durable Timer
+ Operation Ledger
高复杂度 ​
满足多个条件时考虑成熟 Workflow Runtime:
大量 Long-running
大量 External Wait
跨系统 Saga
强恢复
高并发
严格 Audit
可以评估:
Temporal
LangGraph
Dapr
Restate
Microsoft Agent Framework
核心问题不是:
“Agent 团队是不是应该用某个热门框架?”
而是:
我们真正需要哪一种 Durable Semantics。
Build、Buy、OSS 和 Managed 怎样做 Runtime 采购决策 ​
Build(自建)、Buy(商业软件采购)、OSS(开源软件)和 Managed(托管服务)不应通过功能数量打分。Runtime 的决策核心是:在预期故障和版本变化下,候选方案能否使用可导出的状态和证据恢复正确业务语义。
Runtime 专属 PoC 应使用同一组带故障的任务,而不是只跑快乐路径:
| 验证项 | 必须注入的条件 | 通过证据 | 直接淘汰信号 |
|---|---|---|---|
| 副作用恢复 | Tool 成功后丢失回包,并在 Checkpoint 前崩溃 | 恢复前先对账,业务资源只有一份,账本可追溯 | 恢复默认重跑外部写操作 |
| 等待与信号 | 审批期间重启,回调重复与乱序 | Inbox 先持久化并去重,只有符合状态前置条件的信号推进 Run | 回调只存内存,重放会二次推进 |
| 版本兼容 | 旧 Run 等待期间升级 Workflow、Tool 和 Schema | 可锁定旧版本或显式迁移,恢复回归集通过 | 默认用最新代码反序列化旧状态 |
| 并发所有权 | Worker 失联后旧 Worker 恢复,新 Worker 已接管 | 过期 Fencing Token(栅栏令牌)写入被拒绝,无重复副作用 | 只靠超时判断 Worker 死亡 |
| 可移植与退出 | 导出 Waiting Run、Checkpoint、History、Timer 和 Operation Ledger | 备用实现可重建等待、版本和操作状态,并重放验证集 | 只能导出业务结果,不能导出可恢复语义 |
三年 TCO(Total Cost of Ownership,总拥有成本)至少包含:许可或托管费用、状态与 History 存储、跨地域复制、执行环境待机、迁移适配、平台运维人力、故障演练以及退出重建成本。成本口径应同时报告每个验证成功 Run、每个 Waiting Run 每日和每次故障恢复的完整成本,否则低价存储可能掩盖高额运维与迁移费用。
SLA(Service Level Agreement,服务等级协议)不能只约定 API 可用性,还应约定状态耐久边界、恢复时间、恢复点、信号丢失和重复的处理、版本下线通知期、安全事件通知、数据导出时限与删除证明。采购通过不等于退出能力成立;至少应在签约前完成一次导出、备用实现恢复和回放验证集的退出演练。
多地域、数据安全与合规
当 Failure Domain(故障域)从“进程 / 机器”扩大到 Region(地域)时,Durability(持久性)的保证边界也必须同步扩大:状态复制、Timer Ownership、Lease Failover、Signal 去重和 Global Idempotency 都要具备跨地域语义。
当整个地域发生故障时还能不能恢复 ​
企业级 Runtime 最终还会面对:
一个 Process 挂
→ Worker Recovery
一个 Node 挂
→ Cluster Recovery
一个 Region 挂
→ Disaster Recovery
必须定义:
RPO(Recovery Point Objective,恢复点目标)
→ 最多能丢多少状态?
RTO(Recovery Time Objective,恢复时间目标)
→ 多久必须恢复?
Multi-region 需要考虑:
- State Replication;
- Timer Ownership;
- Lease Failover;
- Duplicate Signal;
- Clock Skew;
- Global Idempotency;
- Artifact Replication。
因此:
Durability 的最高层不是“进程不丢”,而是“故障域变化以后语义仍然正确”。
持久化以后,原本的内存数据会带来哪些安全问题 ​
Runtime 会把大量原本“只在内存里”的数据长期落盘:
User Context
Tool Arguments
Approval Evidence
Model Output
Secrets / Tokens
Artifact Ref
OpenAI Agents SDK 当前也明确提醒:序列化 RunState 时,应用 Context 会成为持久数据,不应把 Secret 放进去,除非明确希望它随 State 一起存储 / 传输。[^openai-runstate]
企业 Runtime 至少需要:
- Encryption at Rest;
- Tenant Isolation;
- Field-level Redaction;
- Secret Reference,而不是 Secret Value;
- Retention Policy;
- Approval Audit;
- Access Log。
所以:
Durability 会把短期内存风险变成长期数据治理风险。
企业演进路线 ​
演进阶段不是功能清单,而是由已经出现的失效信号驱动。每个阶段都应有进入条件、退出证据、暂不建设的能力与回滚方式:
| 阶段 | 进入条件 | 核心机制 | 退出证据 | 暂不建设 / 回滚 |
|---|---|---|---|---|
| 阶段一:统一 Run | 无法追踪任务归属、截止时间和结果 | run_id、状态机、截止时间、预算、Trace |
Run 不丢失,终态可解释,一次任务可端到端追踪 | 不自建 Workflow Engine;不稳定时回退到单 Worker 但保留 Run 记录 |
| 阶段二:副作用可对账 | 已出现写操作、超时未知或重复资源 | Operation ID、幂等键、操作账本、Reconciliation | 故障注入中无未解释重复副作用,UNKNOWN 均有可执行的对账路径 |
不为全部读操作建账本;紧急回滚为停止自动写入、转人工对账 |
| 阶段三:持久等待与恢复 | 任务跨进程、需要审批或等待外部作业 | Checkpoint、Wait / Signal、Durable Inbox、版本清单 | 重启、重复信号、等待期间发版等测试下均能正确恢复 | 不持有长期 Worker 或执行环境;恢复不确定时暂停并转人工 |
| 阶段四:分布式调度 | 多 Worker、多租户或恢复风暴使单队列方案失效 | Lease、Fencing、背压、配额、公平调度 | 无脑裂写入,峰值下重要租户 SLO(Service Level Objective,服务水平目标)仍可达 | 不提前做多地域;调度故障时降低准入、保护正在执行的 Run |
| 阶段五:引入完整持久工作流 | 多团队重复实现 Timer、Saga、版本恢复和审计,自研边际成本高于迁移 | 成熟 Runtime 及治理控制面 | PoC 故障用例、三年 TCO、迁移及退出演练均达标 | 不迁移短任务和低失败成本流程;保留双读、影子恢复和回切窗口 |
这一顺序的含义不是所有系统都必须走到阶段五,而是先证明当前复杂度已经失效,再把对应可靠性责任移入共享 Runtime。如果任务可以安全从头重跑、没有外部写入且没有长时等待,停留在阶段一仍是合理架构。
七、运行时问题怎样治理 ​
7.1 先建立失败分类和 Debug 路径 ​
| 故障类型 | 典型症状 | 首查证据 | 责任边界与恢复方向 |
|---|---|---|---|
| 状态丢失 | Run 消失或退回旧状态 | State Store 写入记录、状态版本 | Runtime 恢复 Durable State(持久状态),业务域确认是否可继续 |
| Checkpoint 损坏 | 反序列化失败或状态缺字段 | Checkpoint Schema Version、校验和 | 从备份恢复或显式迁移,不用最新代码强行读取 |
| 重试风暴 | 依赖故障时请求放大 | Retry Trace、依赖健康度 | Runtime 实施退避、重试预算和熔断,Harness 处理业务重规划 |
| 重复副作用 | 重复工单、通知或付款 | Operation Ledger、外部资源 ID | 恢复前对账,使用幂等键或补偿,不盲目重试 |
| 信号丢失或重复 | Run 永久等待或重复推进 | Inbox、signal_id、状态迁移日志 |
Runtime 先持久化并去重,只在前置状态匹配时 Resume |
| 脑裂执行 | 两个 Worker 同时写同一 Run | Lease Owner、Fencing Token、State Version | Runtime 拒绝过期所有者写入;Execution 的副作用仍需幂等与对账 |
| 版本漂移 | 旧 Run 恢复失败或语义改变 | Version Manifest、恢复回归结果 | 锁定旧版本、显式迁移或安全重启并对账 |
| 执行环境丢失 | Sandbox 已销毁或凭证过期 | Environment Manifest、Artifact Hash | Runtime 提供恢复需求,Execution 重建环境,验证机制判定可否继续 |
| 背压失效 | Queue 和 Resume 速率超过下游容量 | Queue Depth、恢复速率、租户配额 | 准入控制、分批唤醒和公平调度 |
Debug(调试)顺序 ​
Run 还存在吗?
↓
State Version 正确吗?
↓
最新 Checkpoint 可读吗?
↓
Pending Operation 是什么状态?
↓
是否有 Pending Wait / Signal?
↓
当前 Lease Owner 是谁?
↓
版本兼容吗?
↓
Execution Environment 能恢复吗?
7.2 典型故障场景怎样暴露 Runtime 机制缺口 ​
以下是由分布式运行时失效模式构造的架构推演,用于设计故障注入和验收用例,不声称它们来自某家企业未公开的真实事故。
故障场景一:Tool 成功后 Crash,恢复时重复创建 ​
链路
create_ticket succeeds
→ process crash
→ checkpoint not advanced
→ resume
→ create_ticket again
错误修复
Retry max = 1
仍不能解决“不知道第一次成功没有”。
正确修复
operation_id
+ idempotency_key
+ external_resource_id
+ durable result
平台结论
副作用恢复靠 Operation Semantics,不靠 Retry 次数。
故障场景二:审批回来后代码已经升级 ​
症状
旧 Run 反序列化失败,或 Tool 参数语义改变。
根因
Checkpoint 没 Version Manifest。
正确修复
agent_version
tool_version
workflow_version
checkpoint_schema_version
Run 恢复时按旧版本执行或显式 Migration。
故障场景三:Cancel 了,但后台 Shell 继续跑 ​
根因
Cancellation 只更新 Control Plane,没有传播到 Execution Plane。
正确修复
Cancel Run
→ Propagate Cancel Signal
→ Kill Process / Tool
→ Reclaim Environment
→ Persist Terminal State
故障场景四:两个 Worker 同时恢复同一 Run ​
链路
Worker A stalls
→ Lease expires
→ B resumes
→ A wakes up
→ both write
正确修复
Lease
+ Fencing Token
+ State Version
故障场景五:Retry Storm(重试风暴) ​
外部 API 大面积故障时,每个 Run 独立重试会把原始请求放大为“Run 数 × 每个 Run 重试次数”,进一步压垮依赖。这里的关键不是某个示意数字,而是请求放大机制。
正确:
Exponential Backoff
Jitter
Retry Budget
Circuit Breaker
Global Rate Limit
故障场景六:Waiting Run 数量激增 ​
审批系统积压时,Waiting Run 可以在短时间内持续增长。
CPU 仍然不高,但:
- Timer;
- DB Index;
- Notification;
- Retention;
开始成为瓶颈。
平台结论:
Waiting Capacity 必须独立建模。
故障场景七:恢复后重复收到同一个 Signal ​
症状
Webhook / Approval 回调重复发送:
APPROVED
APPROVED
APPROVED
Runtime 如果每次都 Resume,可能重复执行后续 Step。
根因
Signal 没有:
signal_id
deduplication
expected_run_state
正确设计
Durable Inbox
+ signal_id
+ state precondition
+ idempotent transition
即:
WAITING_APPROVAL
+ APPROVAL(A100)
→ QUEUED
再次收到 A100
→ ignore / return previous result
平台结论
外部 Signal 也必须有 Idempotency Semantics。
故障场景八:Heartbeat 丢失导致双执行 ​
长 Tool:
Worker A processing 30 min
网络隔离后 Runtime 认为 A 死了,Worker B 开始 Retry。
但 A 实际仍在外部系统执行。
这时:
A + B
可能同时产生副作用。
正确设计:
- Activity Heartbeat;
- Lease / Fencing;
- Operation ID;
- 外部系统 Idempotency;
- Reconciliation。
平台结论
“Worker 看起来死了”不代表外部副作用已经停止。
故障场景九:Run 恢复了,但 Session History 被重复写入 ​
症状
Agent 恢复以后:
- 同一个 Tool Result 出现两次;
- 模型认为执行了两遍;
- 后续 Planning 开始偏离。
链路
Tool succeeded
→ append Tool Result to Session
→ append failed
→ process crashed
→ resume
→ tool executed again / result appended again
根因
Runtime 只保证:
Run State durable
却没有保证:
Session History mutation durable / idempotent
正确修复
pending session append
+
append batch identity
+
exclusive session access
+
resume reconciliation
OpenAI Agents SDK 当前已经对这类 Pending Session Append 做持久化和恢复前 Reconcile。[^openai-runstate]
平台结论
Agent Runtime 的恢复一致性必须覆盖 Run State 和 Agent History 两层。
7.3 怎样把故障映射到机制、证据和指标 ​
前面的故障场景不应该只是“故事”。
真正有价值的是形成一张 Production Map(生产映射表):
| 故障场景 | 根因 | 核心机制 | 调试证据 | 关键指标 |
|---|---|---|---|---|
| Tool 成功后 Crash,恢复时重复创建 | 副作用状态未知 | Operation Ledger + Idempotency + Reconciliation | operation_id、external_resource_id、Tool Result | Duplicate Side-effect Rate |
| 审批回来后旧 Run 无法恢复 | 版本漂移 | Version Pinning + Migration | version manifest、checkpoint schema | Resume Failure Rate |
| Cancel 后 Shell 继续跑 | Cancel 未传到执行层 | Cancellation Propagation | cancel event、process state | Cancel Completion Latency |
| 两个 Worker 同时恢复一个 Run | Ownership 不唯一 | Lease + Fencing Token | lease_owner、fencing_token | Split-brain Incident |
| 外部系统故障引发 Retry Storm | Retry 无全局预算 | Backoff + Retry Budget + Circuit Breaker | retry trace、dependency health | Retry Amplification |
| Waiting Run 数量爆炸 | 等待容量未建模 | Durable Timer + Capacity Planning | waiting_count、timer_count | Cost per Waiting Run / Day |
| 同一个 Approval Signal 重复到达 | Signal 非幂等 | Durable Inbox + signal_id | signal log、state transition | Lost / Duplicate Signal Rate |
| Heartbeat 丢失后双执行 | Liveness 与 Ownership 混淆 | Heartbeat + Lease + Fencing | heartbeat、lease、operation ledger | Duplicate Side-effect Rate |
| Run 恢复但 Session History 重复 | History mutation 未持久对齐 | Pending Append + Reconciliation | session batch id、history diff | History Corruption Rate |
这张表体现一个重要原则:
生产治理不是“遇到事故再看日志”,而是 Failure → Evidence → Mechanism → Metric 的闭环。
7.4 怎样衡量运行时的正确性、稳定性和效率 ​
指标只有在样本、分母、时间窗和归因规则固定后才能用于治理。本节不再沿用系统性质的 P0 / P1 / P2,而是分为结果指标、诊断指标和效率指标,避免把“先保证什么”与“如何定位问题”混成一套优先级。
结果指标|最终是否不丢、不重、不乱
| Metric | 口径 | 主要证据 |
|---|---|---|
| Run Completion Rate | 观测窗口内进入合法终态且通过业务验证的 Run / 到达 Deadline 的有效 Run;用户取消单独分类 | Run State Transition、Verification Result |
| Recovery Success Rate | 故障注入或真实故障后在恢复时限内回到可安全推进状态的 Run / 进入恢复的 Run | Recovery Attempt、Checkpoint、Reconciliation Evidence |
| Duplicate Side-effect Rate | 被确认为重复业务效果的 Operation / 已执行外部写 Operation | Operation Ledger、外部资源 ID |
| Lost Run Rate | 已接受但无可查状态或不能解释终态的 Run / 已接受 Run | 接入日志、Run Registry |
| Lost Signal Rate | 已由上游确认投递却未进入 Inbox 或未形成合法迁移的信号 / 已投递有效信号 | 上游投递记录、Inbox、State Transition |
| History Duplication / Corruption Rate | 恢复后发生重复或无来源 History 写入的 Run / 已恢复 Run | Session Batch ID、History Diff |
诊断指标|问题发生在哪个环节
| Metric | 含义 |
|---|---|
| Stuck Run Rate | 卡住比例 |
| Checkpoint Failure Rate | Checkpoint 失败 |
| Retry Recovery Rate | 重试恢复效果 |
| Split-brain Incident | 并发所有权错误 |
| Resume Failure Rate | 恢复失败 |
效率指标|是否足够快、足够省
| Metric | 含义 |
|---|---|
| Mean / P95 Resume Latency | 恢复延迟 |
| Checkpoint IO / Run | 存储成本 |
| Retry Amplification | 重试额外倍数 |
| Cost per Successful Run | 成功任务成本 |
| Cost per Waiting Run / Day | 等待任务持有成本 |
| Scheduler Queue Latency | 调度等待 |
7.5 容量和成本怎样进入运行时治理 ​
至少估算:
Active Runs
Waiting Runs
Runs Created / sec
Resume / sec
Checkpoint QPS
Checkpoint Size
Timer Count
Queue Depth
Model Concurrency
Tool Concurrency
Subagent Fan-out
Artifact Volume
最容易漏掉:
Resume Storm(恢复风暴)
例如依赖系统恢复后:
100k WAITING_EXTERNAL
→ 同时收到 Signal
→ 瞬间 Resume
需要 Admission Control(准入控制)和分批唤醒。
可以做一个粗略容量模型:
required_resume_capacity
=
incoming_resume_rate
×
avg_step_duration
例如:
10,000 resumes / min
×
6 sec avg execution
≈
1,000 concurrent slots
再乘:
- model fan-out;
- subagent factor;
- tool concurrency;
最终可能远大于表面 Run 数。
因此 Runtime Capacity Planning 必须同时建模:
steady-state
+
burst
+
recovery burst
三种流量。
Runtime 的成本到底花在哪里 ​
Runtime Cost
=
State Storage
+ Checkpoint IO
+ Queue / Timer
+ Scheduler
+ Retry Waste
+ Waiting Retention
+ Environment Hold Cost
+ Human Operations
优化要看:
Cost per Verified Successful Run
Cost per Waiting Run / Day
Cost per Retained Completed Run
Retention 越长:
- Audit 越强;
- Storage Cost 越高。
因此 Cost Model 需要同时考虑:
active cost
+
waiting cost
+
historical retention cost
而不是:
DB 单次写入多少钱
7.6 怎样证明运行时真的能够恢复 ​
上线前至少演练:
1. Model Call 中进程 Crash
2. Tool 成功后 Checkpoint 前 Crash
3. Approval 等待期间 Deploy
4. Sandbox 被回收
5. Database Failover
6. Queue Duplicate Delivery
7. Worker Split Brain
8. Timer Service 延迟
9. Version Migration Failure
10. Massive Resume Storm
Durability 是:
必须通过故障注入证明的系统属性。
不能只靠单元测试证明。
怎样评测 Runtime(运行时)
Outcome Eval(结果评测) ​
Recovery Success
Duplicate Side Effect
Resume Latency
Ablation(消融实验) ​
关闭:
Operation Ledger
Checkpoint
Fencing
Backoff
观察 Failure。
Counterfactual(反事实评测) ​
构造:
Tool success + lost response
Old Checkpoint + new Tool Schema
Duplicate Signal
Clock skew
Lease expiry
检查 Runtime 是否保持正确性。
Sensitivity(敏感性分析) ​
变化:
checkpoint_interval
lease_ttl
retry_max
queue_concurrency
寻找合理 Operating Range。
7.7 运行时怎样发布、灰度和迁移 ​
发布要考虑:
New Run
Old Running Run
Waiting Run
Paused Approval Run
Retry-scheduled Run
推荐:
new runs → new runtime version
old runs → pinned old version
worker drain
checkpoint compatibility check
migration canary
resume test
rollback
指标必须按:
runtime_version
checkpoint_schema_version
agent_version
分桶。
附录 A:高频架构问题与快速回答 ​
整篇 Runtime 机制可以压缩成下面这条主线:
任务活得越来越久
↓
内存状态不可靠
↓
需要 Run + Checkpoint
任务会等待
↓
需要 Durable Wait + Signal
任务会改变真实世界
↓
需要 Operation Ledger + Idempotency + Reconciliation
任务会分布式运行
↓
需要 Lease + Fencing + Backpressure
任务会跨版本、跨故障域恢复
↓
需要 Versioning + Migration + DR
A.1 核心概念与边界问题 ​
Q1:什么是 Agent Runtime? ​
30 秒答案
Agent Runtime 是负责一次 Agent Run 的状态、调度、等待、重试、恢复、并发和持久化的运行层。Harness 解决“怎么工作”,Runtime 解决“这个 Run 怎么可靠地活着”。
详细答案
如果只有 Agent Loop:
Model
→ Tool
→ Observation
→ Model
它可以在一个进程里正常工作。
但只要出现:
进程 Crash
人工审批等待
异步 Tool
服务发布
Worker 迁移
并发恢复
就需要另一个系统回答:
Run 现在是什么状态?
做到哪一步?
等什么?
哪些副作用已经发生?
谁有权继续执行?
恢复时用哪个版本?
Runtime 就负责这些语义。
它通常包括:
Run State
Checkpoint
Queue / Scheduler
Wait / Signal
Retry
Lease
Cancellation
Version
Operation Ledger
边界
Runtime 不等于 Harness,也不等于 Sandbox。Runtime 可以调用 Execution Plane,但不负责真正执行 Browser / Shell。
常见误区
“Runtime 就是保存 Agent Messages 的数据库。”
Messages 只是状态的一部分;真正 Runtime 还要管理 Control Flow、等待、副作用和恢复。
Q2:什么是 Durable Execution? ​
30 秒答案
Durable Execution 是让一个长任务在 Crash、Restart、Deploy、Node Migration、Long Wait 之后,仍能从正确状态继续,而且不错误重复已经发生的副作用。
详细答案
Durability 至少包含:
Durable State
+
Durable Control Flow
+
Durable Wait
+
Durable Side-effect Semantics
+
Version-aware Resume
举例:
Agent 创建工单成功
→ 进程 Crash
→ 2 分钟后恢复
如果 Runtime 只保存“当前 Step”,恢复时可能再次创建工单。
真正 Durable Runtime 应该知道:
operation_id = OP1
external_ticket_id = T100
status = SUCCEEDED
于是跳过副作用,继续下一步。
高分点
Durable Execution 的核心不是“任务不会失败”,而是“失败以后可以在保持语义正确的前提下继续”。
Q3:Checkpoint 和 State 有什么区别? ​
30 秒答案
State 是“当前事实”;Checkpoint 是“某一时刻可以用于恢复的持久快照”。
详细答案
Run State 可能不断变化:
status
current_step
budget
latest_observation
Checkpoint 则是:
State
+ Resume Cursor
+ Pending Request
+ Pending Operation
+ Version Manifest
+ Artifact Pointer
因此:
State
≠
Checkpoint
一个系统可以在内存中有 State,但如果没有 Durable Checkpoint,进程 Crash 后仍然无法恢复。
工程边界
Checkpoint Frequency 需要权衡:
write cost
vs
recovery rework
高价值边界如 Tool Side Effect、Approval、Model Result 通常更值得保存。
Q4:为什么 Checkpoint 不是简单存 JSON? ​
30 秒答案
因为恢复需要的不只是“数据值”,还要恢复控制流、待处理工作、副作用状态、版本和环境需求。
详细答案
简单 JSON:
{"step": 3}
无法回答:
Step 3 的 Tool 是否已经执行?
审批是否还在等?
Tool 是 V1 还是 V2?
原 Sandbox 是否需要重建?
恢复后下一步是 Retry 还是 Continue?
完整 Checkpoint 至少需要:
logical state
next cursor
pending requests
pending operations
artifact refs
budget / deadline
version manifest
environment manifest
常见误区
“把整个 Agent 对象 pickle 一下。”
这会把进程内实现细节和 Durable Contract 混在一起,版本升级也极难兼容。
Q5:为什么 Exactly-once 很危险? ​
30 秒答案
Exactly-once 要先问“在哪个边界内”。网络和外部系统存在不确定性,通常不能靠 Runtime 一句话保证业务副作用全局恰好一次;现实方案是 Idempotency + Durable Ledger + Reconciliation。
详细答案
经典场景:
charge_payment()
→ 支付成功
→ Response Lost
→ Runtime 看到 timeout
如果直接 Retry,就可能重复付款。
所以:
Delivery exactly-once
≠
Business Effect exactly-once
更可靠:
operation_id
idempotency_key
external_resource_id
result record
reconcile()
例外
如果业务写入和 Durable Step Record 能在同一个 DB Transaction 中提交,例如 DBOS 的 Transaction Step,那么这个数据库边界内可以获得更强的 Exactly-once 语义;但跨外部 API 后仍要重新分析。
Q6:Run 和 Session 有什么区别? ​
30 秒答案
Run 是一次具体任务执行;Session / Thread 是多轮交互容器。一个 Session 可以包含多个 Run。
详细答案
例如:
Session: “修复支付 Bug”
├─ Run 1:定位
├─ Run 2:用户补充信息后修改
└─ Run 3:最终验证
Run 有自己的:
- Budget;
- Checkpoint;
- Operation Ledger;
- Outcome;
- Trace。
Session 更偏:
- conversation history;
- user continuity。
如果两者混在一起:
- 成本无法归属;
- Checkpoint 不清楚;
- Approval 难定位;
- Retry 可能跨任务污染。
边界
不同产品命名不统一,关键是逻辑语义要分开。
Q7:为什么 HITL 是 Runtime 问题? ​
30 秒答案
因为 HITL 的本质不是 UI 弹窗,而是一个可能持续小时或数天的 Durable Wait。
详细答案
当 Agent 请求审批:
Run
→ WAITING_APPROVAL
此时 Runtime 应:
- 保存 Pending Request;
- 持久化 State;
- 释放 Worker;
- 等待 Signal;
- 审批回来后重新 Queue;
- 从正确 Version Resume。
审批期间进程可以完全不存在。
还需要:
- approver identity;
- deadline;
- evidence;
- multi-approval;
- version compatibility。
所以 Approval 是 Runtime State,而不是前端状态。
Q8:为什么取消不是改 status? ​
30 秒答案
因为数据库里写 status=CANCELLED 只改变控制面,不代表真实 Tool、Shell、Browser 已停止。
详细答案
正确取消链路:
User Cancel
→ CANCELLING
→ stop queued work
→ notify worker
→ cancel model request
→ terminate tool / process
→ reclaim sandbox
→ persist terminal state
还要考虑 Race:
Cancel
vs
Tool just completed
如果 Tool 已产生副作用,Runtime 还需要正确记录 Result,不能因为 Cancel 就把真实世界状态忘掉。
高分点
Cancellation 是分布式状态转换,不是一个 Boolean。
Q9:什么场景不需要 Durable Workflow Engine? ​
30 秒答案
秒级、无副作用、无外部等待、失败可以低成本从头重跑的 Agent,通常不需要复杂 Durable Workflow Engine。
详细答案
例如:
用户问天气
→ Tool
→ Answer
即使进程 Crash:
重新请求一次
成本很低。
如果为了这种任务引入:
- Event Sourcing;
- Saga;
- Durable Timer;
- Worker Version Pin;
只会增加复杂度。
可以按:
Task Lifetime
×
Side-effect Risk
×
Recovery Value
×
Concurrency Scale
决定 Runtime 等级。
高分点
Durable Runtime 是失败成本驱动的,而不是 Agent 身份驱动的。
Q10:Runtime 和 Workflow Engine 有什么关系? ​
30 秒答案
Workflow Engine 是实现 Durable Runtime 的一种重要技术,但 Runtime 范围更广。
详细答案
Workflow Engine 强项:
Control Flow
Checkpoint
Timer
Retry
Signal
Agent Runtime 还可能需要:
Agent RunState
Operation Ledger
Model Usage
Approval
Execution Environment Manifest
Agent / Tool Version
有些系统:
- Temporal:Workflow Runtime 更强;
- OpenAI Agents SDK:Agent RunState 更强;
- LangGraph:Agent Graph + Persistence 融合。
因此:
Workflow Engine 可以承载 Runtime,但 Runtime 概念不应被限定成 Workflow DAG。
常见误区
把“用了 Agent”理解成“不需要 Workflow Runtime”。实际上长任务 Agent 往往更需要 Durable Control。
A.2 生产工程问题 ​
Q11:Tool 成功后 Crash,怎么避免重复执行? ​
详细答案
最危险的 Crash Point 是:External Tool 已成功,但 Runtime 还没持久化 operation_id 与等待关系。正确链路应由 Execution / Operation Service 先保留 operation_id 并将权威账本写为 PLANNED,Runtime 再保存一致引用;Execution 调用带 Idempotency Key 的外部 Tool,并将 external_resource_id / result 写入权威账本后标记 SUCCEEDED。恢复时 Runtime 先使用 operation_id 查询权威账本:SUCCEEDED 直接复用;STARTED / UNKNOWN 先 Reconcile,不能盲目 Retry。
Q12:Retry 怎么分类? ​
详细答案
Retry 至少区分 Transient、Deterministic、Permission、Side-effect Unknown、Dependency Outage、Model Format。429 可以 Backoff;SQL Syntax Error 同参数 Retry 没意义;Payment Timeout 则必须先查外部状态。Retry Policy 应结合 Error Classifier、Deadline、Attempt、Side-effect Contract 和 Retry Budget。
Q13:Checkpoint 多久一次? ​
详细答案
Checkpoint 频率本质是 Checkpoint IO Cost 与 Crash 后 Rework Cost 的权衡。Model Call 完成、高价值 Tool Result、不可逆 Side Effect、Human Approval、Phase Boundary 通常值得 Checkpoint;纯内存格式转换未必。不要机械地“每 N 秒一次”。
看:
checkpoint cost
vs
recovery rework cost
副作用 / Model / Approval 等边界优先 Checkpoint。
Q14:如何处理 Side-effect Unknown? ​
详细答案
Side-effect Unknown 是最危险状态。请求 Timeout 后无法知道对方没执行、执行失败,还是已执行但响应丢失。必须提供 query_by_operation_id、查询外部资源或业务状态的 Reconcile Path;只有确认未发生,才能重试。
Q15:如何避免两个 Worker 同时执行一个 Run? ​
详细答案
典型做法是 claim(run_id, expected_version) → lease + fencing_token。Worker 每次写 State 都带 state version 和 fencing token。Lease 过期后旧 Worker 即使恢复,也因为 Token 过旧而无法写入。这比单纯分布式锁更能防 Zombie Writer。
Q16:Waiting Run 怎么实现低成本? ​
详细答案
Waiting Run 应持久化 wait condition + wake_at / signal key 后释放 Worker。Approval 等待 2 天不需要一个 Worker 活 2 天;Signal 到达后再重新 Queue / Claim / Resume。这让十万 Waiting Run 的 CPU 仍可接近零。
持久化:
wait condition + wake_at / signal key
释放 Worker,由 Timer / Signal 再 Queue。
Q17:审批回来时 Tool 已升级怎么办? ​
详细答案
Run State 应保存 Agent、Prompt、Tool、Skill、Workflow、Runtime、Checkpoint Schema Version。恢复策略只能是 Pin Old Version、Explicit Migration 或 Safe Restart / Reconcile 之一,不能默认用最新代码反序列化旧状态。
Q18:怎样处理 Runtime Version Upgrade? ​
详细答案
Runtime 发布必须区分 New Run、Running Run、Waiting Run 和 Retry-scheduled Run。推荐 New Run 使用新版本、Old Run Pin 老版本、旧 Worker Drain、Migration 先 Canary,并单独做 Resume Compatibility Test。
Q19:Saga 为什么适合 Agent? ​
详细答案
Agent 跨 Ticket、Cloud、Git、Email、Payment 时没有统一事务。Saga 将流程拆成 Forward Action + Compensation,例如 create resource → delete resource、merge PR → revert commit。但 Compensation 不等于真实 Rollback,不可逆动作应提高 Approval / Verification 门槛。
Q20:Checkpoint Full Snapshot 和 Delta 怎么选? ​
详细答案
Full Snapshot 简单且 Resume 快,但状态大时写放大严重;Delta 写入小,但恢复需要 Merge / Replay。常见折中是 Periodic Full Snapshot + Intermediate Delta。选择取决于 State Growth、Checkpoint QPS 和 Recovery Latency。
附录 B:正文资料脚注 ​
[^restate-architecture]: Restate, Architecture / Request Lifecycle. https://docs.restate.dev/references/architecture [^restate-retention]: Restate, Service Configuration — Retention. https://docs.restate.dev/services/configuration [^dbos]: DBOS Architecture. https://docs.dbos.dev/architecture [^dbos-queue]: DBOS Queues. https://docs.dbos.dev/python/reference/queues [^dbos-outbox]: DBOS Transactional Outbox. https://docs.dbos.dev/python/examples/outbox [^dbos-recovery]: DBOS Workflows & Steps — max recovery attempts. https://docs.dbos.dev/python/reference/decorators
[^openai-runstate]: OpenAI Agents SDK, RunState / Human-in-the-loop. https://openai.github.io/openai-agents-python/ref/run_state/ ; https://openai.github.io/openai-agents-python/human_in_the_loop/ [^openai-hitl]: OpenAI Agents SDK, Human-in-the-loop. https://openai.github.io/openai-agents-python/human_in_the_loop/ [^ms-checkpoint]: Microsoft Agent Framework, Workflows - Checkpoints. https://learn.microsoft.com/en-us/agent-framework/workflows/checkpoints [^ms-hitl]: Microsoft Agent Framework, Workflows - Human-in-the-loop. https://learn.microsoft.com/en-us/agent-framework/workflows/human-in-the-loop [^ms-workflow]: Microsoft Agent Framework, Workflow concepts. https://learn.microsoft.com/en-us/agent-framework/concepts/workflows/ [^langgraph-persistence]: LangGraph, Persistence. https://docs.langchain.com/oss/python/langgraph/persistence [^langgraph-time-travel]: LangGraph, Time travel. https://docs.langchain.com/oss/python/langgraph/use-time-travel [^langgraph-interrupt]: LangGraph, Interrupts. https://docs.langchain.com/oss/python/langgraph/interrupts [^langgraph-functional]: LangGraph, Functional API / Durable Execution. https://docs.langchain.com/oss/python/langgraph/functional-api [^temporal]: Temporal Documentation. https://docs.temporal.io/ [^dapr]: Dapr Documentation — Durable execution for workflows and AI agents. https://docs.dapr.io/ [^dapr-agent]: Dapr Agents — Why Dapr Agents / Durable Agent. https://docs.dapr.io/developing-ai/dapr-agents/dapr-agents-why/ [^dapr-agents]: Dapr Docs, Dapr Agents v1.0 — Generally Available, https://docs.dapr.io/developing-ai/dapr-agents/ [^dapr-docs]: Dapr Docs, Durable execution engine for workflows and AI agents, https://docs.dapr.io/ [^restate]: Restate Documentation. https://docs.restate.dev/
[^ms-checkpoint-storage]: Microsoft Agent Framework, Workflow Checkpoints and CheckpointStorage providers. https://learn.microsoft.com/en-us/agent-framework/workflows/checkpoints
附录 C:参考资料(截至 2026-09-13) ​
-
OpenAI Agents SDK — RunState
https://openai.github.io/openai-agents-python/ref/run_state/ -
OpenAI Agents SDK — Human-in-the-loop
https://openai.github.io/openai-agents-python/human_in_the_loop/ -
LangGraph — Persistence
https://docs.langchain.com/oss/python/langgraph/persistence -
LangGraph — Interrupts
https://docs.langchain.com/oss/python/langgraph/interrupts -
LangGraph — Time Travel
https://docs.langchain.com/oss/python/langgraph/use-time-travel -
LangGraph — Functional API
https://docs.langchain.com/oss/python/langgraph/functional-api -
Microsoft Agent Framework — Workflow Concepts
https://learn.microsoft.com/en-us/agent-framework/concepts/workflows/ -
Microsoft Agent Framework — Checkpoints
https://learn.microsoft.com/en-us/agent-framework/workflows/checkpoints -
Microsoft Agent Framework — Human-in-the-loop
https://learn.microsoft.com/en-us/agent-framework/workflows/human-in-the-loop -
Temporal Documentation
https://docs.temporal.io/ -
Dapr Documentation
https://docs.dapr.io/ -
Dapr Agents — Durable Agent
https://docs.dapr.io/developing-ai/dapr-agents/dapr-agents-why/ -
Restate Documentation
https://docs.restate.dev/ -
DBOS Documentation
https://docs.dbos.dev/ -
DBOS Architecture
https://docs.dbos.dev/architecture -
DBOS Queues
https://docs.dbos.dev/python/reference/queues -
Restate Architecture
https://docs.restate.dev/references/architecture -
Restate Retention
https://docs.restate.dev/services/configuration