实践部分 3. 项目宪法:首次规则公投
状态:建议。 将 immutable_principles 与 mutable_rules 分离,并显式标注 ttl、max_scope、rollback_condition,是一种稳健的实践。带有确定性平局规则(tie-breaker)的智能体自动公投尚处于前沿:其具体形式取决于团队和模型。
对于教学完成度来说,手动填写 constitution.md 并检查可变规则是否包含 ttl、max_scope 和 rollback_condition 已经足够。自动公投和外部仲裁属于完整的 production 轨道。
这里的「项目宪法」是一个经过版本管理的不变量、可变规则和修订批准程序的集合。该集合在任何危险动作执行前都会被检查。
一个词,两个不同的文件
在第一卷中,项目宪法分布在三个文件中:mission.md(我们为什么做这件事)、tech-stack.md(我们用什么技术)、roadmap.md(按什么顺序做什么)。该契约描述的是产品。
在第二卷中,同一个词下出现了第四个文件 —— constitution.md。它描述的不是产品,而是安全自动化的边界:禁止智能体执行哪些动作、哪些动作在受限条件下被允许、谁以及如何更改规则。这是一层叠加,而不是替代。第一卷的文件保留在原位,constitution.md 添加在旁,并在任何危险动作执行前被读取。
如果想要一行话划清边界:mission/tech-stack/roadmap 回答「我们在构建什么」,constitution.md 回答「即使很想做,智能体也不能对它做什么」。
在教学最低要求中,一个小小的 constitution.md,包含两条不可变禁令与一条可变规则就足够了。智能体公投、外部仲裁和剧本(playbook)的自动核查仅对完整轨道是必要的。本章以 第一卷第 6 部分(mission.md、tech-stack.md、roadmap.md 的来源)和 第 18 部分(SDD 流程安全性的概念来源)为依托。
阅读前
- 第一卷的依托:第 6 部分引入
mission.md、tech-stack.md、roadmap.md;第 18 部分解释 SDD 的安全性。
- 本地教学案例:
node_not_ready,因为它能清晰展现「允许的轻量动作」与「危险操作禁令」之间的差别。 - 留给
capstone/的痕迹:两条immutable_principles、一条mutable_rule,以及针对high_memory_usage的简短governance_protocol。 - 第一遍阅读的主要术语:
immutable_principles和mutable_rules。第三层governance_protocol是参考性的,只有在团队引入投票规则时才需要。 - 暂时可以略过:智能体公投、外部仲裁和剧本的自动核查。
单独设置 production 层的理由很简单:node_not_ready 或 autoscale_200pct 这样的场景没有时间在聊天中讨论。危险动作必须通过形式化的关卡,或者被直接阻止。关于这个关卡的安全性,我们将在依托 第 18 部分. SDD 安全性 中讨论。与已有代码交互的联系在 第 13 部分. 支持现有项目 中。
本章路线图
本章篇幅较长,无需线性阅读。行进地图如下:
- 最低教学场景。 手动填写
constitution.md:两条 immutable 禁令、一条带有六个字段的 mutable 规则。这就是通过本章所需的一切。 - 核心思想。 解释为什么最低要求长这样。
- 通过 Qwen Code 进行访谈。 一种加速填写
constitution.md的方法。第一遍可以跳过 —— 手动填写同样有效。 - 完整轨道。
governance_protocol、change_log、decision_hash、公投。第一遍阅读时非必须:只有在团队出现自动关卡时它们才变得必要。
如果你是第一次阅读,请在「核心思想」一节后停下,去做练习。完整轨道等你在 capstone/constitution.md 中用最简片段收尾之后再回来读。
目标
到本节结束时,你将为自动修复(auto-remediation)开发出一份可工作的 constitution.md。不变量与可变规范将变得清晰,通过智能体公投演进,并在修订谱系中完全可追溯。
这在实践中的意义。智能体不再依据零散的提示行事。它会将每项决策与形式化的项目契约比对。这样的契约有助于加速对重复性事件的响应,但不会让速度成为自动绕过备份、审计、爆炸半径(blast radius)限制和安全回滚条件的权利。
最低教学场景
教学案例
对于 node_not_ready,需要允许 triage 阶段的轻量级智能体重启,但要禁止针对 production 数据库、备份和审计的危险操作。目标是得到一份小巧的 constitution.md,在每次 production 动作前都可以人眼检查。
准备
book2/examples/templates/proposal.md—— 修订提案的格式。- 一份不能被自动执行的危险动作清单。
- 一个可重复出现的 unknown 模式,可能演变为可变规则。
第一次阅读可以使用这样的起始集合:
dangerous_actions:
- delete_namespace
- restart_database
- bypass_audit_trace
candidate_mutable_rule:
incident_type: node_not_ready
pipeline_phase: triage
permitted_actions: ["soft_restart_agent"]
max_scope: "single_node"
ttl: "30d"
rollback_condition: "repeat_incidents_same_node>=2 or Safety veto"
如果你正在把本章适配到自己的项目,请替换动作的名称,但不要去掉 max_scope、ttl 和 rollback_condition。
步骤
- 将至少两条
immutable_principles写成禁令,而不是建议。 - 添加一条
mutable_rule,包含incident_type、pipeline_phase、permitted_actions、max_scope、ttl、rollback_condition。 - 描述
governance_protocol:角色、法定票数、Safety 的 veto、以及平局打破规则。 - 填写一份简短的
proposal.md:规则为何出现、谁投票、何时激活、如何回滚。 - 用一个问题检验文件:「即使会降低 MTTR,智能体也无法执行哪个动作?」*期望:答案在
immutable_principles中,而不在聊天里。* - 与 [
examples/spec-ci/](examples/spec-ci/README.md) 中的可运行示例做对比:思路相同 —— 动作执行前必须有一道可验证的关卡。
验收事实
每条可变规则都必须包含 ttl、max_scope 和 rollback_condition。只要缺少其中任一字段,该规则就不被纳入宪法,只能作为 proposal 草案保留。
可以使用与 capstone/ 中相同的可运行示例来检查文件:
cd book2/examples/constitution
python3 scripts/check.py \
--constitution specs/constitution.yaml \
--proposal proposals/valid_proposal.md
你将得到 verdict: PASS。然后换成 proposals/missing_evidence.md 和 proposals/conflict_with_immutable.md 再跑一遍 —— 你会得到 verdict: BLOCK 和相应的原因。这就是危险动作执行前的教学关卡。
迁移到 high_memory_usage
本章的本地案例是 node_not_ready,但要考核的是 high_memory_usage。迁移只需一条原则,而不是复制整条规则。
从 node_not_ready 中取什么 | 写到 capstone/constitution.md 中给 high_memory_usage 的内容 |
|---|---|
| immutable「没有连续两次 OK,不关闭事件」 | immutable「不关闭 high_memory_usage,除非确认 RSS 已连续两次回到 80% 以下」 |
mutable soft_restart_agent,ttl: 30d,max_scope: single_node | mutable restart_pod,ttl: 14d,max_scope: single_pod,rollback_condition: 5xx_increase OR memory_percent>=90% after 2 windows |
| 扩大爆炸半径时的 Safety-veto | 试图将 restart_pod 扩大到 namespace 时的 Safety-veto |
如果无法用一条原则表达出来,那么本地案例还没有提炼出可迁移的原则 —— 在进入 capstone/ 之前请回到本地案例。
如何进入 capstone/
将两条 immutable_principles、一条 mutable_rule 和一段简短的 governance_protocol 迁移到 capstone/constitution.md。如果你编写过 proposal.md,请在 capstone/README.md 中留下一行说明规则出现的原因。如果自动公投并未实际运行,请不要把完整的智能体公投迁移过去。
最简片段:
immutable_principles:
- "没有 audit_trace 不得执行 auto-remediation。"
- "没有确认过的备份不得触碰有状态工作负载。"
mutable_rules:
- incident_type: high_memory_usage
pipeline_phase: recovery
permitted_actions: ["restart_pod"]
max_scope: "single_pod"
ttl: "14d"
rollback_condition: "memory_percent>=90% after 2 windows or 5xx increases"
governance_protocol: "任何 max_scope 的扩展都需要单独的 proposal。"
可评审的痕迹
在教学包中,请将 constitution.md 与 proposal.md 或 change_log 记录一同保存。没有出处的修订不被视为 production SDD 的一部分。
核心思想
constitution.md 的第一层是 immutable_principles。所谓 immutable_principles(不可变原则),是指永远不能被自动关闭的规则。它们包括安全层级的禁令与义务:
- 没有最新备份,不得重启 production 数据库;
- 不得为了释放空间而删除备份;
- 没有确认过的稳定化,不得将事件转为
resolved; - 不得在安全关键命名空间执行绕过(bypass)。
请把这些规范表述为不变量,而不是建议。正是它们在压力时刻约束智能体,因为缩短 MTTR 的最短路径可能造成更大的级联故障。
第二层是 mutable_rules。这是具有精确作用半径的可变规范(与 immutable_principles 不同,这类规则可以通过正式程序被撤销或重写)。每条这样的规则至少需要包含六个字段。其含义如下:
incident_type—— 规则适用的事件类别;pipeline_phase—— 响应阶段(triage、recovery等);permitted_actions—— 被允许的动作列表;
max_scope—— 爆炸半径的限制(例如single_node);ttl(time to live,存活时间)—— 规则的有效期,到期后需要重新审视或续期;rollback_condition—— 自动回滚规则所触发的条件。
没有这些字段,修订就过于宽泛,开始与不变量争夺空间。请对比两种写法。
差:
- id: node_not_ready_soft_restart
incident_type: "NodeNotReady"
permitted_actions: ["soft_restart_agent"]
问题:缺少 ttl、max_scope、rollback_condition。规则无期限、无爆炸半径边界。半年后将无人再评审它。
好:
- id: node_not_ready_soft_restart
incident_type: "NodeNotReady"
pipeline_phase: "triage"
permitted_actions: ["soft_restart_agent"]
max_scope: "single_node"
ttl: "30d"
rollback_condition: "repeat_incidents_same_node>=2 or Safety veto"
例如,对 incident:NodeNotReady 在 triage 阶段允许轻量重启(soft restart),但对 incident:DBWriteLag 在 recovery 阶段禁止。同一动作在不同上下文下,对数据与可用性的风险不同。
第一遍的暂停点
如果你是第一次读到这儿,那么到这里你已经掌握了通过本章所需的一切:两条 immutable 禁令、一条带六个字段的 mutable 规则,以及它与第一卷 mission/tech-stack/roadmap 的区别。接下来是 完整轨道 —— governance_protocol、change_log、decision_hash、公投。这些机制在团队出现自动关卡、并围绕它配置三到四个角色时才会变得必要。对于考核的 capstone/constitution.md,它们不是必须的。
如果你的项目还没有任何自动化动作,请直接跳到「产物与就绪标准」一节,完整轨道等读完第 8 章后再读(那里的角色会再次出现在文件仲裁中)。
完整轨道:governance_protocol
让演进的触发器可观测:同一 pattern_id 出现三次及以上反复出现的未预测事件时,强制启动针对修订的公投。这一阈值将单次异常与稳定的规则缺口分开,避免在每次嘈杂告警后都修改宪法。
固定一个聚合窗口,例如 48 小时。在候选修订(即送交公投的那份 proposal.md)中包含:
- 输入事件;
- 当前的
unknown分类; - 预估风险;
- 预期效果;
- 撤销条件。
为什么这里要立刻引入多个角色,而第一卷中一个评审者就够。在 第一卷第 16 部分 中,一个评审者就够,因为评审的对象是 AgentClinic 教学项目中某个特性的代码。代价是返工特性。在 production 场景中,评审的对象不同:针对线上服务的自动危险动作。代价是数据丢失、爆炸半径扩大、P0 被悄然关闭。一个评审者不够,不是因为不够聪明,而是因为人很难同时在脑中维持不变量正确性、剧本适用性以及对 production 的风险。因此评审被拆分到多个角色上,每个角色负责一个狭窄的问题。在最简形式下,它们仍可以是同一智能体的三个模型,依次阅读同一文件 —— 但它们的问题不同。
请在 governance_protocol 中描述投票流程。包括智能体角色、票权、法定票数、召集时间,以及确定性的平局打破规则。最简组成是三个角色:
Verifier(验证者)—— 查找不变量违反;Implementor(实施者)—— 评估剧本中的适用性;Safety(安全)—— 检查爆炸半径、隐私与回滚条件。
通过条件设为两票、2 approve + no Safety-veto 的规则、触发后 15 分钟内召集,平局打破规则为 safety-first。在这样的打破规则下,Safety 的关键风险始终导致否决。
第四个角色 Coordinator(协调者)不在 governance_protocol 中投票。它出现在更高一层 —— 文件仲裁(第 8 部分),在那里负责 judgment.md 与回合顺序。在本章的宪法中,Coordinator 仅负责将投票结果记入 change_log。
将每次更新作为不可篡改的痕迹记录,而不是没有出处的修改。记录中应包含:
version与parent_version—— 当前与上一版宪法;reason—— 修订原因;votes—— 各角色的投票结果;
decision_hash—— 决策的密码学哈希,可被复算与校验;incident_context—— 触发该决策的事件;activation_time—— 修订生效的时间点;- 指向仓库内 diff 的链接。
这样的日志将修订史转化为证据链。如果之后出现「为什么智能体被允许执行该动作」的问题,答案将不再出现在团队聊天里,而出现在可复算的决策谱系中。
示例与应用
constitution.md 的基本结构可以如下:
immutable_principles:
- id: prod_db_no_restart_without_backup
rule: "Production database restart is forbidden unless a verified backup exists."
enforcement: "always_on"
- id: backups_are_protected_assets
rule: "Backup deletion, overwrite, or quarantine bypass cannot be executed automatically."
enforcement: "always_on"
mutable_rules:
- id: node_not_ready_soft_restart_triage
incident_type: "NodeNotReady"
pipeline_phase: "triage"
permitted_actions:
- "cordon_node"
- "soft_restart_agent"
max_scope: "single_node"
ttl: "30d"
rollback_condition: "increase in repeat incidents or Safety veto"
governance_protocol:
roles:
verifier:
vote_weight: 1
implementor:
vote_weight: 1
safety:
vote_weight: 1
veto: "critical_risk"
quorum: 2
pass_rule: "at_least_2_approve_and_no_safety_veto"
convene_after: "3_unknown_incidents_same_pattern"
convene_within: "15m"
tie_breaker: "safety_first_then_latest_matching_precedent"
change_log:
- version: "1.2.0"
parent_version: "1.1.0"
reason: "3 unknown NodeNotReady incidents in 48h"
votes:
verifier: "approve"
implementor: "approve"
safety: "abstain"
decision_hash: "sha256:..."
activation_time: "2026-05-17T12:00:00Z"
应将宪法接入剧本执行前的关卡检查,而不是执行之后。步骤顺序如下:
- 本地脚本检查
immutable_principles和mutable_rules; - Qwen Code 在 Plan Mode 下解释风险与缺口;
- 执行者获得动作授权。
/constitution 和 /tasks 并非 Qwen Code 的内建命令。如果需要它们,请将它们实现为项目的自定义命令(project custom commands)。
通过 Qwen Code 进行访谈
在手动编写 constitution.md 之前,请进行与 第一卷第 6 部分 相同的访谈。production 层在经典的 mission/tech-stack/roadmap 三件套之上增加两个回路,进入这两个回路必须有人类做出明确决定:智能体在未经确认时有权执行哪些动作、谁拥有 veto、triage 阶段允许的最大爆炸半径是多少。
请求:
/clear
我们正在扩展 AgentClinic-production 的宪法。
请阅读 @specs/mission.md、@specs/tech-stack.md、@specs/roadmap.md
以及 @specs/playbooks/node-not-ready.md。
请创建一份 @specs/constitution.md 草稿,包含三个章节:
- immutable_principles
- mutable_rules
- governance_protocol
在写入磁盘前,请向我提出正好三组问题:
1. 智能体在 production 中哪些动作始终被禁止(备份、审计、密钥)。
2. 哪些动作可以在带爆炸半径限制与 ttl 的条件下,授权给 triage 阶段。
3. 谁投票、谁拥有 veto、按什么阈值召集公投。
先不要写文件。收到我的回答后,请展示变更计划,并等待批准。
如果 Qwen Code 立刻开始写 YAML 而没有提问,请叫停它:
停下来。在访谈完成前不要写文件。
请先按 QWEN.md 的要求提出三组问题。
人类对访谈的好回答包含决定,而不是愿望:
不可变原则:
没有经过验证的备份,不得自动重启 production 数据库。
不得自动删除备份、审计与密钥 —— 即使在释放空间的压力下也不允许。
事件必须在连续两次 OK 之后才能转为 resolved。
可变规则:
在 triage 阶段对 NodeNotReady 允许 soft_restart_agent,max_scope=single_node,ttl=30d。
规则回滚条件 —— 同一节点上出现两次重复事件或 Safety 否决。
未经公投不得扩大 max_scope。
治理:
Verifier、Implementor、Safety 参与投票。每票权重为 1。
法定通过条件 —— 2 票 approve 且无 Safety veto。
公投召集条件 —— 48 小时内同一 pattern_id 出现 3 次 unknown 事件。
平局打破规则 —— safety_first。
然后请 Qwen 检查自己的草稿:
检查 @specs/constitution.md。
找出被写成「建议」而非禁令的 immutable 规则。
找出缺少 ttl、max_scope 或 rollback_condition 的 mutable 规则。
找出缺少平局打破规则(tie-breaker)的 governance_protocol。
不要修改文件,返回不符合项列表。
这一步的常见错误:
- 不可变规则被写成「尽量避免」 —— 这是建议,不是禁令;
- 可变规则缺少
rollback_condition,事件发生时它就成了无期限规则; governance_protocol没有描述平局打破规则 —— 出现平票时系统会卡住;- 缺少
change_log,宪法的第一次修改就会丢失出处。
只有按这份清单走完之后,才能在项目中锁定草稿。在教学用的 book2/ 中,这一步不需要 git 命令:重要的是一份带规则出处的可读 constitution.md。
> 宪法关卡:可运行示例与项目接口。 可运行检查位于 [examples/constitution/](examples/constitution/README.md) —— 它读取 YAML 格式的宪法和带 YAML frontmatter 的 Markdown(修订提案),返回 verdict: PASS|BLOCK 与阻止原因列表。在考核中用它就够了。在真实项目中,这个关卡还要补两步:Qwen Code Plan Mode 下的请求,以及剧本 max_scope 与规则 max_scope 的比对。三步作为一个整体执行。
# [runnable] — 用宪法检查提案
python3 book2/examples/constitution/scripts/check.py \
--constitution book2/examples/constitution/specs/constitution.yaml \
--proposal book2/examples/constitution/proposals/valid_proposal.md
# [project script] — 在你自己的宪法和剧本上做同样的检查
python3 scripts/constitution/check.py \
--constitution specs/constitution.yaml \
--proposal proposals/<your-proposal>.md
# [project script] — Qwen 在 Plan Mode 下做的额外关卡
qwen -p "检查 @specs/constitution.yaml 与 @specs/playbooks/node-not-ready.md。
模式:仅规划。请说明该修复计划是否违反 immutable_principles,
并指出需要哪些 mutable_rules 才能放行。" \
--approval-mode plan \
--output-format json \
> out/node-not-ready-constitution-review.json
flowchart TD A[事件] --> B[Plan Mode 与脚本对 immutable 的检查] --> C[可变规则检查] --> D[放行 / 阻止] --> E[动作执行] C --> F[Unknown 事件] --> G[Unknown 阈值达成] --> H[形成 proposal] --> I[公投] --> J[Commit] --> K[新宪法]
公投很适合在回放场景中演练。三个相同的 unknown 事件会生成一份 proposal.md,智能体投票,通过的修订连同决策哈希一起记入 change_log。
例如:临时规则 hotfix_ticketing_flood 可以允许 12 小时内的简化工单路由。前提是存在检查点(checkpoint),并保留对审计丢失的不可变禁令。修订通过后,请确认它没有变成无期限规则。确保 ttl、rollback_condition、decision_hash 与 activation_time 与变更记录一起在仓库中可见。
小结
constitution.md 为自动修复划定了边界:智能体可以适配什么、以及它没有权利为了局部优化而关闭什么。
这层边界中的角色分工如下:
- 不变量保护备份、审计、数据与关键区域;
- 可变层允许针对重复事件细调剧本;
- 智能体公投使规则变更可被验证;
- 修订谱系保存原因、票数、决策哈希与生效时刻。
接下来,这些规范将在 LLM 对决中得到检验:Verifier 对阵 Implementor。
产物与就绪标准
| 产物 | 就绪条件 |
|---|---|
specs/constitution.md | 不可变规则被表述为禁令而非建议;通过文件可以读出智能体即使为了改善 MTTR 也不能执行的动作 |
一条 mutable_rule | 填齐 incident_type、pipeline_phase、permitted_actions、max_scope、ttl、rollback_condition —— 全部六个字段 |
proposal.md 或规则的出处记录 | 修订有出现的理由,否则它不属于 SDD 包 |
完整轨道还会增加若干具有不同爆炸半径的修订、含 parent_version、票数与 decision_hash 的 change_log 记录,以及一个在剧本执行前检查宪法的项目级关卡。完整轨道可以视为就绪的条件是:关卡确实在执行前阻止了对不可变层的违反;公投具备法定票数、Safety 的 veto 与确定性的平局打破规则;已通过的修订可从事件上下文一路追溯到 diff。
练习
- 打开
book2/examples/templates/proposal.md,针对一个危险域(部署、迁移、事件、密钥)填写:至少两条immutable_principles(写成禁令)和一条mutable_rule,包含incident_type、pipeline_phase、permitted_actions、max_scope、ttl、rollback_condition。期望:每条可变规则都包含全部六个字段;教学场景的验收事实成立。 - 编写
governance_protocol:角色、法定票数、Safety 的 veto 与平局打破规则;自问「即使会降低 MTTR,智能体也无法执行哪个动作?」。*期望:答案在immutable_principles中,而不在聊天里。*
- 迁移到
capstone/constitution.md:两条immutable_principles、一条mutable_rule与一段简短的governance_protocol(格式参考本章教学场景中的片段);如果运行过proposal.md,请在capstone/README.md中添加一行说明规则出现的原因。期望:没有出处的修订不会进入 SDD 包。
复习题
- 项目的哪些规则应该是不可变的,哪些不应该?
- 为什么可变规则需要
ttl?一年后没有它会发生什么? - 当 Safety 行使 veto 时应该发生什么?为什么它不能被两个
approve绕过? - 48 小时内出现三次相同的
NodeNotReady,公投触发器已经激活,但此时正有一个活跃事件在处理。你会接受、推迟该修订,还是回滚触发器?