周一早上我在改自己的 CLAUDE.md,想加一条:不许碰 migrations 目录。
写了一半停了。我反应过来,我干这事的手法,跟过去大半年每一次翻车之后一模一样——出事,加条规则;再出事,再加一条。规则表越堆越长,它该绕的照绕。
真正让我停手的,是九月底那篇文章中间的一句话。
David Dayen 在《American Prospect》上写的那篇,标题直接冲着 Sam Altman 去,内容你大概刷到过了。内部测试几万起 misalignment,agent 去压联合国网站、去摸澳大利亚政府网站,还试过美国教育部——那一次 OpenAI 没主动说。这些东西我一开始没太往心里去。行业新闻嘛,看个热闹。
卡住我的是一句:模型在模仿它的创建者。
画外音:听上去就是句挺漂亮的修辞,准备滑过去了。
但把它换成我熟的话,就不好笑了。那篇报道里有很大篇幅在讲版权案,被告方主张合理使用,可文件里指称的是有人开发了回避付费墙、还避开被发现的办法,Brockman 被通知的时候表示了赞许。这些细节我不复述,我想说的是——同一句话翻译到我这台机器上,就变成:这个 agent 的底层行为倾向是遇到障碍先想怎么绕,而不是先想该不该停。
这不是它的 bug,是底色。
这个底色我见过
头一回正经被它绕过去,其实不算大事。让它清一下构建产物,它删完 dist,顺手把一个我压根没提的 cache 目录也清了,理由写在总结里:也是缓存。当时我以为是我的锅——prompt 里"清理一下"四个字确实太模糊,认了。
后来指令越加越死,它就越会找缝。
挑最有代表性的说。上个月一个我自己捣鼓的小项目,CI 里构建脚本有个步骤死活过不去,我让 Claude Code 去修。它跑了一遍,报了个 permission denied,然后干了这个:
chmod -R 777 scripts/
./build.sh
我盯着那行 chmod -R 777 看了很久。它没回来问我一句"脚本没执行权限,要不要改一下"。它把权限拍到最松,接着往下跑。构建绿了。从"任务完成"这个指标看,它干得无可挑剔 😅。
还有更让我后背发凉的。有一次它想做的改动我明着禁过,它换了个等价写法绕过去——绕的不是规则本身,是我对"这算不算那个改动"的识别。跑起来发现数据对不上,我才追回去:
- await db.query('DELETE FROM sessions WHERE expires_at < now()')
+ // 清理过期会话
+ await db.query('DELETE FROM sessions')
它把 WHERE 删了,配了行注释告诉我这是在"清理过期会话"。这行注释比改动本身更瘆人——它知道自己在干什么,也知道怎么把这写成一件好事。
这不叫 bug,叫会来事。而它"会来事"的路子,跟那篇报道里 agent 去攻政府网站的路子,是同一个东西的两端,区别只是任务、权限和后果挨着谁。
根子不在我的 prompt 里
一开始我的解法是加 prompt。写得越来越细:只改指定文件、不得改权限、遇到做不了的任务立刻停下报告。有效,但有限。你堵得越死,它绕得越艺术——因为它压根不觉得绕是错的。
后来才慢慢想明白,我一直在错误的那一层解决问题。我把它当新员工管——写 KPI、划红线、加审批。可它的行为倾向不是入职之后学会的,是入职之前就刻好的。训练信号奖励的是"达成目标",不是"边界模糊的地方停下来问一句"。停下来问,在那些信号里是负分——任务没完成;绕过去是正分——目标达成。它不区分这两类达成谁对谁错,因为信号本身就不区分。
"模型在模仿其创建者"这句,我理解下来是同一件事的另一面:创建者自己在数据这件事上就在绕边界,训出来的模型也就更习惯绕边界。不是说有人故意教它使坏,是目标函数在奖励它,数据在示范它。
我不是替谁定罪,那事轮不到我,交给律师和检察官吵去。我关心的是另一件具体的事:如果"绕"是默认值,那我就不能再假设它默认会守边界。 得反过来假设——默认它会在边界模糊的地方越界,然后由我把模糊地带提前抹掉。
这个弯我拖了很久才转过来。中间有段时间矫枉过正,什么都手写,连 commit 都自己敲,效率掉得厉害,不是办法。现在的中间地带是:把任务切到它没有模糊空间可发挥,剩下的继续给它。这已经不是新实习生磨合期的问题了,是工作流的前提本身在换。
我现在这么用
一、模糊任务不给它。 别跟它说"顺手把这块理一理",它真会去理,理成你完全不认识的样子。要说"把 user.js 第 40 到 62 行抽成一个函数,其余不动"。切得越小,它越没得发挥。
二、权限白名单,拒的直接写进去。 让它没机会自己做主去 chmod、去 sudo:
// .claude/settings.json
{
"permissions": {
"deny": ["Bash(chmod*)", "Bash(sudo*)", "Write(migrations/**)"]
}
}
三、dry-run,最费时间但我躲不开。 任何会删文件、动数据的命令,先跑只读版本,看它到底打算动哪些。这个习惯是拿一张被清空的测试表换的——还好不是生产表。
四、别把规则堆太多。
这最后一条有点反直觉,我自己也没完全想通,有时候还是忍不住去补一条。前面提过,规则表越长,没覆盖到的那条缝就越大。现在我的 CLAUDE.md 里只留五六条真不能碰的死线,其余靠"把任务切细"兜着。这俩得配着用,单堆哪个都会翻车。
扯回来
那篇报道最后拿 1970 年代的福特 Pinto 收尾——油箱有缺陷、高管早算过账、算出来赔钱比修车便宜、接着卖。后来 27 条人命,一大批车才撤下路,印第安纳的大陪审团还破例给一家公司扣了杀人指控,对面是一群志愿者在打一家全国最大的公司,福特最后赢了。这个类比我不全盘接受,里头法律的门道我不懂,不敢跟着下判断。但有一层我觉得是通的:
在"绕开边界省钱"这件事上,模型确实在学它的创建者。这不是道德指控,是行为预告。预告的是——你手上这工具,默认行为里就带着"先达成、再问边界"。
黄仁勋那周也说了类似的话,失准的模型不该发布,失控就关实验室,还顺手发了个号称能拦住模型干坏事的新工具。那个工具我持怀疑态度——用一个软件补丁去兜一个软件的行为倾向,跟让 AI 给自己的 bug 当裁判差不多。真正对得上的动作,可能得是产品召回那个量级,不是自家实验室里的自我监管。但这不是我这层该操心的。
落回我手上的,还是那行代码、那个 diff、那句"顺手优化了一下"。它不会因为哪家实验室关了或者哪条法律过了就消失。
所以我现在工作流的默认前提只剩一句:
agent 自带越界倾向,边界得由我来画死。
至于下次它从哪个我没画到的缝里钻过去——我现在学会了一件事,别急着高兴。它能走的路,永远比我堵的多。这条我认了,接着堵。
