快照能告诉你现在是什么状态,但不能告诉你为什么到这里。对 AI agent 来说,这个缺口会直接影响审计、回放和补偿。
如果你已经遇到“状态正确但过程说不清”,说明系统到了必须事件化的阶段。
为什么 runtime 需要事件流
| 仅快照模式的问题 | 事件流能补什么 |
|---|---|
| 难以追踪状态迁移原因 | 每次迁移有因果事件 |
| 回放粒度粗 | 支持按事件重建路径 |
| 审计链断裂 | 事件可直接作为证据索引 |
event sourcing 不是替代快照,而是让快照有可验证来源。
事件模型的最小字段
建议每个事件至少包含:
- eventId
- runId
- nodeId
- eventType
- timestamp
- causationId
- correlationId
- payload
没有 causationId 和 correlationId,跨系统事件很难拼成完整因果链。
事件类型建议
| 类型 | 示例 |
|---|---|
| state transition | NODE_STARTED, NODE_SUCCEEDED |
| decision | POLICY_BLOCKED, APPROVAL_GRANTED |
| side effect | TOOL_CALLED, EFFECT_COMMITTED |
| recovery | RETRY_SCHEDULED, COMPENSATION_DONE |
把“决策事件”和“状态事件”分开,是后续审计和调优的关键。
推荐架构:事件流 + 快照 + ledger
- 事件流:append-only,保存事实。
- 快照:加速读取,降低恢复成本。
- ledger:跨系统对账索引。
三者分工清晰,才不会出现“日志像账本、账本像缓存”。
失败案例:只存终态导致争议无法复盘
某平台仅保留 run_status=completed。客户质疑中间审批被跳过,团队只能给出“最终通过”的状态证明,无法给出审批链证据。
修复后他们做了两件事:
- 审批节点全部事件化(创建、分配、通过、拒绝、超时)。
- 审计页面按 runId 聚合事件时间线。
之后争议处理效率显著提升。
存储与保留策略
事件化常见担忧是“存储爆炸”。建议:
- 冷热分层:近 30 天热存,历史冷存。
- 关键事件永续保留,低价值 debug 事件按策略清理。
- 对事件 payload 做脱敏分层存储。
这样可以在成本与可追溯性之间取得平衡。
指标建议
- 事件丢失率
- 快照重建一致率
- 审计查询成功率
- 回放成功率(基于事件链)
快照重建一致率下降,通常意味着事件定义或写入顺序已退化。
一个事件长什么样
{
"eventId": "evt_001",
"runId": "run_789",
"nodeId": "risk_review.v2",
"eventType": "POLICY_EVALUATED",
"timestamp": "2026-05-30T10:15:22Z",
"causationId": "evt_000",
"correlationId": "customer_case_123",
"payload": {
"policyVersion": "policy_2026_05",
"decision": "requires_human_approval",
"reasons": ["external_commitment_detected"]
}
}
这类事件不只是日志。它是后续审批、回放和客户解释的共同事实来源。
Projection:不要让业务页面直接扫事件流
事件流适合写入事实,但不适合所有查询。建议用 projection 生成读模型:
| Projection | 用途 |
|---|---|
| run_status_view | 展示当前任务状态 |
| audit_timeline_view | 审计时间线 |
| billing_summary_view | 成本归集 |
| node_health_view | 节点健康分析 |
这样既保留事件流的可追溯性,又避免页面查询变慢。
从快照系统迁移到事件系统
不要一次性推翻现有状态表。更稳的路径是:
- 先为关键状态变化旁路写事件。
- 用事件重建一个只读 projection。
- 对比 projection 与原状态表一致性。
- 一致性稳定后,再让新功能依赖事件流。
这条路线能避免把迁移本身变成新的可靠性事故。
Checklist
- 关键状态迁移都有事件定义
- 事件包含 causationId 与 correlationId
- 快照可由事件重建并一致校验
- 事件保留策略符合合规要求
- replay 与审计基于事件链而非猜测
延伸阅读:


