让 agent 动手前先划清目录边界:哪些能改,哪些必须先问你

2026-07-29

数据截至 2026-07,各产品的额度与报错口径以官方最新说明为准。

大多数人把「agent 动了不该动的目录」归因成模型不守规矩,实际上多数案例卡在更前面一环:你写下的边界,它压根没读到,或者读到了也判定不出某个具体文件算不算越界。 一句「不要动核心模块」在你脑子里边界清晰,落到执行时就是一团模糊——哪个目录算核心,改一行常量算不算动,新增文件算不算改。模型不是在违抗你,是在一个没有判定标准的空间里自己补了个标准。

这篇讲的是动手之前的那一步:怎么把“能动 / 先问 / 禁区”切成机器能判定的形式,怎么验证它真的生效,以及失效时按什么顺序往下查。站内另有两篇是它的邻居:一句需求换来 30 个文件的改动 处理的是改动已经铺开之后怎么定位和收敛,属于事后止血;CLAUDE.md 怎么写 讲的是项目规则文件本身的结构和模板,属于载体。本篇夹在中间——边界内容怎么设计成可判定的、怎么确认它被执行链路读到了。三篇分工不重叠,遇到问题按阶段挑一篇看就行。

一、先判断你踩的是哪一类,而不是急着改规则

失守的表象很像,但成因分四层,越靠前越常见,也越好修。按下面这张表对号入座,别跳过验证那一列——不验证就改规则,改完通常还是原样。

现象大概率成因怎么验证处置动作
明明说过别动配置目录,还是改了根本没有成文约定,只在某次对话里口头说过在仓库里搜一遍规则文件,看这条是否真的落成文字落成文件,写清具体路径而非模块名
规则文件里写了,agent 表现得像没看见规则文件没被加载:路径不对、层级太深、或会话没走到读取那一步直接问它当前生效的项目约定有哪些,看它复述得出来吗把文件放到工具约定的位置,任务开头让它先复述一遍边界
复述得出规则,动手时仍越界规则是抽象句,无法映射到具体文件拿一个真实路径去问它「这个算不算禁区」,看它能否答得确定改写成路径通配形式,一条一个目录
单个改动都合规,合起来越界只约束了「改哪里」,没约束「改多少、改完谁验」开工前记下提交号,收工后 git diff --stat <该提交>,统计跨了几个顶层目录加改动面阈值:超过 N 个目录必须停下来确认
它说没动某文件,实际动了汇报与事实脱节,没有机器校验只信 git status --porcelain;若它中途提交过,status 会是干净的,得改用 git diff --stat <开工时的提交> 才看得见用脚本或钩子做校验,不用自然语言复述当验收
团队里你没事、同事天天中招规则文件有多份且互相打架搜出所有同类规则文件,逐条比对是否冲突合并成单一来源,冲突时明确谁优先

这张表的排序是有讲究的:从上往下,修复成本递增,而发生频率递减。所以排查也照这个顺序走——先确认约定存在,再确认被读到,再确认可判定,最后才是加硬拦。跳着查很容易一上来就折腾工具层的拦截脚本,结果发现只是规则文件放错了目录。

二、把边界切成三色分区,而不是写成一句原则

有效的边界有一个硬指标:拿任意一个仓库内路径去比对,都能在三秒内得出确定结论。做不到这点的表述,就是留给模型自由发挥的空间。我的做法是切成三档,每档只写路径,不写形容词。

绿区(可以直接动)。 业务代码目录、测试目录、文档目录、脚手架生成的样板。这一档要写得尽量大,因为绿区太小的直接后果是每一步都要问你,人被拖住之后你就会开始随手放行,边界反而形同虚设。

黄区(改之前必须先说,说清改什么、为什么,等一句确认)。 典型是数据库迁移脚本、依赖清单、构建配置、对外接口的签名、鉴权与权限判定相关的代码。这些改动本身合理,但影响半径超出当前任务,需要人过一眼。黄区不是禁止,是强制暴露。

红区(禁止改动,需要变更时由人来做)。 生产环境配置、密钥与凭据文件、CI 部署流水线定义、.git 内部、许可证与合规声明文件、已归档的历史目录。红区的判断标准很朴素:改错了要么线上出事,要么涉及信任与合规,要么根本不该由自动化流程碰。

写下来大致是这个形态,注意每条都落到路径:

可直接修改:src/**、tests/**、docs/**
改动前须确认:migrations/**、package.json、pnpm-lock.yaml、Dockerfile、.github/workflows/**
禁止修改:.env*、deploy/**、certs/**、LICENSE
新增文件:仅限 src/ 与 tests/ 下,其它位置新建文件同样视为需确认

最后一行经常被漏掉。多数人只想到“修改”,忘了“新增”和“删除”也是改动。agent 在根目录新建一个配置文件、或者顺手删掉它认为无用的脚本,这两类在事后 diff 里最不显眼,却最容易埋雷。把它们显式写进去,成本只是一行字。

分档写完还有一步:给每个红区条目补一句“为什么”。不是为了让模型认同,是为了让它在遇到边缘情况时有推理依据。只写“禁止改 deploy/”,它遇到 deploy/README.md 时会犹豫;补一句“该目录变更会直接影响线上发布流程”,它就能自己判断改文档不算触线,而改脚本算。

三、确认约定真的进了执行链路

规则写得再好,没被读到等于零。这一层的排查很直接:开工前让它把当前生效的边界复述一遍,你比对一下有没有丢条目。复述不出来,问题就在加载环节,常见有三种。

一是位置不对。各家工具对项目级规则文件的存放位置有各自约定,放错地方就是普通文本文件。这里不列具体文件名和路径规则——各家不同且会调整,以官方最新说明为准,你按自己在用的那款工具的说明放。

二是层级太深。单体仓库里,规则文件放在子包里,而 agent 的工作目录是仓库根,或者反过来。团队规则文件互相打架 那篇讲的就是这类多份规则并存时的优先级问题,单体仓库里尤其容易踩。

三是内容被挤掉了。上下文空间是有限的,长会话跑到后半程,早期加载的约定可能已经被后续内容挤出有效范围。这不是玄学,你能观察到的信号是:同一个 agent 在会话早期守规矩,聊到几十轮之后开始越界。对策是每次进入实际改动动作前,重新点一次边界——不用重复全文,把红区那几条贴一遍就够。

验证手法我推荐一个很笨但可靠的做法:准备两三个“陷阱路径”,都是红区里的真实文件,开工前逐个问“这个文件我这次任务里能不能改”。答得含糊或者答错,说明这次会话的边界没立住,别急着让它动手。这一步花不了一分钟,比事后翻 diff 便宜得多。

四、判定对了还不够,得有一层不依赖自觉的拦截

前三节做完,日常任务里的边界基本能守住。剩下那些守不住的场合,靠约定是拦不住的,得让越界这件事在物理上更难发生。几个可选层次,按侵入性从低到高。

最轻的一层是文件系统权限。红区文件设成只读,越界时它会直接撞上写入失败,而不是悄悄改完。这招对本地开发环境很好使,代价是你自己要改的时候得先解锁。它防的是「顺手写了」,不防「决定绕开」——一个有命令执行权限的 agent 完全可以自己把权限改回来,所以别把它当唯一防线,它的价值在于把无意的越界变成一次显式的、你能在日志里看见的动作。

中间一层是版本控制层的校验。提交前跑一个脚本,检查暂存区里有没有红区路径:

#!/bin/sh
# 取本次暂存的文件清单,与红区路径前缀比对
if git diff --cached --name-only | grep -qE '^(deploy/|certs/|\.env)'; then
  echo "触碰红区文件,已阻断提交" >&2
  exit 1
fi
exit 0

这里用 if 包住、结尾还显式写一行 exit 0,不是啰嗦。假如图省事写成 grep -qE '...' && exit 1 并让它当脚本的最后一条语句:没命中红区时 grep 返回非零,&& 短路,整条语句的退出码就是那个非零值;而脚本的退出码等于最后一条执行的命令的退出码,钩子收到非零就把这次完全正常的提交也挡了。顺手纠正一个流传很广的解释——这跟 set -e 没关系:set -e&& 列表里非最后一条的命令是豁免的,grep 在这里失败既不会提前中止脚本,set -e 也救不了末行退出码这个坑。真正的保险是判定写完补上 exit 0。红区清单换成你自己的目录,正则里的 . 记得转义(示例里的 \.env 已经转过)。

这段逻辑放进提交钩子,或者放进 CI 的一个前置任务,都能起作用。它的好处是不依赖任何一款 AI 工具的具体机制,谁改的都拦,包括你自己手滑。

再重一层是环境隔离。让 agent 在一个独立工作区里干活,主工作区不受影响:

# 在同级目录建一个新工作区,同时开一条新分支
git worktree add ../feature-x -b feature-x

新工作区里跑任务,完事回主工作区审阅合并,不满意就整个目录删掉重来。这里要说清一件容易想当然的事:新工作区是按分支内容检出的,凡是被版本控制跟踪的红区文件,照样会出现在新目录里,隔离掉的只是未被跟踪的那部分——本地 .env、私钥、个人调试脚本这些通常不进版本库的东西,不会跟过去。所以 worktree 解决的是「炸了好清理」和「本地密钥不暴露」,不解决「跟踪中的 deploy/ 改不了」。真要让跟踪文件也从工作区里消失,得再叠一层稀疏检出,只把绿区目录检出来:

cd ../feature-x
git sparse-checkout set src tests docs

这一步的模式只作用于当前这个工作区,主工作区的文件不会跟着消失,可以放心试。代价是环境要重新装依赖,稀疏检出还可能让构建因缺文件而失败,不适合每个小任务都这么搞。

最重的一层是把黄区改动强制走人工确认流程。这跟 把人卡在关键节点上 讲的是同一件事——不是所有步骤都要人参与,而是识别出那几个不可逆或高影响的动作,只在那里设卡。设卡设得准,人的注意力才用在刀刃上;设得太密,人会疲劳,疲劳之后就是无脑放行。

还有一件事值得单独提:别把边界约定和权限档位搞混。给 agent 全放开权限之后该收到哪一步 谈的是授权层级——它能不能执行命令、能不能写文件、能不能联网。本篇谈的是在已授权的前提下,写操作允许落在哪些路径。两者是正交的:权限给到能写文件,不等于所有文件都能写;权限收到只读,边界约定也就无从谈起。先定权限档,再定路径边界,顺序反了会做很多无用功。

五、什么情况下别再折腾了

工程师最容易犯的错是在一个已经跑偏的方向上继续加规则。下面几条,踩到一条就该停手。

同一条边界,你已经改写第三遍还是被突破。 这通常说明问题不在表述,而在这条边界本身跟任务目标冲突。比如你要求“重构这个模块但别动它的接口”,而合理的重构方案必然要动接口——这时候模型是在两难里选了一边,再怎么措辞都没用。止损动作是回过头改任务,不是改规则。

改动已经跨了三个以上顶层目录,而你说不清每处为什么改。 到这一步的成本收益已经翻转:读懂并挑拣这堆改动,通常比丢掉重来更费劲。干净的回滚点是最好的兜底,所以开工前那一步很关键:

git status --porcelain   # 无输出即工作区干净
git stash list           # 无输出即没有遗留的 stash

工作区干净的状态下开工,回滚就是一条命令的事;带着未提交改动开工,回滚时你自己的活也一起没了。这是所有兜底手段里性价比最高的一个,却最常被跳过。

红区已经被碰过,而且改动进了共享分支。 这时候优先级不再是修边界,是评估影响:密钥类文件一旦进过版本历史,删掉文件不等于删掉历史,该轮换的凭据要轮换。这一步别侥幸,也别指望“没人注意到”。

同一个任务,你花在纠正边界上的时间已经超过自己写的时间。 换条路的信号很明确了:这个任务的边界复杂度超出了当前的协作方式。要么拆小——把涉及黄区的部分抽出来自己做,剩下的纯绿区部分交给它;要么直接自己动手。承认某类任务不适合交出去,不丢人,比来回拉扯划算。

判断止损的通用尺子是:每一轮纠正之后,越界的距离有没有变短。变短说明方向对,继续;三轮之后原地踏步,方向就是错的。

六、避坑清单

用模块名而不用路径。 会踩是因为写规则时你脑子里有一张架构图,“核心模块”“公共层”这些词对你是精确的,对一个只能看到文件树的执行者是模糊的。避法很简单:每条边界写完自查一遍,能不能直接拿去做路径匹配,不能就改到能。

只列禁区,不列允许区。 会踩是因为写禁止清单符合直觉,而且写起来快。后果是它在不确定的地方会一律停下来问你,或者反过来——认为没被禁止的都能动,而你其实还有一堆没想到要禁的。三档都写,绿区写足,黄区才有意义。

把边界只放在一次性的对话里。 会踩是因为当下说完确实生效了,你就以为这事定了。但换个会话、换个人、换台机器,边界就没了。避法是凡是重复出现的约定,一律落文件,对话里只说这次任务特有的。

忘了约束新增和删除。 会踩是因为“改动”这个词在中文里默认指向修改。避法是在规则里明写三个动作:修改、新增、删除,各自的允许范围分别是什么。

拿它的总结当验收。 会踩是因为总结读起来很像事实,而且通常大部分是对的,几次没出事就放松了。避法是把 diff 当唯一事实来源,git diff --stat 花五秒钟,比读三段总结可靠。尤其当它说“已完成,未改动其它文件”时,这句话的可信度取决于你有没有去核对,而不是取决于它的语气。

黄区设得太宽,人被拖垮。 会踩是因为出过一次事之后本能地把范围往大了划。后果是每步都要确认,人开始不看内容直接放行,黄区退化成绿区且带着虚假的安全感。避法是黄区只保留“影响半径超出本次任务”的那几类,定期回看:哪些条目你最近十次都是无脑放行的,该降级就降级。

在单体仓库里只写一份根级边界。 会踩是因为根级规则看起来能覆盖全仓。实际上不同子包的红区完全不同——某个包的构建配置对它自己是黄区,对别的包是无关文件。避法是根级写通用红区,子包各自补充自己的,并明确子包规则优先。

边界写完就再不动了。 会踩是因为它确实一开始有效。但目录结构会变,新目录出现之后不在任何一档里,处于未定义状态。避法是把边界清单的复查挂在目录结构变更上:每次新增顶层目录,顺手归档。

收束:开工前的六条自检

边界这件事的性价比,在于它是少数几个“事前花一分钟能省掉事后一小时”的动作。它不能让模型变聪明,也替代不了代码评审,但能把“你需要审查的范围”从整个仓库收窄到几个目录——审查范围一小,人的注意力才够用。

每次让 agent 动手之前,过一遍这六条:

  1. 工作区是干净的吗?回滚点在不在?
  2. 三档路径清单存在吗?是路径还是形容词?
  3. 新增和删除有没有单独写清楚?
  4. 让它复述一遍边界,复述得全吗?
  5. 拿一个红区真实路径问它能不能改,答得确定吗?
  6. 万一它真越界,有没有一层不依赖自觉的东西能拦住?

六条都过,剩下的越界基本属于小概率;哪怕发生了,也停在一个你能一条命令退回去的位置。这就够了——边界的目的从来不是零事故,是让事故的代价小到可以承受。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。