实践部分 8. 争议变更的文件化仲裁:角色、裁决与先例
状态:前沿。 验证者/实现者/安全(Safety)三方文件化仲裁(投票表决),由协调者(Coordinator)记录协议,并写入 judgment.md(争议裁决记录)和 precedents.md(先例库)——这是一项已被实际使用的技术,但 Qwen Code 中并未内置。兼容性与限制详见 appendix-b-qwen-code-compatibility.md。
对于教学演练,只需从可运行示例中获取 judgment.md 并理解验证者接受哪些证据即可。角色轮换、外部协调者与模型矩阵属于完整的生产级轨道。
与第 4 章的边界在于:LLM 决斗回答「是否找到了最小反例以及如何消除它」。文件化仲裁回答的是另一个问题:「由角色团队通过的正式裁决是什么,接受了哪些证据,以及将为未来争议留下怎样的先例」。
文件化仲裁本身并不搜寻所有缺陷。它将其他机制的结果作为证据加以接受:决斗中的反例、Spec CI 报告、anti-Goodhart 不变量、就绪门(readiness gate)检查点或变异记录(mutant 记录)。如果文件中没有证据,协调者就不应将代理的印象转化为裁决。
来自 第一卷第 16 部分 的团队评审是基本模式:人类评审者依据证据包审阅拉取请求(pull request)。这里将同一模式提升了一个层级。不再是单个人在工作,而是角色在工作:验证者(Verifier)、实现者(Implementor)与 Safety 进行投票;协调者(Coordinator)维护协议但不投票。不再是 PR 中的评论,而是两个文件:judgment.md(争议裁决日志)和 precedents.md(反复出现的争议库)。支撑基础则保持不变:裁决与 第 9 部分 中 validation.md 的事实进行核对,结论对路线图的修订方式也与 第 10 部分 中重新规划的方式相同。
阅读前
- 来自第一卷的支撑:第 16 部分阐述了团队评审,第 10 部分展示了在事实之后的重新规划。
- 本地教学案例:
autoscale_200pct,因为它已经具备决斗、不变量与judgment.md。 capstone/留痕要求:针对high_memory_usage给出一条APPROVE、DENY或DEFERRED裁决,并附evidence_ref。- 首轮阅读的核心术语:文件化仲裁与
judgment.md。角色(验证者/实现者/Safety + 协调者-记录员)已在第 3 部分中介绍——这里为它们补上程序性内容。 - 可暂时搁置的内容:模型矩阵、外部协调者以及常设的
precedents.md库。
目标
你将学会开展争议变更的文件化仲裁。这是一种由多个角色对同一变更进行的集体审议,其结果记录在文件中而非聊天中。目标是设计一种模式,使同一规范(specification)在角色、模型与严格度模式轮换时依然可复现地接受检验。
角色轮换指通过不同的实现者/验证者配对(每个位置上使用本地或更强大的代理)来运行同一规范。其必要性在于确保裁决不依赖于具体模型。
实际收益很简单:争议不再停留为聊天中的意见交换,而是演化为一条证据链。协调者主导流程。实现者提出变更。验证者按形式标准接受或拒绝。Safety 在 critical_risk 下拥有一票否决权。最终结论记录到项目制品(artifact)中。
这种方法延续了 SDD 的逻辑:规范仍然是系统行为的真理之源,而非开发者意图的可选描述(GitHub Spec Kit)。
该机制的工程名称是「多角色争议变更文件化仲裁协议」。tribunal 这个名字仅作为可运行示例目录的技术标签,而非 Qwen Code 的独立产品。
最小教学场景
教学案例
仍然使用 autoscale_200pct,但现在需要的不仅是反例,而是一份正式的协议:决斗、anti-Goodhart 不变量与最终的 judgment.md。
准备工作
book2/examples/tribunal/specs/autoscale_spec.yaml。book2/examples/tribunal/cases/。book2/examples/tribunal/metrics/validation_metrics.json。- 脚本
run_duel.py、check_invariants.py、write_judgment.py。
步骤
cd book2/examples/tribunal。预期:你已进入可运行示例所在目录。python3 scripts/run_duel.py --spec specs/autoscale_spec.yaml --cases cases/ --out out/duel.json。预期:决斗已记录各案例的裁决。python3 scripts/check_invariants.py --metrics metrics/validation_metrics.json --out out/invariants.json。预期:anti-Goodhart 不变量已与决斗分开检验。python3 scripts/write_judgment.py --duel-out out/duel.json --invariants-out out/invariants.json --to out/judgment.md。预期:出现最终的 markdown 协议文件。- 打开
out/judgment.md并将一个可复现的冲突迁移到precedents.md:条件、证据、裁决、适用范围。
检验事实
judgment.md 不仅包含 PASS/FAIL,还包含依据:检验了哪个案例、哪个不变量触发、实现者在再次出现争议时应做什么。缺少这些,文件化仲裁就仍只是第 4 章中的决斗。
如何进入 capstone/
将一条裁决、其理由、evidence_ref 与下一步可验证的操作迁移到 capstone/judgment.md。如果该冲突可复现,再添加一条简短的先例记录。如果 out/duel.json 可通过可运行示例的命令复现,则无需全量迁移。
最小片段:
verdict: DEFERRED
reason: "readiness passes by score, but stateful blocker has no backup evidence"
evidence_ref: "fixtures/readiness_block_stateful.json"
next_step: "add backup_verified evidence or keep remediation manual"
可评审留痕
如果 judgment.md 或其中的摘录已成为教学证据包的一部分,则予以保存。如果本地 out/duel.json 与 out/invariants.json 可通过命令复现,则可不纳入代码仓库。
核心思想
文件化仲裁的阶段契约始于协调者角色。协调者开启会话,确定各轮顺序,维护争议队列,并负责将官方协议记录到 judgment.md。judgment.md 本身是会话裁决的日志:完成了哪一轮、审阅了哪个差异(diff)、接受了哪些证据。
最小循环如下:协调者接收初始规范,将其拆分到可检验的文件中,为关键风险指派实现者、验证者与 Safety 角色,并要求在未记录上一阶段结果时不得进入下一阶段。完整的角色章程(含投票权重 vote_weight、法定人数与一票否决条件)见 第 3 部分。此处我们关心的是:这些角色如何围绕一项具体的争议变更展开工作。
这在实践中的含义。来看聊天中的一条简短裁决:
不佳示例: >「验证者驳回了实现者的提议。」
问题在于:没有依据,也没有证据引用(evidence_ref),争议不可复现。下一位评审者既无法反驳也无法支持该裁决。
良好示例: > verdict=DENY, reason=violates_invariant:silent_p0, evidence_ref=tests/regression_001.json, next_step=实现者在自动升级前添加 severity 检查
这里的 evidence_ref 与 第 1 部分 中使用的证据标记相同:指向文件中具体位置的引用,而非复述。silent_p0 是不变量「任何 P0 事件在升级之前不得关闭」。如果验证者返回 DENY,请勿在聊天中手动结案。必须要求相关方提供形式化依据:指向具体需求、钩子(hook)日志、模式违反或未经证实的场景。这样 judgment.md 就从「谁赢了」的报告演变为程序状态的日志。
在 Qwen Code 中,这种仲裁并非单一的内置命令。最小实现可由 /review、无头(headless)调用 qwen -p、项目脚本,以及必要时自定义命令组装而成。所有裁决都应保存到文件中,以便其他工程师无需聊天历史即可复现争议。角色与 CLI 内置能力的详细对应关系见 [appendix-b-qwen-code-compatibility.md](appendix-b-qwen-code-compatibility.md)。
> [runnable] —— 文件化仲裁的可运行示例位于 [examples/tribunal/](examples/tribunal/)(参见 [examples/tribunal/README.md](examples/tribunal/README.md))。实际运行由三个脚本组成:
run_duel.py写入决斗的 JSON 结果;check_invariants.py校验 anti-Goodhart 阈值(该规则禁止以牺牲其他指标为代价来改善单一指标);write_judgment.py由前两个输出汇总生成最终的judgment.md。
从 book2/examples/tribunal 目录运行。
cd book2/examples/tribunal
python3 scripts/run_duel.py \
--spec specs/autoscale_spec.yaml \
--cases cases/ \
--out out/duel.json
python3 scripts/check_invariants.py \
--metrics metrics/validation_metrics.json \
--out out/invariants.json
python3 scripts/write_judgment.py \
--duel-out out/duel.json \
--invariants-out out/invariants.json \
--to out/judgment.md
run_duel.py 读取规范并对 cases/ 中的反例进行运行。check_invariants.py 将实际指标与阈值比对。write_judgment.py 汇总生成最终的 markdown 协议。这里没有外部的「协调者」或「验证者」作为独立进程存在。在生产环境中,该仲裁由内置命令 /review、带不同角色提示的无头 qwen -p 调用与项目脚本组装而成——每个组件各自生成磁盘上的制品。
对同一规范在不同实现者/验证者配置之间进行 A/B 对比,可以揭示裁决对某一层级(tier)代理的依赖程度。这里的模型层级指:低成本的本地模型(local-coder)或强大的云端模型(frontier-reviewer)。同一份 rate_limit_spec.md 在多组配对下运行:
- C1:低成本的本地实现者 对 强大的验证者;
- C2:强大的实现者 对 本地验证者;
- C3:对称的本地配对;
- C4:对称的高成本配对。
若 C1 与 C4 给出 PASS,而 C2 稳定地返回 FAIL,这并不是立即更换模型的信号。首先应检查证据框架:较弱层级的验证者可能未识别出请求频率限制、冷却窗口(cooldown)与队列安全状态之间的隐含关联。
该测试的价值正在于:它保持规范不变,仅改变角色配置。
教学版的可运行类比位于 [examples/tribunal/matrix/](examples/tribunal/matrix/README.md):决斗中的同一 judge() 会在 matrix/tiers.json 所述的四组层级配对下运行。配置模拟了证据形式之间的差异——local-coder 输出简短的 diagnostic_code(minimal_form),frontier-reviewer 输出 evidence_by_invariant 结构(extended_form),而较弱的验证者只能识别 minimal_form。因此 C2 配对(强大实现者 + 弱验证者)稳定地失败,其他三组通过——这正是教学信号 signal: tier_dependent_spec。
cd book2/examples/tribunal
python3 scripts/matrix.py \
--spec specs/autoscale_spec.yaml \
--cases cases/ \
--tiers matrix/tiers.json \
--out out/matrix.json
#### 检验:summary.signal != "tier_dependent_spec" —— 这时应在 validation.md 中解释分歧,或记入 precedents.md
在生产项目中,这一输出背后是 scripts/tribunal_matrix.py——它将 judge() 替换为带不同角色提示的真实 qwen -p 调用,但制品接口(summary.signal、pairs[*].verdict、pairs[*].cases[*].reasons)保持不变。如果教学版矩阵出现分歧,退出码为 1,并在 smoke_all.sh 中以 expect_fail 包装:分歧在此是有意的教学信号,而非故障。
为验证者设计的提示不应是泛泛的「检查一下解决方案」,而应是严格的证据要求。共有三类:钩子(hook)日志、JSON Schema 的一致性,以及形式化的 Given/When/Then 场景。
PreToolUse 日志展示了执行前允许或阻止了哪些工具调用。PostToolUse 日志记录实际结果、退出码、差异(diff)的校验和以及证据中的事件引用。
JSON Schema 覆盖了一类错误:代理生成了看似合理的文本,却违反了数据契约。此类违反的示例包括:
- 缺少必填字段;
- 参数类型由 integer 变为 string;
- 限值被设置在允许范围之外。
Given/When/Then 场景补充了因果检查:在何种初始条件下该操作被允许、何种事件触发它、何种可观察结果应确认其安全性。
flowchart TD
COORD[协调者:写入需求]
IMPL[实现者:patch_plan 与 hooks]
PRE[PreToolUse:阻止危险操作]
POST[PostToolUse:证据与 hash]
VER["验证者:与 validation 核对,给出裁决"]
SAFETY["Safety:在 critical_risk 时一票否决"]
DISPUTE["争议:requirements/hooks/validation 中的 diff"]
COORD --> IMPL
IMPL --> PRE
PRE --> POST
POST --> VER
VER --> SAFETY
SAFETY --> DISPUTE
DISPUTE --> COORD冲突只能通过 requirements.md、hooks.md、validation.md 中的差异(diff)来解决。聊天对话中任何隐性的修改都不纳入证据基础。
若实现者认为驳回有误,不应以自由形式重写解释,而应追加一项可检验的修改:澄清需求、加强钩子或扩展验证场景。
协调者只有在差异既关联到初始规范、也关联到具体证据事件时,才会接受下一轮。否则,争议将退化为不可复现的私人历史。再次出现冲突时,将该决定迁移到 precedents.md——先例日志,其中为每个案例精确记录五个字段:
case_id—— 先例的稳定标识符;verdict—— 按仲裁规则得出的结论(APPROVE/DENY/DEFERRED);evidence_ref—— 支撑该裁决的差异、钩子日志、模式或场景的引用;applies_to—— 先例的适用范围(层级、严格度模式、领域);next_check—— 需要重新审视该先例的条件。
- case_id: PREC-021
verdict: DENY
evidence_ref: "tests/rate_limit_tenant_isolation.json"
applies_to: "rate-limit without tenant_id deduplication, all tiers, strict_guardrails_prompt"
next_check: "burst_window_sec rises above 60 or new tenant_id isolation evidence appears"
anti-Goodhart 规则保护文件化仲裁免于「一项指标改善而系统整体退化」的情形。MTTR(平均恢复时间,mean time to recovery)无法以误升级率、静默失败(silent failure)或回滚抖动(rollback-flapping)的增加为代价来为自身辩护。即使单轮显示快速 PASS,这一原则依然成立。
因此,应在 validation.md 中设置硬性停止条件:
false_escalation_rate <= 0.05;rollback_flapping < 3/小时;silent_p0_ratio == 0。
任一阈值被突破,无论时间收益如何,裁决都将转为 FAIL。这使得 Goodhart 防护从道德警示转化为可执行的仲裁规则。
示例与应用
示例:API 网关中自动限流(rate limiting)的规范要求在请求突发时临时限制特定租户(tenant),但不能阻塞整个服务,也不能将每次突发升级为 P0。
实现者提议的补丁:
- 在去重键中加入
tenant_id; - 引入
burst_window_sec=60的窗口; - 每次限流生效后向
evidence/rate_limit.ndjson写入事件。
验证者仅在满足三项证据时才接受:
- JSON Schema 要求存在
tenant_id、limit_reason、expires_at; PreToolUse禁止在未指明租户作用域的情况下修改全局限流;Given/When/Then显示某租户的突发不会降低相邻租户的配额。
如果上述任一证据缺失,验证者将返回 DENY,即使补丁在技术上看起来合理。
在 A/B 轮中,配置 Implementor=local-coder, Verifier=frontier-reviewer 可能通过。强大的验证者能识别出模式、钩子日志与场景之间的充分联系。
相反的配置 Implementor=frontier-reviewer, Verifier=local-coder 可能驳回相同方案。这通常发生在安全证明隐藏在实现者冗长的推理中、而未显式写入 validation.md 的情况。
这并不意味着一方代理「正确」而另一方「错误」。仲裁揭示的是:该需求在模型各层级之间尚不够可移植。修正应体现为差异(diff)——例如,新增场景 Given tenant A exceeds burst limit / When tenant B sends normal traffic / Then tenant B quota remains unchanged。
Scenario: 突发负载下的租户隔离
Given tenant A 发送 800 req/min
And tenant B 发送 40 req/min
When rate-limit hook 实施限流
Then tenant A 获得 60 秒的临时限流
And tenant B 保留基础配额
And evidence 包含 tenant_id、limit_reason 与 expires_at
针对 Goodhart 陷阱的压力测试通过一次独立的微分析进行。实现者接到将 MTTR 由 6 分钟降至 2 分钟的任务,并提出在首次告警事件时即激进地自动升级。
应促使验证者不仅检查速度,还要检查副作用:
- 误升级比例;
- 回滚抖动频率(rollback-flapping,即在短时间窗内反复回滚);
- 重复通知量;
- 是否存在冷却窗口(cooldown)。
若快速方案使 false_escalation_rate 超出允许阈值,协调者将在 judgment.md 中记录 FAIL(reason=metric corruption),并要求修改 validation.md,而非聊天中的表面解释。如此,仲裁得以学会区分真正的改进与以运营稳定性为代价的单一数字优化。
小结
文件化仲裁使争议解决可复现。协调者管理阶段与协议。实现者只修改受控制品。验证者要求钩子日志、JSON Schema 与 Given/When/Then。所有冲突均通过 requirements.md、hooks.md、validation.md 的差异来解决,必要时记入 precedents.md。
角色轮换将不同层级的代理转化为规范鲁棒性的检验工具。如果在切换实现者/验证者配对时裁决发生变化,应强化证据,而非依赖某一具体模型的权威。
anti-Goodhart 规则完成了闭环:它禁止以误升级、静默失败或回滚抖动为代价来换取 MTTR 的快速改善。接下来,这一仲裁闭环将进入分层路由的经济学以及角色之间的 token 分配。
以决策轨迹(Decision trace)替代隐式推理
仲裁并不需要模型完整的思维链。它需要可复现的决策协议:抽取了哪些事实、检验了哪些红旗、应用了哪项政策、得到了哪一裁决。因此,争议结论应被组织为分阶段的 decision_trace,而非「模型在想什么」式的自由文本。
最小结构:
case_id: "JDG-001"
facts:
- "readiness_block_audit.json 给出 score=22/25"
- "audit_trace_coverage=0.7"
checks:
- rule: "auto mode requires audit_trace_coverage=1.0"
status: "fail"
- rule: "score >= 23"
status: "fail"
policy_outcome: "deny_auto_mode"
verdict: "DENY"
evidence_ref: "fixtures/readiness_block_audit.json"
customer_safe_summary: "auto mode is blocked until full audit_trace"
internal_note: "fix Process evidence, then re-run readiness"
这样的轨迹可以在没有聊天历史的情况下交付给另一位验证者或 Safety 角色。如果裁决发生变化,团队比较 facts、checks 与 policy_outcome 字段,而非争论解释的风格。
制品与就绪标准
| 制品 | 就绪条件 |
|---|---|
judgment.md(或其摘录) | 裁决附有理由与 evidence_ref,指向差异、钩子日志、模式或 Given/When/Then,而非复述 |
decision_trace | 事实、检验、政策结果与最终裁决彼此独立 |
out/duel.json 与 out/invariants.json | 本地可复现;book2/examples/tribunal 中的可运行示例通过 smoke-pass |
precedents.md 记录 | 在冲突可复现时创建;否则跳过 |
完整轨道额外要求:在协调者协议下,投票角色(验证者/实现者/Safety)的多轮 judgment.md;针对同一不变规范、按层级配对得到的裁决矩阵;以及作为仲裁必需要件的 anti-Goodhart 不变量。当满足以下条件时,可视为就绪:层级配对间裁决的分歧能通过 validation.md 的差异得到解释;anti-Goodhart 阈值能阻止快速但有害的方案;反复出现的冲突已记入 precedents.md。
实践
cd book2/examples/tribunal && python3 scripts/run_duel.py --spec specs/autoscale_spec.yaml --cases cases --out out/duel.json && python3 scripts/check_invariants.py --metrics metrics/validation_metrics.json --out out/invariants.json && python3 scripts/write_judgment.py --duel-out out/duel.json --invariants-out out/invariants.json --to out/judgment.md—— *预期:out/judgment.md中包含指向具体案例的verdict与evidence_ref。*- 记录验证者有权接受的证据类型:差异、钩子日志、模式、Given/When/Then。*预期:
out/judgment.md中evidence_ref字段指向文件而非复述。*
- 将可复现的冲突按以下模板迁移到
capstone/precedents.md(最少字段):
- case_id: "PREC-001"
verdict: "DENY"
evidence_ref: "tests/regression_001.json"
applies_to: "auto-remediation without full audit_trace"
next_check: "repeat duel when manual_review_floor changes"
*预期:下一次类似争议通过引用 PREC-001 解决,而非再开一轮。*
检验问题
- 协调者与验证者、Safety 有何不同,为什么它不与他们一同投票?
- 为什么争议必须通过差异(diff)解决,而非通过来回通信?
- 在切换层级代理时,裁决的分歧说明了什么?
- 实现者与验证者连续三轮未能达成决议,事件队列持续增长。在将争议转交人工之前,你应记录哪一停止条件与哪一制品?