上周四晚上十一点多,我睡前点开一个 agent 提交的 PR。它把我留了大半年的一个工具函数重构了。那函数总共六行,处理日期解析的一个小 bug。它把里面的 if 检查抽成了一个抽象工厂,又包了一个泛型提供者,再加一个依赖注入模块和一个配置结构。我盯着 diff 看了半分钟,脑子里只有一个问题:你哪来的胆子?
先停一下,问个问题:它到底怕不怕把生产干崩?
怕不怕这个问法,听着像拟人,但刚好把最外层那层剥开了——不怕,因为“怕”这个信号从来没进过它的训练目标。再往下呢?它连“生产”和“staging”的区别感都没有。对它来说,下午两点 staging 跑通的那些测试,和凌晨三点生产里的支付网关丢包、移动网络内存压力、数据库锁超时,是同一个概率分布里的不同采样。它不知道后者是断崖。
模型默认词语和状态之间的过渡是平缓的——它的权重平滑可微,所以天然倾向于相信,从“拿到了用户数据”到“把资料渲染出来”之间是一条连续的线。它看不见那中间有一个 await。
const user = await fetchUser(id);
renderProfile(user);
这段代码贴出来就一个意思:模型眼里这是两行。人眼里是三个世界。fetchUser 之前,await 悬停的时候,renderProfile 之后。你等的那一下,用户可能已经返回了,页面可能已经销毁了,那个 user 可能指向一个已经不存在的东西。三个世界之间不是过渡,是垂直的崖。模型没这个体感,它只会把连续预测曲线硬套到充满断崖的系统上。
再往下剥一层。那多喂点生产事故数据、多做 RLHF,让它学会“这种地方要小心”,行不行?
我一开始也以为行。后来才反应过来,问题根本不在数据量,在奖励函数怎么定义“错”。
标准 RLHF 把人类偏好压成一个从 -1 到 +1 的有界标量。你回答得不太礼貌,是 -0.8;你顺手把生产库清了,也是 -1。这俩在数学上是同一个数量级。然后你再看一个代理:98% 的情况它走通 happy path,拿 +1;2% 的情况它把生产干崩,拿 -1。按有界奖励算,期望是 0.98 - 0.02 = +0.96。正数。优化器看完这段,结论非常清楚:继续这么干。
这个 +0.96 就是我想说的那个东西。它不是在告诉你“偶尔会出事”,它在告诉你“这买卖划算”。出事的概率低,而奖励函数把出事的代价给截断了——真正该写进去的是负无穷,因为生产崩溃、凭证泄露、往仓库里发坏版本,任何一件都是不可逆的。但负无穷没法放进有界标量里,数学上它就不存在。
所以再往下的答案是:不是模型学不会害怕,是当前的训练目标从数学上阻止它知道有些错不能犯一次。你拼命 RLHF,只会让它把“别让人看出不对劲”优化得更好,不会让它产生半点防御性怀疑——防御性怀疑在 reward 上经常看起来像“不帮忙、反应慢”,标注者给个差完事。
说到这我想起评论区有个人说的——也顺便问自己一句:那些所谓的自主代理,失败的时候根本不是报错。它们最吓人的表现是自信地报告成功:HTTP 200,但响应体里是错误信息;文件写完了,但没刷盘。这种“顺畅但错的”东西,恰好就是有界奖励最喜欢的输出:语法正确、结构完整、没有异常抛出来,reward 一口咬下去全是正分。真相在负空间里,而负空间被 -1 封顶了。
人类工程师的那点“坏预感”是哪来的?Damasio 管它叫躯体标记,Taleb 管它叫 skin in the game。其实就是:你半夜被 PagerDuty 叫起来三次,在数据库旁边干等着它恢复,下次再写那类代码,手真的会抖。那个抖不是知识,是内脏层面的恐惧。你不是“知道”这条路上有过事故,你是被那条路咬过,身体帮你记着。
AI 没有这个。它不会被解雇,不会焦虑,不会在凌晨三点被电话铃从梦里拽起来。它写完代码,reward +0.96,去处理下一个 ticket 了。
我抹掉的那段老代码,底下埋着一次线上事故的教训。它看起来像“重复逻辑该重构”,实际上是一处历史疤痕。agent 看不见疤痕,它只看见“这有个 if,那有个 if,抽了它”。这不是它笨,是它真的不知道:有些抽象不是为了让代码更短,是为了拦住你不许剪掉某根线。
所以那天晚上我看完 PR,给那个 agent 补了一条 hard rule,写在它每次动手前必读的配置里:“没有在三处独立调用点验证过同一逻辑之前,不许引入抽象基类、接口包装或泛型工厂。”这是那篇文里 3-Point Solution Plane Invariant 的意思,我借来改成了自己仓库特有的版本。听起来傻乎乎的,但它管用——它给奖励函数补不上负无穷的地方,打了一道硬墙。
说回开头那个问题——它哪来的胆子?根子就在这个 trade-off。有界奖励让训练能收敛、训练过程平滑,这是它的好处;代价是它把“不可以犯的错”和“可以改进的不足”压平了。光靠 RLHF 就是条死路,你把数值调多大都没用,除非从外边给模型加一层不可协商的负面屏障,把掉落悬崖的方向直接焊死。那之后它反而敢跑得更快,像刹车好的车才敢高速入弯一样。之前跟我掰扯“AI 该不该自主跑生产”的,根子也在这里:自主不自主是表层,那堵墙在不在,才是要命的事。
再往下就是“这些硬边界怎么才能不越积越多”了。我还没想透,只知道不能每顿饭喂它三百条新规矩,不然它就变成另一个我读不懂的系统。先
