Appearance
Runtime 怎么做确定性验证:Unit、Contract、Fault Injection 与 Invariant Tests
前置:Agent Trace 如何标准化导出:OpenTelemetry 与 Export Pipeline
核心问题:哪些 Runtime 行为必须用 deterministic test 证明,Fault Injection 应打在什么 crash window 才能真正验证 invariant?
为什么真实模型反而会污染 Runtime Test
如果要验证“Tool timeout 后只重试一次,并正确记录两个 attempts”,真正被测的是 Runtime。测试若依赖真实模型、真实网络和随机输出,一旦失败就很难判断是哪一层出了问题。
因此 Runtime Test 应优先使用 Fake Clock、Scripted Model、Fake Tool/Store,把输入和故障位置固定下来。
Unit Test:证明状态迁移和 Policy Wiring
例如:
text
ScriptedModel → tool_call(search)
FakeTool attempt-1 → TIMEOUT
FakeTool attempt-2 → SUCCESS断言:
text
attempts == 2
same logical operation_id
retry budget consumed once
final node == SUCCEEDED测试不需要真实 Provider,因为这里验证的是自己的状态机和 retry wiring。
Contract Test:证明 Adapter 没破坏内部语义
Model / Tool / MCP Adapter 的 Contract Test 只关心它是否满足 Harness 的内部 contract,例如:字段规范化、error taxonomy、stream event、schema/capability、timeout/cancellation。
更换 SDK/Provider 后复用同一套 cases,就能判断变化是否被 Adapter 正确吸收,而不用重跑整个 Scheduler 的每个路径。
Fault Injection:把 Crash 打在两个事实之间
最有价值的故障点通常是:
text
remote side effect success
→ CRASH
→ local completion commit或:
text
READY state committed
→ CRASH
→ broker enqueue / ack只有主动在这些边界 kill worker,才能验证 idempotency、reconciliation、checkpoint 和 redelivery,而不是只证明 happy path 会跑。
🔍 源码对照:最值得故障注入的地方:Checkpoint 已写、Transcript 未 commit
完整源码与通用分析见 09-06 Checkpoint / Pause / Resume。这里不重复代码;这里关注 fault injection:故障点应插在 checkpoint 已提交与 transcript commit 之间,并断言恢复结果仍满足既定 invariant。
源码说明
该 reconstruction 本身维护 focused regression tests,而 turn-observation / auto-review-gate 又提供清晰可注入的状态和依赖边界。对我们真正有价值的不是复制测试文件,而是看到 Runtime correctness 可以通过 fake clock、fake controller、fake tool/event sink 做 deterministic assertions,不需要让真实 LLM 来判断 state machine 是否正确。
工程边界
仓库测试覆盖范围不等于完整产品质量证明;本文仍坚持 Testing 验证 software contract,Evaluation 才衡量 Agent decision quality。
Shadow Harness 对照
- FakeModel/FakeTool/FakeClock 是一等测试接口。
- 所有 state transition、approval gate、retry class 都有 table/property tests。
验证
不联网、不调用真实模型,完整跑通 tool success、timeout、approval wait、cancel、resume 五条 Runtime trajectory,并断言事件顺序。
Invariant / Property Test:检查“无论怎么组合都不能发生”的事
与固定 case 不同,Property Test 可以生成大量 DAG、事件和 retry 顺序,只断言不变量,例如:
text
terminal state 不回到 RUNNING
dependency 未满足不得启动
同一 fencing epoch 只能有一个合法 owner
同一 logical operation 不出现两个 committed success它尤其适合状态组合爆炸的 Runtime。
四类测试怎么分工
| 方法 | 最适合验证 |
|---|---|
| Unit | 单个 reducer/policy/调度决策 |
| Contract | Adapter 是否保持内部接口语义 |
| Fault Injection | crash/timeout/partial write 边界 |
| Invariant / Property | 大量组合下永远成立的规则 |
关联源码
自测
- 要测试“side effect 成功后、checkpoint 前 crash”,Fault Injection 应插在哪个精确边界,关键断言是什么?
- Unit、Contract、Invariant test 分别更适合验证局部函数、组件协议还是跨状态机不可破坏条件?