为什么需要 Harness
Agent 循环最怕的不是写不出来,而是悄悄写坏。
文件被外部进程改了,agent 还在基于旧快照做决定;一次失败的编辑留下半截内容,agent 却以为成功了;两条命令写同一个文件,谁覆盖了谁没人知道;任务结束前最后一轮修改绕过了证据检查,agent 说"完成"但其实没完成。
这些问题在 demo 里不出现,在长跑里一定会出现。Harness 2.0 就是为这些场景装的一层强制链路。
一条强制入口
第一条规则简单粗暴:生产入口必须经过 AgentHarness。
AgentHarness → LegacyLoopAdapter → agentLoop
直接调用 agentLoop() 被禁止。规则写在文档里,也写在代码的审查路径上——新入口如果不走这条链,review 过不去。为什么强制?因为"记得走"在代码库里不管用。入口多了,总有一个人图方便直接调底层函数,然后绕过整层保护。强制入口让保护层不可能被绕开。
文件状态机:FreshnessGate
文件不是静态的。读的时候是新的,写到一半可能被外部进程改掉。FreshnessGate 把文件状态建模成显式的状态机,写操作 preflight 检查五类目标:
| 状态 | 含义 | 写操作行为 |
|---|---|---|
| fresh | 读到的内容和磁盘一致 | 放行 |
| stale | 磁盘内容在读取后发生变化 | fail-close |
| partial | 读的时候文件不完整 | fail-close |
| truncated / deleted | 读的时候已经不完整或不存在 | fail-close |
| unread | 从未读过就直接写 | fail-close |
写工具(write_file / edit_file / multi_edit / edit_fim)全部走这个前置。写成功之后记录 agent 自己写的基线,同一轮会话里再读就是"已知内容"。
关键的细节是快照绑定:管理写操作携带审批时的内容/哈希快照,提交前重新绑定。磁盘内容和快照不一致 → 这次写拒绝执行。外部进程改过文件 → agent 的写被拦 → agent 必须重新读。这个循环保证 agent 永远基于最新事实做决定。
FreshnessGate 和 HookSystem 的分工也在这里定死:hook 用 warn 模式观察和记录,拦截归 FreshnessGate。观察和拦截分开——观察者不该有执法权,执法者不需要讲故事。
补丁事务与证据链:完成是一个论证
"完成"不是一个字面动作,是一条证据链。CompletionOrchestrator 负责最后裁决,它检查四件事:
- 最终轮真实性检查——完成文本声称做了的事,必须在写入/变更文件的证据里找得到。声称和证据矛盾 → fail-close,不给过。
- 验证器身份固定——只有固定白名单里的内置验证器能发出 verification,运行时解析它们的核心结果并覆写所有权威戳。不能随便换个"验证器"来背书。
- 提交历史指纹——每次成功的 PatchTransaction 提交都会更新运行时持有的历史指纹。任何被观察到的写操作都必须匹配精确的提交历史快照,包括绕过 PatchTransaction 的 shell/shadow 写。
- 代次失效——证据有效性分代:写代次(L2,回滚时推进)和已提交事务证据状态(L3)。
rollback_transaction和提交失败自动回滚会同时推进这两代——撤销一件事,就等于撤销它的一切证据。回滚前的旧证据永远无法满足完成门。
还有一个很妙的机制叫命令级文件覆盖(command-to-file coverage):shell / run_process 写操作会记录精确的文件路径,只有 run_targeted_verification 覆盖了这些路径的验证才算数。完成门只有在每个未管理写都被覆盖时才放松绑定(避免死锁),L1/L2 保持硬约束。换句话说:你可以不走补丁事务,但你得为你的每个脚印负责。
HookSystem 2.0
HookSystem 从"攒警告"升级成完整的事件系统:
- 警告累积而不是直接失败——运行时 bootstrap 显式选择 warn 模式,因为真正的 enforcement 在 FreshnessGate
- Pre/Post 链式替换:一个 hook 的输出可以替换后续 hook 的输入
- 处理器异常 fail-closed:hook 自己崩了,默认挡住而不是放行
- 生命周期事件全覆盖
对外保持 strict 兼容语义(老调用方行为不变),对内 canonical runtime 用 warn/observe 模式——就是上面说的观察与执法分离。
观测:事件分类学
一次 agent 运行会产生海量事件。PR-10.1 把事件分类收敛成有限 9 类:lifecycle / model / tool / gate / verification / evidence / workflow / error / internal。每类有 canonical 事件表,未知类型可发现。telemetry 从"日志串"变成"分类学"——你能说出每一次 gate 判定、每一次证据失效的准确类型,而不是在一堆日志里 grep 关键词。
orcana doctor 把诊断做成命令:版本、运行时、配置、模型/认证、并发 provider 探测、沙箱矩阵、MCP 状态、路径,--json 输出给 CI。
验证基线
Harness 不是"写完就完"的:70 个确定性回放 fixture 是 CI core gate(每次提交都跑);H12 是 12 个脚本化 E2E 场景(rubric + floors + 历史),全部通过;mini benchmark 持久基线(pass@1 100%,false done rate 0%)带回归门——完成门一旦退化,benchmark 直接红。
验收口径
H0–H12 加 R1 全部完成,Harness 线冻结(封版)。验收标准一句话:代码、测试、运行时接线三者同时在场才算 Done。每个 PR 有明确的场景断言。
这条线冻结后,新的演进在 Graph Runtime(typed execution graph)上进行——Harness 的每一层保护,都会成为 graph 节点的执行边界。