只写 AGENTS.md,挡不住 Agent 改客户的迁移文件
Claude Code 只把指令文件当作上下文,不当作配置;真正能拦下写入的,是 PreToolUse 钩子。
要点速览
- GitHub 建议把边界分成三级,“绝不能做”一级要写明具体路径,比如 `db/migrations/`。
- Agent 读取离得最近的 AGENTS.md,Codex 默认最多只加载 32 KiB,所以要按目录拆分文件,并保持精简。
- AGENTS.md 只是上下文:要彻底拦下某个操作,得用 PreToolUse 钩子配合 exit 2,而且钩子只能防住无心之失。
在一个陌生的仓库里,编码 Agent 并不知道哪个目录正跑在生产环境。在它眼里,db/migrations/ 和其他目录没什么两样。GitHub 分析了 2,500 多个包含 AGENTS.md 的仓库,给出的建议里有一条说得很直白:明确告诉 Agent 哪些东西绝对不能碰。
但告诉不等于拦住。Claude Code 的文档写明,这类指令文件被当作上下文,而不是强制执行的配置。要彻底拦下某个操作,需要一个 PreToolUse 钩子(hook)。
本指南写给那些入场第一周就要和 Agent 一起在客户代码库里干活的前线部署工程师(FDE)。走完六步,你会有两层防护:一份 AGENTS.md,让 Agent 理解规则;一个钩子,把禁区真正锁上。你还会清楚每一层的局限,免得向客户许下做不到的承诺。
要做什么,需要准备什么
全文围绕一个假想的例子:一家物流公司的 monorepo,里面有 services/api、services/billing,以及一个已在生产环境生效的 db/migrations 目录。文中的所有命令,比如 make test,都只是针对这个假想仓库的示例。实际工作中,请换成客户团队真正在用的命令。
你需要仓库的读权限,以及一个支持 AGENTS.md 的编码 Agent。Claude Code 可以读取 AGENTS.md 来代替 CLAUDE.md,Codex 也有自己加载这个文件的机制。最重要的是,约客户的一位工程师聊半小时,问清楚他们怎么构建、怎么测试,以及哪些目录谁都不许改。
第 1 步:像给新同事写 README 一样写给 Agent
agents.md 网站把这个文件描述为写给 Agent 的 README。它就是纯 Markdown,标题随你定,因为 Agent 只是读你写下的文字。没有 schema,也意味着没有任何语法能保证 Agent 一定照做。
文件开头先列出 Agent 需要运行的命令,每个参数都写全:
# AGENTS.md
## 命令
- 构建:`make build`
- 测试(单个服务):`make test SERVICE=api`
- 提交前做 lint:`make lint`
检查: 在自己的机器上把每条命令都跑一遍。你跑不通的命令,Agent 也跑不通,只是 Agent 可能会用你不想要的方式去“修”它。
第 2 步:三级边界,写明具体路径
在客户的仓库里,这是最重要的一部分。GitHub 提出了三级划分(始终要做、先问再做、绝不能做),并把边界列为每份文件都应具备的六个核心部分之一。GitHub 的示例文档 Agent 就带有一条路径级别的禁令:绝不能碰 src/。把同样的写法原样用到迁移目录上:
## 边界
- 始终要做:提议提交前先运行 `make lint` 和 `make test`。
- 先问再做:新增依赖;修改 `services/billing/` 下的文件。
- 绝不能做:修改或删除 `db/migrations/` 下的文件;提交 `.env` 文件。
别省掉中间这一级:在客户那里,它往往最有用。计费代码不算禁区,但那里的每次改动都应该先得到客户方某个人的点头。
第 3 步:在迁移目录里单独放一份文件
据 agents.md 介绍,Agent 会自动读取目录树中离得最近的那份 AGENTS.md,且最近的文件优先。Codex 同样会从项目根目录(通常是 Git 根目录)一路走到当前工作目录,拼出一条指令链。所以放在 db/migrations/ 里的文件,会在 Agent 进入该目录工作时加载。
# db/migrations/AGENTS.md
本目录下的每个文件都已在生产环境执行过。
不要修改或删除现有文件。
如果需要改 schema,先停下来,询问团队成员。
拆分文件还有一个技术上的原因。Codex 在文件总大小达到 project_doc_max_bytes 上限(默认 32 KiB)后就不再加载更多文件。根目录的文件一旦臃肿,子目录的文件可能在没人察觉的情况下被跳过。
检查: 在 db/migrations 里开一个 Agent 会话,问它能看到哪些规则。如果没有出现这个目录的规则,问题就出在文件顺序或大小上。
第 4 步:用 PreToolUse 钩子锁住禁区
文件里写一句“绝不能做”并不是锁。Blake Crosley 在一篇讲解 Claude Code 钩子的文章里把这个区别讲得很清楚:指令文件给模型的是它大概率会遵循的指引,而钩子无论模型是否配合都会执行。
拦截机制很简单。钩子脚本向 stderr 写一条消息,然后以退出码 2 退出:这次工具调用会被拒绝,消息会回传给 Claude。下面是一个简化的示例脚本。它假定钩子从 stdin 收到工具调用的描述,只要调用中提到 db/migrations/ 就拦下。
#!/usr/bin/env bash
# block-migrations.sh —— 示例,简化版
input=$(cat)
if echo "$input" | grep -q "db/migrations/"; then
echo "已拦截:db/migrations/ 已在生产环境执行过。不要修改这里的文件,请停下来询问客户团队。" >&2
exit 2
fi
exit 0
留意 stderr 里的这条消息。它会回传给 Agent,所以要写成带有下一步建议的解释,而不只是一句“拒绝”。这个简化版连读取该目录也会拦下;实际使用时,请查阅客户所用 Agent 的文档,弄清输入格式和 matcher 的声明方式,做到只拦写入。
exit 2 并不是唯一的办法。钩子也可以返回一个结构化决策 permissionDecision: "deny"。两者结果相同,都是拦下工具:一个靠退出码加 stderr,一个显式返回决策。
据 Crosley 介绍,即使在 bypassPermissions 模式下,返回 deny 的钩子依然会拦下工具。在客户那里,总可能有人把 Agent 的权限全部放开运行,这个细节很有分量。
检查: 让 Agent 往一个旧的迁移文件里加一行。这次编辑必须被拒绝,而且 Agent 应当复述你写在 stderr 里的原因。
第 5 步:钩子也有局限
钩子是防止失误的护栏,而不是阻止 Agent 有意绕过规则的机制。一篇在 dev.to 上讲如何拦截 rm -rf 的指南也指出,路径匹配是可以被绕过的。
看看上面的脚本:它只查找精确的字符串 db/migrations/。一条通过相对路径或环境变量进入该目录的 shell 命令,未必包含这个字符串,护栏很容易就放它过去。
所以和客户沟通时,不要把这两层中的任何一层称作“安全措施”。它们让失误更少,也更容易被发现。生产环境真正的保护,仍然是客户团队现有的访问控制和评审流程。
第 6 步:从小处起步,Agent 出错就补
第一天别写 300 行,何况 Codex 还有大小限制。给 Agent 一个真实的小工单,看它运行了哪些命令、改了哪些文件,然后在它出错的地方精准补充。
假设 Agent 没有只测一个服务,而是对整个仓库跑了 make test,花了二十分钟。你就加一行,说明如何用 SERVICE 测试单个服务。文件里的每一行,都应该能追溯到一次已经发生的失误,或客户点名的一项风险。
这两层在客户那里有什么用?
AGENTS.md 实际上就是一份关于代码库的客户调研纪要。问一句“哪些目录谁都不能碰?”,往往能挖出从未写下来的东西:一次曾引发事故的迁移,或者一个只有一个人敢改的模块。
把这些写下来,为最危险的区域加上钩子,再请客户的技术负责人把两者都过一遍。这是向对方表明你尊重他们系统的一个快捷方式。
如果你正在转向 FDE 岗位,可以在作品集里加一个示例仓库:一份三级边界的 AGENTS.md、迁移目录里的一份子文件、一个带测试的写入拦截钩子,以及一段展示每次 Agent 出错后文件如何改进的提交历史。
当职位描述提到要向客户团队推广 AI 编码工具时,你在面试中就有一个具体的例子可以展开,而不只是说自己“熟悉 Agent”。
你的草稿有没有漏掉什么?
拿你手头的仓库检查一遍。命令能否照写的样子跑通?边界里有没有“先问再做”这一级,还是只有“绝不能做”?“绝不能做”一级有没有写明具体路径?
接着,在某个子目录里打开 Agent,问它看到了哪些规则。最后,对每一条“绝不能做”问自己:如果 Agent 无视这一条,会不会出真正的事故?如果会,这一条就需要一个钩子。
你留给客户最好的 AGENTS.md,是你离开项目后对方团队还在持续修改的那一份;最好的钩子,是他们明白其存在理由的那一个。
这篇文章对你有帮助吗?
感谢你的反馈!
6 个信源
- AGENTS.md
- How to write a great agents.md: Lessons from over 2,500 repositories · 2025-11-19
- How Claude remembers your project
- Custom instructions with AGENTS.md | ChatGPT Learn
- Claude Code Hooks Explained: The Deterministic Layer Around Your Agent · 2026-07-01
- Block rm -rf with a Claude Code PreToolUse hook, and know its limits