← [ BACK_TO_NOTES ]

Agent Runtime 中 Command、Event、Replay、Resume 与状态层的关系

把 L3 对话中形成的 Command/Event/Effect、Replay/Resume、ActiveTurn/Event Log/Checkpoint/Read Model、Thread/Run/Interaction/Config 等关系收敛成一套可恢复 Agent Runtime 心智模型。

#synthesis #growing #ai #architecture #agent #bodysense

[!info] related notes

Agent Runtime 中 Command、Event、Replay、Resume 与状态层的关系

覆盖范围

这篇笔记不重新解释 SSE、Reducer、Checkpoint 等单个概念,而是解决 L3 学习中最容易混淆的一组关系:

  • Command、Event、Effect 分别是什么;
  • Replay 为什么不能重新执行 Command;
  • Resume 为什么不是 Replay;
  • ActiveTurnState、Runtime Event Log、Checkpoint、Read Model 分别拥有哪类状态;
  • Interaction ID、Semantic Thread ID、Public Run ID、Configuration ID 为什么不是同一个 identity;
  • interrupted、completed、failed、cancelled 的 authority 与终止语义。

这组关系来自 BodySense Consultation L3 的实际学习场景,但可以复用于其他 production Agent Runtime。

一张总图:从用户意图到可重放 UI

                         User Intent
                             │
                             ↓
                          Command
                  SubmitAnswer / CancelRun
                             │
                             ↓
┌────────────────────── Server Runtime ──────────────────────┐
│                                                            │
│  Semantic Thread / Agent Execution                         │
│             │                                              │
│             ├──── Checkpoint                               │
│             │      “未完成执行从哪里继续?”                 │
│             │                                              │
│             ├──── Commands / Tools / real side effects     │
│             │                                              │
│             └──── Durable Runtime Events                   │
│                    “已经发生了什么?”                       │
│                                                            │
│  Durable Interaction / Messages / Read Model               │
│                    “现在稳定展示什么?”                     │
└───────────────────────────┬────────────────────────────────┘
                            │
                 ┌──────────┴──────────┐
                 ↓                     ↓
              Live SSE          after_seq Replay
                 └──────────┬──────────┘
                            ↓
                       Network bytes
                            ↓
                           unknown
                            ↓
                    Runtime Validation
                            ↓
                    Trusted StreamEvent
                            ↓
                       Pure Reducer
                            ↓
                     ActiveTurnState
                   “当前正在发生什么?”
                            ↓
                            UI

完成后:
ActiveTurn ── reconcile ──> Durable Read Model
                         └─> reset ActiveTurn

这张图是 L3 最重要的总心智模型:实时 transport 可以断,执行 identity 和 durable facts 不能跟着消失;Live 与 Replay 最终都必须收敛到同一个 projection。

Command、Event、Effect:不要把“请求改变现实”和“现实已经改变”混在一起

Command

Command 表达:

我希望系统改变现实。

例如:

SubmitInteractionAnswer(I7, "Yes")
CancelRun(R7)
SendNotification(N3)

Command 需要经过服务端当前状态、权限、幂等和时效性验证。

Event

Event 表达:

某个变化已经成为事实。

例如:

interaction.answered(I7)
run.resumed
run.cancelled
notification.sent(N3)

Effect

前端 Reducer 可以在纯状态转换之外声明客户端协调型 Effect,例如:

reconcileConversation
invalidate durable query
upsert query cache

但不要让 Replayable public Event 重新触发现实世界 Side Effect,例如:

send email
execute tool
resume checkpoint
cancel server run

完整关系

User Intent
   ↓
Command
   ↓
Server State Machine
   ↓
Real Side Effect / Durable Transition
   ↓
Event
   ↓
Reducer
   ↓
Projection
   ↓
Client Coordination Effect

核心记忆:

Command 请求创造事实;Event 描述事实已经成立;Reducer/Effect 负责根据事实让客户端收敛。

Replay vs Resume:重建过去,不重新创造过去

这两个词很像,但语义完全不同。

Replay

Replay 的目标是:

Event Log
↓
validation
↓
same reducer
↓
reconstruct projection

它回答:

历史上发生过什么,现在 UI 应恢复成什么样?

例如历史已有:

tool.call T7
 tool.result T7 success
notification.sent N3
interaction.answered I7
run.resumed

Replay 只能把这些事实重新投影出来,不能再次调用工具、再次发送通知、再次提交答案或再次 Resume。

Resume

Resume 的目标是:

unfinished Semantic Thread
+
Checkpoint
+
validated Resume Input
↓
continue real execution

它回答:

这个尚未结束的 Agent execution 应该从哪里继续?

所以:

Replay = reconstruct history/projection
Resume = continue unfinished computation

核心记忆:

Replay 重建过去,不重新创造过去。

四种 State Owner:同样都“保存状态”,回答的问题却不同

┌────────────────────────────────────────────┐
│ ActiveTurnState                            │
│ 当前 Run 的 transient/live projection     │
│                                            │
│ 回答:当前正在发生什么?                   │
└────────────────────────────────────────────┘

┌────────────────────────────────────────────┐
│ Runtime Event Log                          │
│ append-only durable runtime facts          │
│                                            │
│ 回答:执行过程中发生过什么?               │
└────────────────────────────────────────────┘

┌────────────────────────────────────────────┐
│ Checkpoint                                 │
│ framework/runtime internal continuation    │
│                                            │
│ 回答:未完成 execution 从哪里继续?        │
└────────────────────────────────────────────┘

┌────────────────────────────────────────────┐
│ Durable Read Model                         │
│ finalized messages / thread / workspace    │
│                                            │
│ 回答:现在稳定页面应该展示什么?           │
└────────────────────────────────────────────┘

因此:

ActiveTurn ≠ Event Log ≠ Checkpoint ≠ Read Model

普通刷新优先读取 Read Model;若存在 active/interrupted Run,再通过 Event Log catch-up 恢复当前 Runtime Projection。真正 Resume Agent 则依赖 Checkpoint,而不是让前端解析 Event Log 猜执行内部状态。

Event Log append-only,不代表 Projection 也 append-only

历史可以保存:

seq=3 tool.call   T7 running
seq=8 tool.result T7 completed

两条 Event 都应该保留,因为它们描述历史。

但 ActiveTurn 中:

toolCallsById[T7] = completed

应该只有一个当前实体。

因此:

History / Event Log
→ append

Current Projection
→ reduce / keyed upsert / converge

Text delta 常常是 append;Tool、Citation、Interaction 等更常使用 stable identity 做 upsert 或小状态机。

Identity Stack:每个 ID 回答不同的问题

一个 Consultation 可能同时存在:

Conversation C1
   ↓
Semantic / Checkpoint Thread T7
   ├── Public Run R10
   │       ↓
   │   Interaction I7
   │       ↓
   │   interrupted
   │
   └── Public Run R11
           ↑ resumes R10
           ↓
       completed

Pinned Agent Configuration = C15

Interaction ID

I7

回答:

用户这次答案是在满足哪个等待点?

Tool Call ID

ToolCall T3

回答:

这次 Agent Tool Invocation 是谁?

如果 ask_user 起源于 Tool Call,它可以关联 Interaction,但二者生命周期不同,因此不应默认共用 identity。

Semantic / Checkpoint Thread ID

Thread T7

回答:

哪一条 Agent execution history / checkpoint chain 被继续?

Public Run ID

R10 / R11

回答:

API / Event Log 对外如何标识本次 execution segment?

Resume 可以保持同一个 Public Run,也可以创建明确关联的 resumed segment;关键不是选哪一种,而是语义必须稳定并可审计。

Configuration ID

C15

回答:

这条 execution 当初由哪套行为配置定义?

同一 Semantic Thread Resume 时默认继续 pinned C15,而不是因为 production pointer 已经升级到 C16 就偷偷换配置。

Resume:先判断还能不能继续,再保持原执行身份

假设:

09:00 Thread T7 starts with Config C15
09:02 interaction.required → interrupted
10:00 production pointer C15 → C16
11:00 user answers

Resume 有两个独立问题:

Current Business Eligibility
→ 旧 execution 现在还允许继续吗?

Execution Identity
→ 如果继续,应使用哪套 config?

正确关系:

仍允许继续
→ resume T7 with pinned C15

不再允许继续
→ explicit cancel/supersede
→ start new analysis if needed

不要用“重新授权当前世界”作为借口偷偷切换到 C16。

answered ≠ resumed:Durable Workflow 必须允许中间态修复

真实故障:

I7 pending
↓
user submits answer
↓
answer persisted successfully
↓
process crashes before resume

此时:

Interaction I7 = answered ✅
Semantic Thread T7 = still interrupted ❗

因此:

interaction.answered
≠
run.resumed

系统重启后应识别:

answered but unconsumed

然后在确认仍允许 Resume 后:

resume T7 exactly once

而不是重新问用户或重复保存 answer。

这就是 durable workflow 的 repair semantics。

Double Submit:幂等不只是 UI 去重

用户可能因 HTTP response 丢失重复提交:

SubmitAnswer(I7, Yes)
SubmitAnswer(I7, Yes)

目标不是仅仅 disable button,而是服务端保证:

one durable answer
+
at most one semantic resume

如果第二次答案不同:

Yes → No

必须有明确 conflict/revision policy,不能 accidental last-write-wins,否则 execution 实际消费的答案和数据库最终显示的答案可能互相矛盾。

Interaction Answer 不是自动的 Domain Fact

Interaction Answer
= 用户确实这样回答过

但它只是 domain input/observation candidate。

例如:

“是,我右肩会痛”

还可能经过:

normalization
source/provenance
timestamp
conflict handling
domain validation

才能进入 BodyState 的 accepted Observation/Fact。

所以:

Interaction Answer
↓
Domain Input / Observation
↓
BodyState Policy
↓
Accepted Fact

不要把 UI answer 自动升级成最终领域真值。

Transport Lifetime 与 Run Lifetime

SSE disconnected
≠ run.failed

AbortController.abort()
≠ run.cancelled

浏览器断线或主动停止 reader,只能证明 transport 状态发生变化。

真正取消 Run 应是正式 Command:

CancelRun(R7)
↓
server state / authority validation
↓
cancellation propagation where possible
↓
run.cancelled

其中 run.cancelled 是权威 terminal fact。

Terminal State:唯一且不可逆

典型 terminal:

completed
failed
cancelled

而:

interrupted

不是 terminal,因为它仍可 Resume。

一旦 Durable Event Log 已经确认:

seq=20 run.completed

之后到达的 Cancel 应被视为 too late / already terminal,而不是:

completed → cancelled

同理,terminal 后迟到的 text/tool event 也不能重新打开 Run。

一条 BodySense Consultation 完整路径

User starts consultation
        ↓
Run / Thread starts
        ↓
message.text.delta
        ↓
tool.call T1
        ↓
--- network lost ---
        ↓
server persists tool.result T1
server persists interaction.required I7
        ↓
after_seq catch-up
        ↓
same validator + same reducer
        ↓
ActiveTurn:
T1 = completed
status = interrupted
pendingInteraction = I7
        ↓
user submits Answer(I7)
        ↓
interaction.answered durable
        ↓
resume eligible?
        ↓ yes
resume same Semantic Thread / pinned config
        ↓
run.resumed
        ↓
streaming
        ↓
run.completed / failed / cancelled
        ↓
reconcile durable Read Model
        ↓
reset ActiveTurn

这条路径把 L3 中原本容易分散理解的 Trust Boundary、Reducer、Recovery、HITL、Durability、Identity 与 Cancellation 放进同一个运行时闭环。

源码审查时的快速问题

看到一段 Runtime 代码时,可以按顺序问:

  1. 这里接收的是 unknown、Command 还是已验证 Event?
  2. 这段代码是在“请求改变现实”,还是“记录/投影已经发生的事实”?
  3. 这个 Event 如果 Replay,会不会错误地重新执行现实副作用?
  4. 当前状态属于 ActiveTurn、Event Log、Checkpoint 还是 Read Model?
  5. Resume 的 identity 是 Interaction、Thread、Public Run 还是 Config?
  6. answered、resumed、completed 是否被错误合并成一个状态?
  7. retry / double submit 是否能收敛为一次语义执行?
  8. transport failure 是否被错误升级成 business/runtime terminal fact?
  9. terminal state 是否唯一且不可逆?
  10. Live 与 Replay 是否最终进入同一 validator / reducer 语义?

最终记忆

Command
= 请求现实改变

Event
= 现实已经改变

Replay
= 重建历史 projection

Resume
= 继续未完成 execution

ActiveTurn
= 当前正在发生什么

Event Log
= 发生过什么

Checkpoint
= 从哪里继续

Read Model
= 现在稳定展示什么

如果这八句话能稳定区分,L3 大部分“看起来相似”的 runtime 概念就不会再混在一起。

创建于 2026/8/26 更新于 2026/9/16
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本