Appearance
恢复逻辑如何防回归:Replay / Resume Tests 与 Golden Trace
前置:Runtime 怎么做确定性验证:Unit、Contract、Fault Injection 与 Invariant Tests
核心问题:恢复逻辑改动后,怎样证明旧 Run 仍能正确 replay/resume,Golden Trace 应固定哪些结构、放过哪些噪声?
普通 happy-path test 测不到恢复系统最危险的地方
下面两条路径最终都可能返回“成功”:
text
A. start → tool → checkpoint → final
B. start → tool → crash → restart → resume → final如果只测 A,就没有证明 B 不会重复 Tool、副作用或丢失 pending approval。Durable Runtime 的关键能力必须以“销毁所有进程内对象后还能恢复”作为测试条件。
Resume Test 从 durable boundary 开始
一个典型测试流程是:
text
1. 用 ScriptedModel/Fake Tool 推进 Run
2. 在指定 checkpoint/crash window 停止
3. 序列化 StateStore / Checkpoint / Event Log
4. 丢弃 Runtime、Worker、coroutine 等内存对象
5. 创建全新的 Runtime 实例
6. 从持久化事实 resume
7. 断言 terminal state、attempt count、side-effect count例如 Tool 已成功并写入 execution record、checkpoint 尚未完成时 crash,恢复后应复用/对账已有 operation,而不是盲目再次调用。
这里要断言的是:
text
same logical operation_id
completed attempt not re-executed
pending wait restored
new attempt identity only when policy allows retry
final state equals expected durable semantics故障注入要打在“边界之间”
最有价值的 crash points 往往不是函数入口,而是两种事实之间:
text
external side effect success
→ CRASH
→ local completion/checkpoint
READY state commit
→ CRASH
→ broker enqueue
approval decision commit
→ CRASH
→ worker wake-up测试 harness 应允许在这些边界注入故障,否则“支持 resume”很容易只覆盖无副作用的简单场景。
🔍 源码对照:真实持久化链天然暴露 Crash Window
完整源码与通用分析见 09-06 Checkpoint / Pause / Resume。这里不重复代码;这里关注 Golden Trace:crash 前后必须能看出哪一侧事实已经提交,并验证 resume 不会重复已经提交的副作用。
源码说明
Transcript mirror、crash guard、trace/test 这些边界组合起来,可以用来设计 Golden Trace:它不是保存最终 answer,而是保存关键 Runtime trajectory——Turn、ToolCall、state transition、checkpoint/approval/retry 等。恢复逻辑升级后,比较 trajectory 才能发现“结果碰巧一样、过程语义已经错了”的回归。
工程边界
Golden Trace 不应锁死时间戳、随机 id、token delta 等噪声字段;也不能用 transcript snapshot 替代 side-effect correctness test。
Shadow Harness 对照
- 建立 canonical event normalization,去掉不稳定字段再 snapshot。
- Replay/Resume test 同时断言 external call count,防止重复副作用。
验证
保存一个“批准后执行一次写操作”的 golden trace;修改 resume 代码后重跑,若出现第二次 write attempt 即使最终文本相同也必须失败。
Golden Trace 固定结构,不固定噪声
一条可用 Golden Trace 可以记录:
text
run.started
model.requested
model.tool_call(search)
tool.started
tool.succeeded
run.waiting_approval
approval.accepted
tool.started
tool.succeeded
run.completed升级 Runtime 后比较事件类型、父子关系、状态顺序、operation identity 和关键属性。以下字段通常需要 normalize 或仅做范围断言:
text
timestamp
span_id / request_id
latency exact value
random model text
token usage exact value
host/worker identity如果 snapshot 把整段模型文本、绝对时间和随机 UUID 全固定,任何正常变化都会让测试变红,最后团队只能无脑更新 snapshot。
Golden Trace 和 Eval Snapshot 不同
Golden Trace 主要验证软件轨迹结构:有没有少 span、状态顺序是否改变、Tool 是否重复、错误属性是否丢失。
模型输出质量可能本来就有随机性,需要 Dataset/Grader/Repeated Trials。不要因为 Golden Trace 没逐字匹配模型回答,就把正常语义波动当 Runtime regression。
反过来,即使最终回答质量没下降,如果升级后每个 Tool 都执行两次,E2E Eval 可能仍然通过,但 Golden Trace 应立即暴露结构回归。
最小回归集应该覆盖哪些恢复状态
至少保留这些 durable fixtures:
text
checkpoint after completed read-only tool
checkpoint around external side effect
WAITING_APPROVAL
WAITING_TIMER / external event
partial DAG completion
cancelled run
old state/schema version每个 fixture 都要能被“新进程”加载。这样恢复代码、State schema、scheduler 或 retry policy 一改,就能及时发现兼容性破坏。
验收标准
一套恢复回归测试真正有用,应能回答:
- crash 后有没有重复副作用;
- 已完成 Node 是否被重新执行;
- pending approval/event 是否仍可继续;
- old checkpoint 是否按 migration policy 处理;
- Trace 是否仍能解释前后两段执行属于同一个 Run;
- 新版本引入的 span/event 是否是有意变化。
Golden Trace 的价值不是“文件没变”,而是把长期运行系统最关键的不变量变成可比较证据。
关联源码
自测
- 修改 Resume 逻辑后,怎样用同一 Golden Trace 检查“恢复点没漂移、已完成副作用没重放”?
- 为什么 Golden Trace 不应只比较最终文本答案,还要比较关键 execution events/state transitions?