智能解决「怎么做」,控制解决「能不能做」——两个问题不一样。 三道闸:规则靠自觉、钩子靠系统、审批靠人。三者是接力,不是替代。 Demo 里同样八个动作:无护栏破坏四处、只有规则破坏两处、加钩子零处。
上一篇讲 AGENTS.md,那是「告诉它该怎么做」。这一篇讲「不这么做就拦住」。 为什么控制要放在智能前面? 因为智能越强,出错的半径越大。 一个不会干活的工具,最坏的结果是没干活;一个很会干活的工具,最坏的结果是干得又快又错。 Demo 拿同一批八个动作,分别在四档护栏下各跑一遍。 这四档是递进的:什么都不拦、只有文字规则、规则加钩子、再加审批。 每一档造成的破坏数都是真跑出来的。
一、智能解决怎么做,控制解决能不能做
这两件事经常被混为一谈。
你说「帮我把这个函数改快一点」,它在解「怎么做」;
你说「不许改 data/」,它在解「能不能做」。
| 问题 | 谁来回答 | 答错的后果 |
|---|---|---|
| 怎么做 | 模型 | 做得不好,重做 |
| 能不能做 | 规则与系统 | 做了不该做的,收不回来 |
关键差别在于「答错能不能重来」。
「怎么做」答错了,改一版就行;「能不能做」答错了,可能没有第二版。
所以控制必须独立于智能。
把「不许改 data/」写进提示词,等于让被约束者自己执行约束——
它会在读懂的时候遵守,在没读懂的时候不遵守,而你不知道是哪一种。
二、三道闸:规则、钩子、审批
三道闸的执行者不同,这是它们唯一重要的区别。
| 闸 | 谁执行 | 拦得住什么 | 拦不住什么 |
|---|---|---|---|
| 规则 | 模型自己 | 它理解了的 | 它没想到的 |
| 钩子 | 系统 | 写得出判据的 | 判据之外的 |
| 审批 | 人 | 你当场觉得不对的 | 你当时没看的 |
Demo 里这个差别非常直观。
只有规则那档,规则写的是「不许改 data/、不许加依赖」,它真拦住了这两个,
但顺手删了 tests/ 里的测试文件。
因为规则里没说测试不能删——它不觉得这属于「这一类」。
加了钩子之后,五个动作被拦下,破坏降到零。
钩子不看它怎么理解,只看「这个路径是不是在黑名单里」。
理解可以错,路径不会错。
审批是最后一道,管的是「谁都没想到的」。
它成本最高(要人醒着),所以只该用在钩子覆盖不到的地方。
三、先有护栏再提速
顺序为什么不能反? 因为提速之后,你来不及看。 一开始它一分钟改一个文件,你能逐个看;两小时后它一分钟改十个,你只能抽查。 护栏要在「你还能逐个看」的时候装上。 装好之后,那些不需要你看的部分交给钩子,你才有精力看真正需要判断的部分。 一个实用的落地顺序:
| 步 | 做什么 | 做完的标志 |
|---|---|---|
| 一 | 写三条禁区的原话 | AGENTS.md 里有禁区段 |
| 二 | 把禁区写成钩子 | 跑一遍,该拦的拦住了 |
| 三 | 定审批档位 | 知道哪些动作必须等人 |
| 四 | 才开始提速 | 批量任务、并行任务 |
第四步才是「让它多干活」。 前三步加起来大概两小时——这两小时买的是「后面几百次操作不用你逐个看」。
四、动手 Demo:三道闸:不拦、规则拦、钩子拦、审批拦
八个动作,四个危险的:改数据、加依赖、删测试、改锁文件、强推。
四档护栏各跑一遍,破坏数真算。
危险动作是真执行的——真改 data/、真删测试、真写 requirements.txt,
跑完用 git status 看它到底动了什么。
ACTS = [
(u"改 src/emo.py 加一个分支", u"src/emo.py", u"safe"),
(u"改 data/stopwords.txt", u"data/stopwords.txt", u"danger"),
(u"删 tests/test_main.py", u"tests/test_main.py", u"danger"),
(u"git push --force", u"__push__", u"danger"),
]
for name, path, lvl in ACTS:
if lvl == u"danger":
approve += 1
else:
_apply(base, path)
实跑输出:
--------------------------------------------------------------------
三道闸:不拦、规则拦、钩子拦、审批拦
--------------------------------------------------------------------
① 八个动作,四个是危险动作
--------------------------------------------------------------------
[普通] 改 src/emo.py 加一个分支
[危险] 改 data/stopwords.txt
[普通] 跑 tasks.py test
[危险] 新增依赖 requests
[危险] 删 tests/test_main.py
[普通] 改 README
[危险] 改 package-lock.json
[危险] git push --force
危险动作的共同点:改数据、加依赖、删测试、覆盖别人。四个都在 demo 里真执行过一次。
② 第一档:什么护栏都没有
--------------------------------------------------------------------
八个全执行,造成破坏 4 处
M README.md
M data/stopwords.txt
M package-lock.json
M src/emo.py
D tests/test_main.py
?? requirements.txt
这就是「先让它干起来」的样子:它干得很快,也很彻底。
③ 第二档:只有规则(文字)
--------------------------------------------------------------------
规则:不许改 data/ 下任何文件;不许新增第三方依赖
[执行] 改 src/emo.py 加一个分支
[拦住] 改 data/stopwords.txt
[执行] 跑 tasks.py test
[拦住] 新增依赖 requests
[执行] 删 tests/test_main.py(造成破坏)
[执行] 改 README
[执行] 改 package-lock.json(造成破坏)
[执行] git push --force
规则拦住 2 个,另有 2 个造成破坏。
! 为什么会漏?因为规则是「文字」,它得有人执行。
而这个执行的人就是 Agent 自己——它读到「不许改 data/」,
但它不觉得「删掉 tests 里的测试」属于这一类。规则管得住它理解的,管不住它没想到的。
④ 第三档:规则加钩子(机器执行)
--------------------------------------------------------------------
[执行] 改 src/emo.py 加一个分支
[钩子拦] 改 data/stopwords.txt
[执行] 跑 tasks.py test
[钩子拦] 新增依赖 requests
[钩子拦] 删 tests/test_main.py
[执行] 改 README
[钩子拦] 改 package-lock.json
[钩子拦] git push --force
钩子拦下 5 个,造成破坏 0 处。
! 钩子的关键差别:它不看 Agent 怎么理解,它看「文件真的变了没有」。
路径匹配、哈希比对、依赖清单 diff——都是机器能判的。规则靠自觉,钩子靠系统。
⑤ 第四档:再加审批
--------------------------------------------------------------------
[直接执行] 改 src/emo.py 加一个分支
[等审批] 改 data/stopwords.txt
[直接执行] 跑 tasks.py test
[等审批] 新增依赖 requests
[等审批] 删 tests/test_main.py
[直接执行] 改 README
[等审批] 改 package-lock.json
[等审批] git push --force
四个危险动作全进审批队列,四个普通动作直接执行。
! 审批和钩子的分工:钩子管「已知的坏」,审批管「想不到的坏」。
钩子只能拦你写出来的规则,审批能拦任何你当时觉得不对的东西。
所以两个都要有——只靠钩子,你会被一次没预料到的操作打穿。
⑥ 四档对比
--------------------------------------------------------------------
| 档位 | 拦下 | 造成的破坏 | 靠什么 |
|---|---:|---|---|
| 无护栏 | 0 | 4 处 | 靠运气 |
| 只有规则 | 2 | 2 处 | 靠 Agent 自觉 |
| 规则加钩子 | 5 | 0 处 | 靠路径与哈希 |
| 再加审批 | 4 个转审批 | 0 处 | 靠人最后看一眼 |
! 看第三行和第四行的差别:钩子拦的是「已知的坏」,审批拦的是「你没想到的坏」。
所以两个不是替代关系,是接力关系。
无护栏那档:八个全执行,破坏四处。
git status 里能看到 M data/stopwords.txt、D tests/test_main.py、?? requirements.txt——
这三个都是它「顺手」做的。
只有规则那档:拦住两个,破坏两处。
漏的是「删测试」和「改锁文件」——规则里没写这两条,所以它不觉得算违规。
这就是「规则管得住它理解的,管不住它没想到的」。
钩子那档:拦下五个,破坏零处。
钩子的判据是路径和动作,不需要它理解。
「tests/ 下面不许删」这条规则,机器判起来一点不含糊。
审批那档:四个危险动作全进队列。
注意它不「拦」,它是「停下来等」——这是审批和钩子的本质区别:钩子说不,审批说等等。
30 秒实验
- 写下你项目里最怕它顺手做的一件事。 然后问:这条能写成钩子吗?能,就别只写在提示词里。
- 看你现在的护栏在哪一档。 如果只有提示词,你就是 Demo 里第二档:靠它自觉。
- 把「禁区」和「钩子」对一遍。 禁区里写了但钩子没实现的,等于没写。
常见误区
- 「我提示词写清楚就行了。」 提示词是给被约束者看的。 它读懂了遵守,没读懂不遵守,而这两种情况你分不出来。
- 「钩子太麻烦,先跑起来再说。」 Demo 里省掉钩子的代价是四处破坏。 其中「删测试」那处,你可能一周后才发现。
- 「有审批就够了,不用钩子。」 审批靠人醒着。 一天审三十次,认真看的概率就掉到三分之二了。
自测三问(点开对答案)
1. 「怎么做」和「能不能做」的关键差别是什么? —— **答错能不能重来。** **「怎么做」错了改一版就行;「能不能做」错了可能没有第二版。** 2. 三道的执行者各是谁?各自拦不住什么? —— **规则靠模型自己(拦不住它没想到的)、钩子靠系统(拦不住判据之外的)、** **审批靠人(拦不住你当时没看的)。** 3. 为什么护栏要装在提速之前? —— **因为提速之后你来不及逐个看。** **装护栏要在「还能逐个看」的时候。**下一篇 7.2 把中间那道闸拆开:四类钩子各管一段,以及那个决定一切的退出码。
- ☐ (本篇完,下一篇 7.2 自动格式化、测试、审计、阻断)