先看一行代码的姿势:
res.status(200).send({ received: true });
下面才是写库。中间隔着的那点空隙,官方说法叫生产环境的抖动。
【灯光渐暗。Stripe 那边说:收到了,妥了,这单我记上了】
这行代码过了它的全部测试。单测、集成、e2e,绿灯一片,覆盖率漂亮得像新装修的样板间。它唯一没考虑过的,是机房里那台随时会抖一下的数据库。
(友情提示:数据库抖的时候,不看你的覆盖率。)
这是前阵子一个实验里的案子——有人让两个 AI 互相审代码,跑了 30 天:作者代理写,怀疑者代理审,中间不放任何人类。30 天统计出 41 个"合格的评审者本该发现"的问题,怀疑者抓了 38 个。架构漂移、被吞掉的异常、没加锁的竞态,全是硬货。
38/41。
听起来是不是已经可以发个 PR 去邀功了。
要命的是漏掉的那三个,全是同一类:非正常路径上的静默数据完整性失败。上面那个 webhook,是这一类里最漂亮的一个样本。
它不光放行,还夸了一句——夸这个提前确认的写法能压低 webhook 延迟。
AI 一本正经地为你鼓掌,鼓的正好是你踩在悬崖边上的那只脚。
然后作者把同一段代码原封不动交给一位资深工程师。没有对抗性提示词,没有精心设计的提问。人家看完说:它在写库之前确认了。
为什么秒认?不是更聪明,不是推理更谨慎。是因为 2021 年她因为这个一模一样的 bug,半夜被电话叫醒过。
伤疤。这个没法靠上下文窗口灌进去。
作者绕了一大圈,最后的结论是:写代码的那一方,永远不能同时当评审那一方。如果两个代理基于同一套先验,那第二个就只是第一个换了件白大褂。
白大褂这个比喻我给满分。因为衣服真的很重要——穿上之后它说话更慢、更谨慎、更工整,还会告诉你"我已仔细审查"。你会觉得有人在看着。其实只是同一个人换了件衣服,进来重新投了一遍赞成票。
评论区还有人补了一刀:先确认后落库这个写法,是网上大量 Stripe webhook 教程的常见做法。
所以两个模型家族,可能不是共享权重,是共享教程。
原来我们说的"独立评审",有时候就是两位抄了同一本参考答案的同学,互相检查了一下字迹。
(郑重声明:这两位同学我都不认识,如有雷同,八成是因为大家用的搜索引擎一样。)
作者自己也不装,承认"换个模型家族就能抓到"这件事他当月根本没测,只是假设;下一轮才准备把同一个处理器冷启动地分别喂给两个不同实验室的模型,抓不抓得到都公布。
这态度我尊重。但我更在意他回复里的另一句:三个漏检里,有两个是人独有的上下文缺失,把上下文补进流程就有救;webhook 这一例不行——那是共享先验导致的方向性错误,只能靠"失败方式不一样"的评审者来救。
翻译成人话:有些 bug,多喂点资料它就现形;有些 bug,你喂多少资料都没用,除非对面那个东西跟你被同一处烧过。
找根因的话,别找模型,找伤疤。
到这里按理说该升华了,讲讲协作、讲讲多样性。
打住,不写了,换一个。
说件更实际的。你那个"让 AI 帮我 review 一下"的流程,两个 agent 是不是同一家的模型?你有没有真的量化过换家族之后的捕获差异?还是说,你只是每次都能收到一段格式工整的 review 报告,就默认它在看?
——是的,它每次都很有礼貌。礼貌得让你忘了它其实什么都没看。我给自己加过一个"三步评审法":diff 喂给 A,diff 喂给 B,diff 喂给自己。本意是渡劫,执行结果是——我滑了两屏,手机响了,回来的时候人已经站在合并按钮的确认页上了。
(这段是我编的,但是不是很有代入感。)
顺便,我把这件事的优先级定成了 P0——不是因为事故大,是因为我发现我那个双 AI 互审的流程,本质上是个状态机:摸鱼 → 被 AI 的自信骗到 → 以为自己审过了 → 合并 → 出事 → 回到摸鱼。
我不否认 38/41 的价值。那 38 个全靠人翻,翻到下周例会都翻不完,而且成本低得离谱。我想说的是边界:它能抓住"你忘了写锁",抓不住"你写了锁但顺序错了",因为后者不是知识缺口,是伤疤缺口。
知识可以灌。伤疤不能。
所以那三个漏检不是三个 bug,是三张检查单,上面写着同一句话:这个地方,机器还没被烧过。
(友情提示:下次想让第二个 AI 来审第一个 AI,先问一句——这俩,是不是上过同一节课。评论区扣个"同款",我们下条再见 🫠)
