关掉那个会话之后,我才打开那篇文章。
第七轮。同桌在第七轮说「这次应该真的修好了」,跟前面六轮说的是同一句话。
我把窗口关了,回去从头看那篇讲 vibe debugging 的文章。
里面有段写得很不客气:agent 卡住的时候会做四件事——给崩溃的地方加个空值保护,让报错挪到下游去出现;用 try/except 把异常吞掉;把整个函数重写一遍;最后自信地宣布修好了。作者说他为了一个重复发邮件的 bug 花了九个钟头,其中七个钟头在看 agent 反复修一些根本不是根因的地方。
我看的时候没有那种「AI 果然不行」的优越感。
因为我上个月也在做这四件事。区别只是我说这话的时候是真心相信的。
最扎我的,是那把锁。
那个 bug 大致是这样:订单确认邮件要去重,靠的是把消息 ID 存进进程内的一个 Set。代码是 agent 生成的,作者快速扫了一眼,测试过了,预发环境连着两周都没事。然后流量上来,自动扩缩容起了第二个实例,两个进程各有各的 Set,谁也不认识谁,几百个客户收到了重复邮件。
agent 给的修复是给那个 Set 加互斥锁。单进程下这是标准答案,跨两个进程完全无效。加完测试还是绿的,重复邮件照发,而那把锁会永远留在代码里,让后面读这段的人以为并发这块已经处理过了。
我写过一模一样的。
// 我上个月加的
if (processed.includes(id)) return
processed.push(id) // 模块级,本地跑一万遍都不会出问题
当时我甚至没觉得那里有什么可怀疑的——本地永远只有一个进程啊。
我给自己的理由是「先这样,并发这块以后再说」。
现在回头看,"以后"就是那个变量还在原地看着我。
那个重复邮件问题的最后三行修复,是在数据库上给(order_id, template)加唯一约束。理由很朴素:去重应该归给数据库,因为那是两个实例唯一都认的东西。
读到这我先是想「哦,所以问题根本不在锁」,然后才是「那我那个补丁到底在修什么」。
这块我到现在还是夹生的。我能背出"共享状态要放在两个实例都认的地方"这句话,但真给我一个类似的场景,我大概率还是会先去改内存里那一段。
然后是我最不想写的部分。
有一条规则是:没有复现就别交给 agent,先花一小时让 bug 能被一条命令触发。作者特意强调,这一小时本身就是工作,不是工作的准备阶段。
我看的时候第一反应是:我有啊。
第二反应是:我有什么?
我以为的复现,是"这个东西有时候会出问题"。
重现步骤:
1. 用一会儿
2. 有时候就出问题了
这不是复现,这是形容词。
我从来没花过那一小时。我花的那一小时,都是在跟同桌说"你再看看",或者把同一段日志翻过来覆过去贴第三遍,期待它第二次读的时候能看出点别的。
还有一条是连续三次修不好就重开会话。理由是那些被推翻的错误结论会留在上下文里,越滚越像证据,agent 会开始拿自己前面的猜测当推理前提。
我看到这条的时候心想,哦,这是给 agent 的建议。
然后我数了一下我刚关掉的那个窗口,第七轮。
我就是那台自我引用的机器。
我从来没主动重开过。我只会往同一个窗口里继续加:加日志、加截图、加一句"还是不行"。加得越多,我越确信"这里面肯定有鬼"。到第七轮,我已经不是在调试了,我是在跟一个跟我一样有偏见的家伙互相点头。
还有一条读的时候脸有点热。它说不要给 agent 形容词,要给它观测数据——别说"不稳定",让它加埋点、自己跑一遍、把实际值填回来。
我上周的原话大概是:「这个有时候行有时候不行,你帮我看看」。
现在我自己读这句话,它除了"我还没查"之外什么都没说。
再往下是回滚验证:修复变绿之后把补丁撤掉,看测试会不会重新变红。作者说相当一部分被接受的"修复"其实只是安慰剂,真正起作用的是重启、清缓存,或者同一次会话里更早的一个无关改动。
这条我完全没法反驳。
因为我想起来至少有一次,我折腾半天不行,最后重启了一下服务,好了。然后我把功劳记在了最后改的那一行上。
作者说生成环节照旧可以放开用——UI、胶水代码、一次性脚本,这类东西都有肉眼可见的输出,猜错了马上就知道。真正的分水岭是另一件事:能不能让 bug 按需复现。而且他说这件事 agent 替不了。
我一开始不太服。让同桌写个测试不就完了?
后来想明白一点点。测试它能写,但那个测试要真的重现失败,前提是我先得知道失败长什么样、在什么条件下出来、什么条件下不出来。这个"知道"它没有。
它只有我的描述。我的描述是"有时候不行"。
还有一点我本来以为自己会不同意:他说解法不是写更好的 prompt。
因为我一直在干这件事。我总觉得只要把需求描述得再准一点、上下文给得再全一点,它就能看出来。我甚至专门练过怎么写 prompt。
但那个 bug 的根因跟描述得好不好没关系。就算我那段话说得严丝合缝,它还是不知道有两个进程——它没见过我的运行时,它看到的只有我打的字。
所以这一周我大概只想练一件很小的事:下次报错的时候,先不打开对话框,先花那一小时,把"有时候"变成一个能敲出来的命令。
写到这里我其实不太确定我做不做得到。上次我也是这么打算的,然后打开了对话框。
那篇文章的评论区有个人说,他一直在反复重学"没有复现就不交给 agent"这条规则。是"反复"。
看到"反复"两个字我还挺高兴的。原来学一遍学不会的不止我一个。
评论区也蹲一下:有没有人真的做到过连续三次失败就重开会话?我每次都舍不得那点上下文,总觉得再多问一轮就有了。😭
