把验收条件塞进路径:给编码代理一份不会串台的合同
代理在一个老仓库里翻车,往往不是不会写代码,而是拿到了一份过大的规则书。根目录 AGENTS.md 里混着 Android、后端、数据库迁移和前端样式要求;它读到了,却不知道这次改的是哪一层。最后就会出现熟悉的场面:一个 UI 小改动带出无关格式化,测试只跑了根目录命令,或者把生成目录也改进了 diff。
我现在把提示词当作临时任务单,把仓库指令当成可执行的接口。GitHub 文档把仓库级指令定义为给 Copilot 提供项目理解、构建、测试和验证方式的持续上下文;它支持仓库全局、路径专属和 agent 三类文件。[1] 重点不在“写得更详细”,而在让规则只在需要它的路径出现。路径命中时,路径规则会和仓库全局规则一起使用。[1]
先拆规则,不拆人设#
根目录只放跨目录都成立、半年内不会轻易变化的东西:包管理器、禁止提交密钥、总验收入口、提交前必须看 diff。别在这里塞组件命名、数据库 migration 命令或某个测试夹具的细节。那些内容一多,代理每次都得在无关约束里找针,连人也很难维护。
第二层是 .github/instructions/*.instructions.md。这类文件能用 applyTo 指定匹配路径;Copilot 处理命中文件时才会带入相应规则。[1] 我通常按“风险边界”划分,而不是按技术名词罗列:db.instructions.md 管迁移和回滚,generated.instructions.md 明说只能改源文件,ui.instructions.md 则规定视觉回归和无障碍检查。一个目录要同时命中两份规则并不奇怪,例如数据库页面既要遵循 UI 检查,也得遵守只读数据约束。
第三层才是 AGENTS.md,它写任务附近的局部事实:这个目录真正的入口文件、可改和不可改的文件、验收命令、交付格式。官方说明允许在仓库任意位置放多个 AGENTS.md,而离工作目录最近的那份优先。[1] 这很适合把“改 packages/payments 必须跑哪些测试”放在 packages/payments/AGENTS.md,不用污染根目录。
这里有个常被忽略的限制:优先并不等于覆盖。不要让根规则说“使用 pnpm”,局部规则又说“使用 npm”,指令可能同时进入上下文,代理只会摇摆。路径规则应该补充验收条件,不该改写已经存在的全局原则。
快速上手:用三层文件写出改动合同#
下面的例子只新增指令和测试文件,不会改业务代码。先在根目录建立目录:
mkdir -p .github/instructions packages/payments/tests/fixtures
根规则保持短小,负责人人都要遵守的事:
<!-- AGENTS.md -->
# 交付规则
- 使用 pnpm;不要改 lockfile,除非任务明确需要新增依赖。
- 修改前先列出目标文件;发现需要扩大范围时先停下说明原因。
- 交付前运行与改动路径对应的测试,并给出实际命令和结果。
- 不要编辑 dist、coverage 或任何由生成器写出的文件。
给支付目录放一份局部合同。它不是一篇“请小心”的说明,而是可以被复核的清单:
<!-- packages/payments/AGENTS.md -->
# Payments 改动合同
可修改:src/、tests/、本目录的文档。
禁止修改:migrations/、provider/ 和 fixtures/;它们需要人工审批。
验收:
1. pnpm --filter @app/payments test
2. pnpm --filter @app/payments lint
3. 在回复中列出触及的支付状态转换;不确定时不要猜。
最后给生成目录加路径规则,防止代理“顺手修”产物。applyTo 是这个文件能否生效的开关,所以模式要尽量窄:
---
applyTo: "packages/**/generated/**"
---
这些文件由代码生成器维护。不要直接编辑。
定位上游 schema 或 generator;如果无法定位,只报告建议的源文件,不要修改生成结果。
把这三个文件提交前,不要只让代理读一遍。用两个相反的小任务做探针:要求它修 packages/payments/src/ 中一个拼写错误,预期得到局部测试命令;再要求它直接改 packages/foo/generated/client.ts,预期它拒绝并指出上游来源。聊天记录里应当能看见读取了哪份指令、实际调用了什么命令。第一条通过只能证明局部规则没丢,第二条通过才证明生成目录的边界真的挡住了“方便的修复”。
把验收结果变成能比较的产物#
提示词里说“记得测试”没有审计价值。我的任务模板会要求代理在最后输出一个固定区块:改动文件、执行命令、退出码、未运行项和原因。然后 CI 用一个很小的检查脚本验证这份报告是否包含支付测试命令。它不能证明测试本身正确,但能抓住最常见的问题:代理改了高风险路径,却把验收留在了脑子里。
Claude Code 的 hooks 适合补上这层确定性。官方把 hook 定义为生命周期节点自动运行的用户 shell 命令,并明确说它能让某些动作必定发生,而不是依赖模型自己决定要不要执行。[2] 实操上我只用它做机械检查,例如在编辑后格式化、在任务结束时检查报告文件是否存在。是否允许迁移、是否接受架构取舍仍然交给人审,不要假装一个正则能做判断。
这套拆法有个很朴素的收益:规则离代码越近,例外越少;验收离改动越近,漏跑越容易被发现。根目录那份规则保持短,局部合同写清楚,最后拿两三个越界样例去撞它。代理有没有“理解项目”不重要,重要的是它在错误路径上没有权限假装理解。
Sources#
[1] https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot — Adding repository custom instructions for GitHub Copilot [2] https://docs.anthropic.com/en/docs/claude-code/hooks-guide — Automate actions with hooks | Claude Code Docs