用 loop-constraints 为 Grok 循环工程搭建硬性护栏:约束文件、技能模板与机械强制执行
用 loop-constraints 为 Grok 循环工程搭建硬性护栏:约束文件、技能模板与机械强制执行
loop-engineering 仓库为 AI 编程 Agent 的循环工程(loop engineering)提供了可落地的模式、starter 与 CLI 工具链。本篇文章以 examples/grok/constraints.md 为骨架,系统讲解 Grok Build TUI 中 loop-constraints 技能的工作方式:如何在项目根部维护一份 Agent 永不违背的约束清单、如何在每次循环运行开始前把规则"烘焙"进上下文,以及如何借助 loop-gate、gate.yaml 等机制把约束从"提示词软约束"升级为"可机械执行的硬约束"。读完本文,你将能在自己的 Grok 工作流中落地一套可复制、可扩展、可审计的循环护栏体系。
为什么循环需要"约束"而不是"建议"
Loops 会放大判断力——好的判断被放大,坏的判断同样被放大。当 Agent 被调度器反复唤醒、自主读取 issue、打开 worktree、提议修复甚至合入 PR 时,仅靠一句"请小心一点"远远不够。约束(constraints)的价值在于:它不是一次性指令,而是一份每次运行开始时都会被重新读取并强制执行的绑定规则清单。Agent 必须遵守(binding),而不是酌情参考。
在 Grok Build TUI 中,这一机制由两件东西配合完成:
loop-constraints.md:位于仓库根目录的约束清单文件,人类用自然语言持续维护;loop-constraints技能:位于.grok/skills/loop-constraints/SKILL.md,每次循环运行的第一步就是读取该文件并把所有规则加载进上下文。
仓库根目录的 loop-constraints.md 正是这样一份真实可用的参考实例,它是 loop-constraints 技能实际读取并执行的绑定规则文件。
快速开始:用 /constraints 追加一条规则
在 Grok Build TUI 中,向约束清单追加规则只需要一条斜杠命令:
# 添加一条约束(追加到 loop-constraints.md 末尾)
/constraints Don't push before telling me. Never edit auth/. Always run tests first.
执行后,Agent 会把这条规则原样追加到 loop-constraints.md 中。这意味着约束的维护过程无需任何特殊编辑器操作——你随时可以用自然语言告诉循环"从今往后不许做什么",规则即被持久化,并在后续每一次运行开始时被重新加载。
与 Claude Code、Codex、Opencode 等工具的约束示例相比(参见 examples/claude-code/constraints.md 与 examples/opencode/constraints.md),Grok 的接入点是原生 TUI 命令 /constraints;而例如 Opencode 的等价做法是直接编辑 loop-constraints.md 或通过一次 opencode run 让 Agent 代为追加。无论哪种入口,落点都是同一份根目录文件。
在每次循环运行之前强制执行
约束技能的定位是"先于一切"。在 Grok 中,任何调度运行的 prompt 都应显式要求先执行约束技能,再进入 triage 或其它动作技能:
# loop-constraints 技能最先运行——它读取 loop-constraints.md,
# 在 triage 或任何动作技能之前把每条规则烘焙进 Agent 上下文。
/loop 1d Run loop-constraints, then loop-triage. Update STATE.md. No auto-fix in week one.
注意这里的 No auto-fix in week one:这是循环工程里常见的"报告先行"(report-only)策略,很多团队会先用 1–2 周只做汇报、不自动修复,确认 triage 质量稳定后再放开动作能力。约束技能与这种渐进式放权天然兼容——你可以在约束清单中写明"第一周禁止自动修复",技能就会在每次运行开始时把它重新施加给 Agent。
从源码结构看,约束技能的运行顺序承诺由 templates/SKILL.md.loop-constraints 与 skills/loop-constraints/SKILL.md 明确约定:该技能在 triage 或任何动作技能之前执行,因此调度 prompt 中的 Run loop-constraints, then loop-triage 与技能自身的"最先运行"定位是一致的。
工作原理:三步循环
约束机制本质上是一个三步闭环:
/constraints <rule>把规则追加到loop-constraints.md;loop-constraints技能(源自 templates/SKILL.md.loop-constraints)必须安装在.grok/skills/loop-constraints/SKILL.md;- 每次循环运行都以
loop-constraints开场——它读取文件、加载规则、强制执行。
关键设计在于:规则是"读进上下文"而非"读一遍就忘"。技能模板要求 Agent 在开始任何其它工作之前:(1) 从项目根目录读取 loop-constraints.md;(2) 把每条规则加载进工作记忆;(3) 检查 loop-pause-all 是否激活,若激活立即退出;(4) 将规则应用到之后每一个动作上。同时,运行开始时必须输出一行确认信息:
Constraints loaded from loop-constraints.md: N rules active.
这里的 N 需要替换为 loop-constraints.md 中实际生效的规则条数。如果文件不存在,技能会明确告知并退回到 docs/safety.md 中定义的默认安全规则继续运行——约束缺失也不会导致循环裸奔。
约束文件的组织:一份可维护的绑定清单
仓库根目录的 loop-constraints.md 展示了约束文件应有的章节结构。每一条规则都用自然语言写成、被 Agent 逐字读取,因此写得越精确,被误解的空间越小。参考实例按主题分为五节:
| 章节 | 示例规则 | 意图 |
|---|---|---|
| Push & Merge | 未经告知不得 push;未经人类批准不得自动合入 main;先建 draft PR 供人类 review | 守住合并与推送这道最后关卡 |
| Paths | 永不修改 .env、.env.*、auth/、payments/、secrets/、credentials/ 等路径 |
路径黑名单,敏感目录隔离 |
| Code | 提议修复前必须运行测试;禁止关闭测试换取 CI 变绿;禁止顺手重构无关代码,一次只修一个问题;同一事项最多尝试 3 次,超出即升级 | 控制修复质量与尝试上限 |
| Communication | 动手前先说明要做什么;未经批准不得关闭 issue 或 PR | 保持人类知情与审批权 |
| Budget | token 消耗达日限额 80% 时切换为只汇报模式;loop-pause-all 激活时立即退出 |
预算护栏与全局停摆开关 |
值得注意的两条"机械执行"规则(出自 Code 一节):
- 尝试上限要机械地执行:每次尝试记录到
loop-ledger.json,重试前先运行loop-context --check(详见loop-guard技能); - 预算护栏:token 消耗达到日限额 80% 时降级为 report-only。
模板 templates/loop-constraints.md 提供了同一套默认规则集,并在末尾留有注释:<!-- Add your own rules below. Use plain English. The loop reads this verbatim. -->。这意味着你可以在文件下半部分持续追加自己的项目专属规则,Agent 会逐字读取——这是该机制可长期演进的根基。
技能模板详解:守卫者(Guardrail)如何工作
skills/loop-constraints/SKILL.md(与模板 templates/SKILL.md.loop-constraints 内容一致)完整定义了技能的 enforcement 规则,可以归纳为四类"动作前检查":
- 推送前:重读 Push & Merge 一节。任何一条规则阻止推送,就停下来告诉人类;
- 编辑文件前:重读 Paths 一节。路径命中黑名单模式,升级(escalate)给人类;
- 提议修复前:重读 Code 一节,先运行测试,一次只修一个问题;
- 合入前:重读 Push & Merge 一节,必须有人类批准。
此外,技能明确定义了"与其他技能的交互",这正是约束机制能贯穿整个循环工作流的原因:
loop-triage:约束可以覆盖 triage 的优先级(例如"不许 push"意味着对 CI 修复也不得擅自动作);minimal-fix:约束限制可以被修改的文件范围;loop-verifier:约束定义的黑名单路径是 verifier 必须检查的对象;loop-budget:约束可以施加比loop-budget.md更严格的预算。
这种"约束贯穿所有技能"的布局,让约束成为循环运行中事实上的最高优先级输入——triage 在约束已经烘焙进上下文的同一个上下文内运行,而不是另起炉灶。
无约束文件时的默认安全底线
如果 loop-constraints.md 不存在,技能不会就此作罢,而是强制施加以下最小安全集(default constraints):
- 永不编辑
.env、.env.*、auth/、payments/、secrets/、credentials/; - 永不自动合入 main;
- 永不关闭测试;
- 连续 3 次修复失败后升级给人类。
这与 docs/safety.md 中的全局护栏一脉相承。该文档给出了更完整的路径黑名单(包括 **/*_key*、**/*_secret*、**/.terraform/**、**/k8s/production/**、**/migrations/**、**/billing/** 等),并强调:这些护栏是"会碰代码或外部系统的生产级循环"的最低要求。
从"软约束"到"硬约束":机械强制执行
提示词层面的约束有个固有弱点:它依赖 Agent 自觉阅读并遵守。仓库提供了两条把约束变成机械执行机制的路径:
1. loop-gate + gate.yaml 的路径级拦截
docs/safety.md 说明,路径黑名单(以及自动合并白名单)可以通过 tools/loop-gate 从 gate.yaml 机械强制执行,而不再依赖循环是否读到了本文档:
loop-gate check --action <type> --paths <changed files>
命令以退出码 2 表示升级(escalate)、0 表示放行——与 loop-context --check 采用的约定一致,控制脚本可以把两者串起来。仓库根目录的 gate.yaml 是这一机制的机器可读孪生文件,它与 docs/safety.md 的 Path Denylist 和 Auto-Merge Policy 章节保持同步,包含:
denylist:12 条路径模式(.env、secrets/**、credentials/**、.terraform/**、k8s/production/**、migrations/**、auth/**、payments/**、billing/**等);maxFiles: 10:单次改动触及超过 10 个文件即升级,无论改的是哪些路径——前提是"提出超大 diff 的循环已经失控";autoMergeAllowlist:仅当允许自动合并时使用,默认允许docs/**、**/*.md、**/*.test.mjs。
docs/safety.md 还提到 loop-sync 会在每次运行时对照 gate.yaml 检查该章节并报告分歧,防止"文档里的软约束"与"机械执行的硬约束"无声漂移。
2. loop-guard + loop-ledger.json 的尝试上限
loop-constraints.md 中的"Max 3 fix attempts per item; escalate after"同样被机械化了:loop-guard 技能把每次尝试记录到 loop-ledger.json,重试前先运行 loop-context --check。从 tools/loop-init/README.md 可以看到,修复类模式(pr-babysitter、ci-sweeper、dependency-sweeper、post-merge-cleanup)都会获得这个熔断器:连续多次同一错误、过多连续失败、token 预算或迭代上限被触发时,循环升级而非无效空转。这种"预算 + 上限 + 台账"的组合,正是约束从提示词走向机械保证的典型示例。
用 loop-init 一键脚手架
手动搭建约束文件与技能容易出错,仓库提供了自动脚手架:
npx @cobusgreyling/loop-init . --pattern daily-triage --tool grok
这条命令会把 starters/ 与 templates/ 中的文件按模式与工具复制到目标项目。就约束机制而言,scaffold 至少会带来:
loop-constraints.md:带默认规则集的约束清单;skills/loop-constraints/SKILL.md:从模板复制而来的约束技能(Grok 工具路径为.grok/skills/loop-constraints/SKILL.md);loop-budget.md与loop-run-log.md:模式专属的日预算上限与追加式运行历史。
根据 tools/loop-init/README.md,loop-init 也支持 -p pr-babysitter -t claude、--dry-run 等简写与预览形式;修复类模式还会自动获得 minimal-fix、loop-verifier 模板与 loop-guard/loop-ledger.json 熔断器。脚手架完成后,建议紧接着运行 npx @cobusgreyling/loop-audit . --suggest 并实际执行第一轮只汇报循环,以产生真实的运行信号。
落地建议与注意事项
- 约束是绑定的,措辞必须精确。技能模板明说"Constraints are binding";Opencode 示例则补充了一句更尖锐的提醒:如果一条规则可能被误解,就重写它——循环不会自行揣摩,最终兜底的是人类(参见 examples/opencode/constraints.md 的 Safety 一节)。
- 先只汇报,再放开动作。第一周用
No auto-fix约束配合 report-only 模式运行,等 triage 质量稳定后再逐步开放修复与合入能力,这与 patterns/daily-triage.md 中"Start report-only"的建议一致。 - 把
loop-pause-all当作全局急停。无论是约束文件、loop-budget.md还是STATE.md中的loop-pause-all标志,都能让循环立即停止动作而不删除调度器记录——这对审计与事故响应都更友好(参见 LOOP.md 的 kill switch 说明)。 - 软硬结合。人类可读的
loop-constraints.md负责表达意图,gate.yaml+loop-gate负责在文件路径层面机械拦截,loop-ledger.json+loop-context --check负责机械执行尝试上限。三层配合,循环才不会在失控时才发现约束只是"纸面约束"。 - 关注状态与成本。patterns/daily-triage.md 给出的成本画像(no-op 约 5k tokens、完整 triage 约 50k tokens、带修复约 200k tokens)可作为设置日预算与 80% 降级阈值的参考;运行后回到 docs/operating-loops.md 与 docs/loop-design-checklist.md 核对护栏清单是否齐全。
约束机制是整个循环工程体系的安全底座:它让 Agent 在高频自主运行时始终戴着"不许越界"的缰绳,也让人类工程师可以用自然语言随时收紧或放松缰绳。在 Grok Build TUI 中,从 /constraints 追加规则、到每次运行前的技能强制执行、再到 loop-gate 的机械拦截,这一整条链路都值得在你启动任何生产级循环之前先行就位。