本节摘要:写成散文的指令是愿望;写成约束的指令是测试。工作台把每条规则变成 Agent 能在运行时检查、审查者能在事后核验的东西。一份典型的
AGENTS.md读起来像入职文档——叫 Agent「小心」「彻底测试」「不确定就问」。三天后,Agent 交付一个没测试的改动、写进禁用目录、从不提问,因为它从不知道线画在哪。指令在可操作(operational)时有力量,在愿景式(aspirational)时软弱。修法是把规则写成工作台能解释、审查者能打分的东西。本节把规则从短根路由里分进docs/agent-rules.md,给出五类覆盖大多数规则的分类(Startup 启动/Forbidden 禁用/Definition of done 完成定义/Uncertainty 不确定/Approval 审批),每规则带 slug、类别、一行描述、一个check字段指向rule_checker.py里的函数——加规则即加检查,检查器随工作台增长。规则机器可读也diff 友好(每规则一标题,改名可见,过期删除而非注释掉)。本节还讲透渐进披露(给地图而非百科全书:根路由<50 行只放指针,深度在话题文件里按需加载)与三层结构(路由/规则/话题文档),并给出可达性测试(任一规则最多两跳到)与新鲜度测试(路由短到审查者每 PR 重读)。最后给三条让规则集活过一季度的生产模式:写时打严重度标签(block/warn/info)、规则到期作为强制函数(Cloudflare 13 万次评审数据:带到期的稳定<30 条/仓,不带的长到 80+ 大多永不触发)、Markdown 为源 JSON 为缓存(同package.json/package-lock.json)。读完本节,你能把散文指令重写成五类可执行约束。
对应原课程:Phase 14 · Lesson 33 ·
instructions-as-executable-constraints(原英文phases/14-agent-engineering/33-instructions-as-executable-constraints/docs/en.md)。前置:第 32 节(最小工作台)。
阅读完本节,你应当能够:
一份典型的 AGENTS.md 读起来像入职文档。它叫 Agent「小心」「彻底测试」「不确定就问」。三天后,Agent 交付一个没有测试的改动、写进禁用目录、从不提问,因为它从不知道线画在哪。
指令在可操作时有力量,在愿景式时软弱。修法是把规则写成工作台能解释、审查者能打分的东西。
| 类别 | 规则回答的问题 | 例子 |
|---|---|---|
| 启动(Startup) | 工作开始前什么必须为真? | 「状态文件存在且新鲜」 |
| 禁用(Forbidden) | 什么绝不能发生? | 「不要编辑 scripts/release.sh」 |
| 完成定义(Definition of done) | 什么证明任务完成? | 「pytest 退出码 0 且验收行通过」 |
| 不确定(Uncertainty) | Agent 不确定时做什么? | 「开一条问题便签而非猜测」 |
| 审批(Approval) | 什么需要人审批? | 「任何新依赖、任何生产写」 |
一条塞不进这五类的规则,通常是想当两条。强制拆分。
每条规则有 slug、类别、一行描述、一个 check 字段指向 rule_checker.py 里的函数。加规则即加检查;检查器随工作台增长。
规则在一个 markdown 文件里每条一标题。改名在 diff 里可见。新规则坐在其类别顶部。过期规则被删除,而非注释掉——因为工作台是真相之源,不是团队上季度感受的聊天记录。
框架护栏(OpenAI Agents SDK guardrails、LangGraph interrupts)在运行时层强制规则。本节的规则集是人类可读、可审查的契约,那些护栏实现的就是它。你两者都要:运行时在一轮里抓违规,规则集证明运行时在做对的事。
AGENTS.md 一直长的原因是:每个事故加一条规则,没有事故删一条。一年后,文件两千行,Agent 读第一屏、注意力预算耗尽、只按被告知的一小部分行动。巨型指令文件失败的原因和四十页入职文档一样:读者略读一次,再不回到重要的部分。
修法不是更短的文件,而是分层的。根路由保持小到每次会话都能读,只放指针。深度活在话题文件里,Agent 只在任务触及时加载。给 Agent 一张地图,而非整本百科全书,让它走到需要的那页。
AGENTS.md # 路由,<50 行:这仓是什么、去哪看、5 条硬规则 docs/ agent-rules.md # 完整规则集(本节) architecture.md # 任务触及模块边界时加载 testing.md # 任务写或跑测试时加载 deploy.md # 只为发布工作加载,门控在审批规则后 feature_list.json # backlog(第 36 节)
| 层 | 活在 | 何时读 | 大小预算 |
|---|---|---|---|
| 路由 | AGENTS.md |
每会话,总是 | <~50 行 |
| 规则 | docs/agent-rules.md |
每会话,启动时 | 每类一屏 |
| 话题文档 | docs/<topic>.md |
只在任务触及该话题时 | 需要多深就多深 |
两个测试保持分层诚实。可达性测试:Agent 从路由到任一规则最多两跳,所以路由必须按路径链接每个话题文档,而非散文描述。新鲜度测试:路由短到审查者每 PR 重读,这是唯一能阻止它静默长回它取代的百科全书的东西。一个不再解析的指针,比缺失规则更糟,所以路由里的死链本身就是一条启动检查违规。
原课程 code/main.py 提供:
agent-rules.md 解析器,把规则加载进数据类。rule_checker.py 式检查函数,每个 check 引用一个。## RULE-FORBID-RELEASE [forbidden] block 不得编辑 scripts/release.sh。 check: no_edit_of("scripts/release.sh") ## RULE-DONE-TESTS [done] block pytest 必须退出 0。 check: shell_exits_zero("pytest") ## RULE-UNCERTAIN-NOTE [uncertainty] warn 不确定时开问题便签而非猜。 check: no_guess_without_note()
@dataclass class Rule: slug: str; category: str; severity: str desc: str; check: str def parse_rules(md_path): rules = [] for heading in parse_headings(md_path): slug, cat, sev = parse_heading_meta(heading) # RULE-X [cat] sev rules.append(Rule(slug, cat, sev, desc, check)) return rules
CHECKS = { "no_edit_of": lambda path: path not in run.touched_files, "shell_exits_zero": lambda cmd: shell(cmd).returncode == 0, "no_guess_without_note": lambda: not run.has_guess or run.has_question_note, } def check_run(run, rules): report = [] for r in rules: fn = CHECKS[r.check_name] passed = fn(*r.check_args) report.append({"slug": r.slug, "severity": r.severity, "passed": passed}) return report # 保存为 rule_report.json
运行 python3 code/main.py 会输出:解析后的规则集、运行轨迹、每规则通过/失败、一份存脚本旁的 rule_report.json。
三条把规则集分开:能撑一季度的 vs 一周就腐的。
写时打严重度标签。 每条规则带 severity:block、warn、info。检查器三者都报;运行时只在 block 上拒绝。多数团队早期高估严重度,然后在截止压力下悄悄削弱;写时打标签强制前置校准。配第 38 节验证门,它把任何对 block 规则的覆盖签进 overrides.jsonl 审计日志。
规则到期作为强制函数。 每条规则带 expires_at 日期(默认成文后 90 天)。检查器在一条未过期规则连续 60 天零违规时发警告;下次季度评审要么论证保留、要么削弱成 info、要么删除。Cloudflare 的生产 AI 代码评审数据(2026-04,30 天跨 5169 仓 131246 次评审运行)显示:带显式到期的规则集稳定在每仓 <30 条;不带的涨到 80+,大多永不触发。
Markdown 为源,JSON 为缓存。 agent-rules.md 是成文文件;agent-rules.lock.json 是检查器热路径读的缓存。锁由 pre-commit 钩子重生。Markdown diff 可审查;JSON 解析不进每一轮。同 package.json/package-lock.json、Cargo.toml/Cargo.lock 的形状。
💡 设计要点:把指令从散文变成约束,本质是把「应该」变成「必须,否则验证门拦住」。这与第 26 节「每步验证门」、第 38 节验证门一脉相承——规则集是人类可读的契约,验证门是它的运行时强制。没有运行时强制的规则,只是更精致的愿望。
| 运行时 | 规则如何被用 |
|---|---|
| Claude Code、Codex、Cursor | 会话开始读规则,拒动作时引用;CI 重跑抓静默漂移 |
| OpenAI Agents SDK | 同样检查注册为输入/输出护栏;markdown 是文档面,SDK 是运行时面 |
| LangGraph | 在飞节点违规时触发 interrupt;处理器读规则、问人、续跑 |
规则集跨三者可移植,因为它只是 markdown 加函数名。
原课程 outputs/skill-rule-set-builder.md:采访项目主,把现有散文指令分进五类,产出带版本的 agent-rules.md 加检查器桩。
AGENTS.md,重写成五类规则。它有多少行是可操作的?多少是愿景式的?下一节,我们做实「状态」这一面——仓库记忆与持久状态:状态文件该承载哪些键、如何演化、如何在多会话与多 Agent 间保持一致,让仓库本身成为 Agent 的长期记忆。