vibe coding
Vibe Coding 问题解决篇:一个bug反复解决不了怎么办
Vibe Coding

问题解决篇:一个bug反复解决不了怎么办

同一个 Bug,AI 已经改了三轮。

原来的问题还在,旁边两个功能又坏了。

这个时候继续说“接着修”,往往就是项目真正失控的开始。

因为连续几轮都没解决,说明前面的原因判断、修改位置或者验证方法,至少有一项出了问题。

它可能还没找到最先出错的位置,也可能找到了现象,却把代码改在了不真正负责这件事的地方。

你再催,它就会继续加判断、加兼容、加绕路。

Bug 还在,代码先被补丁糊满了。

一天一个 vibe coding 小技巧。

今天就讲清楚,同一个问题反复修不好时,怎么让 AI 真正停下来查原因。

一共四步,每一步该说的原话,我都直接给你。想要整理好的完整版,看到最后我告诉你怎么拿。

一、先说清楚,什么时候该踩这脚刹车

先说清楚这个“三”。

它不是什么强制标准,但也不是我一个人的习惯。

国外做 AI 编程的人给这种状态起了个名字,叫死亡循环。普遍的建议就是:同一个问题让 AI 改到第三次还没好,就别再让它试了。

道理很朴素。连续三轮都没解决,说明它对原因的判断从一开始就错了,再来第四次,只是换个写法继续碰运气。

那什么才算一轮?

一轮完整的处理,至少包含三件事。

AI 提出一个明确原因,针对这个原因完成一次修改,再重新走一遍原来会出错的过程。

如果问题仍然存在,这才算一轮失败。

同一个问题连续三轮还是没解决,我就不再允许它继续试。

有时还没到三轮,也该提前停。

比如每改一次,都会让新的功能跟着坏掉。

或者 AI 已经说不清前几次为什么失败,只是在换一种写法继续碰运气。

这些现象都说明,局部修改已经失去方向。

这时候最不能说的,就是“不管用什么办法,先给我修好”。

目标压得越死,AI 越容易为了交差,先把报错藏起来。

藏起来长什么样?它可能在出错的那一段外面裹一层,让程序即使失败也不吭声;也可能干脆把那个报错提示删掉,页面上换成一句“加载中”。

你一看,红字没了,以为修好了。

实际上那一步根本没跑成功,只是从“报错”变成了“悄悄失败”。

等你发现的时候,往往是用户来问,我的订单怎么一直没提交上去。

页面暂时不报错,不等于真正的问题已经解决。

所以踩下刹车的第一句话,不是“接着修”,而是让它先停手:

「停止一切代码修改,本轮不再以“先把功能做出来”为目标追加补丁、兼容分支和绕行逻辑。」

二、第一步:换个新对话,再补一份真源

停手之后,还有个动作很多人不知道要做:把当前这个对话关掉,重开一个。

因为聊到这会儿,这个对话里已经堆满了三轮失败的尝试、被推翻的判断,还有你们互相矛盾的指令。

AI 每次回答都会把前面这些一并读进去。

它于是被自己错过三次的判断带着走,你越催,它越是在那条错路上打转。

换个新对话,等于把这些干扰全部清掉。

代价只是你要把必要的信息重新交代一遍——而下面这几步,正好就是在交代这些。

新对话开好,第一件事不是让它查。

它得先知道这个功能原本应该做成什么样。

当前阶段要完成什么,哪些行为不能改变,页面、接口和数据之间怎么配合,什么结果才算通过。

这些东西写下来,就是当前阶段的真源。

它不是拿来限制排错的死答案。

而是让 AI 在检查现有代码时,知道哪些地方已经偏离了原来的设计。

如果你只说了两句话就让 AI 开始干,需求和边界一直靠它猜,那项目越改越乱并不意外。

但也不用因为过去没写文档,就把整个项目扔掉。

让它根据已经确认的需求、现有代码、真实运行结果和 Git 记录,把当前阶段重新整理出来:

「依据现有需求文档、阶段记录、当前代码、真实运行结果和 Git 历史,重建该功能当前阶段的真源。内容包含:本阶段要解决的问题、正常流程的预期运行路径、不得变更的现有行为、判定完成的验收标准。」

这里还要追一句,不然它会把猜的和真的混在一起写给你:

「逐项区分三类内容:已由文档与代码互相印证的事实、文档与代码存在冲突之处、需要我确认的开放问题。本轮只交付阶段真源,不进入缺陷修复。」

哪些是你明确要求过的,哪些是代码现在真的在做的,哪些只是它的推测,必须分开。

你确认这份真源,才进入下一步。

三、第二步:把现象钉死,再翻出前三轮

真源确认以后,才开始查。

这一步只干两件事:把问题钉死,把老账翻出来。

先说钉死。

最好能找到一套固定操作,让问题每次都出现。正常结果该是什么,实际又发生了什么,一并写清楚。

「先固定复现路径:写明操作步骤、预期结果和实际结果。本轮不修改任何代码。」

但有些问题不是每次都犯。

它可能挑时间、挑数据,也可能网络一抖或者第三方服务一故障才出来。

这时候最怕 AI 为了凑“稳定复现”,给你编一条根本没发生过的路径。

所以要补一句:

「无法稳定复现时,记录发生时间、操作步骤、输入数据、运行环境、发生频率和相关日志,另外定义一套修改前后都能重复执行的观察标准,不得以虚构的复现路径充当稳定复现。」

观察标准就是:连续运行多少次,出现什么日志、什么返回结果或者什么页面现象,就算问题又犯了。

这样即便它不是每次都出现,修改前后仍然有同一把尺子可以比。

接着是翻老账。

前三轮改了什么,必须全部摆出来:

「列出前三轮各自的原因判断、修改内容、验证结果,以及未能解决的原因。」

这一步很重要。

它能阻止 AI 把已经失败过的办法,换个说法再做一次。

老账翻完,再让它顺着整条链路找第一个出问题的地方:

「围绕当前问题审计完整调用链,定位第一个可观测到异常的边界,用它收敛排查范围。该边界不等同于根因,异常在何处产生仍需由证据判定。」

举个例子。页面提交的数据本来是对的,到了接口以后才变错,那就继续查接口。

如果接口返回已经正确,页面显示时才出问题,就不该转头去改数据库。

注意最后那半句:找到这个位置,只是把范围缩小了。

它告诉你问题最早在哪里露出来,还不能证明这一层就是根因,更不能马上把责任推给这一层。

四、第三步:逼它拿出根因,别信“听起来很合理”

查到可疑位置以后,AI 往往很快就说“已经找到根因了”。

先别急着信。

有些问题只有一个原因。

还有些问题,是特定数据、程序状态、网络或者第三方服务碰到一起才会出现。

所以它找出来的,可能是一个原因,也可能是一组缺一不可的原因。

不管哪一种,你都用同一把尺子卡它——同时解释三件事:

「根因必须同时解释三点:为什么出现当前症状、为什么最早在这个位置观测到异常、为什么前三轮修改都没有解决。三点不能同时解释的,不得宣布根因成立。」

只解释其中一个,它仍然只是猜测。

光有这一句还不够,得让它把所有可能摆到台面上:

「建立原因假设表,每条假设列出支持证据、反证、下一步排除方法和当前状态。判定为多条件共同触发的,列出该组条件,并设计逐个移除条件的验证方法。」

逐个拿掉一个条件看问题消不消失,才能判断这些条件是不是真的缺一不可,而不是碰巧一起出现。

如果问题涉及框架、依赖或者第三方服务,还要让它去核对官方说法:

「核对当前版本的官方文档、变更日志,以及 GitHub 上已确认的问题和修复讨论。」

接下来是这一步里最容易被跳过的一件事,也是最省钱的一件事。

支付、短信、AI、地图、文件存储这些能力,并不完全跑在你的项目里。

你的程序只是把请求发到服务商那边。

所以功能突然失败,问题不一定出在你的代码。

服务商可能正在故障。也可能是账号被停用、余额或额度用完、密钥失效、允许访问的域名或 IP 变了,又或者请求次数已经到上限。

可 AI 眼前看见的,往往只是自己程序里的一次调用失败。

它要是默认问题一定在代码里,就会反复改参数、换接入工具、加重试。

外面的故障没消失,自己本来跑通的接入代码反而被改乱了。

所以在它动第三方接入代码之前,先让它给你一张你自己去点的清单:

「将服务商侧故障单独列为一条原因假设,并输出一份由我手动执行的检查清单,覆盖管理后台中的服务状态、账号状态、余额或额度、密钥权限、域名与 IP 访问限制、请求频率限制、准确错误码、故障时间和可见的服务商日志;服务商提供官方状态页面的,一并给出查看位置。我只回传检查结论和已脱敏的错误信息,不提供密钥原文。上述核对完成之前,不修改第三方接入代码。」

这张清单你自己打开后台,一条条看过去就行,不需要懂代码。

这一步的最后,才轮到方案:

「只输出一份针对已证实原因、可验证的最小修复方案,写明变更位置、判定这些位置应当负责的依据、必须保持不变的功能,以及问题已解决的证明方式。同一原因确需跨文件调整的,可归入同一修复单元;本单元范围以外的问题不处理。本轮不修改代码,经我确认后再实施。」

方案看不懂,就让它用大白话重讲一遍。

如果它连“为什么前三轮都失败”都说不清楚,这份方案还不够让你点头。

五、第四步:只改证实过的那一处,改完自己验

方案确认了,也不是放开让它随便改。

动手之前先存个退路。

让它列出准备保存的文件、还没提交的修改,以及没被 Git 记录的文件,把存着真实密钥和配置的文件、生产数据、用户信息和不该进仓库的日志排除掉,再存一个能退回来的检查点。

当前这版本来就有毛病,就明确标成“问题修复前”,别假装它是个稳定版本。

改之前,还要先把“坏的样子”留下来。

跑一遍原来的复现过程,或者按前面定好的观察标准跑一轮,把失败结果存下来。

不然改完你没有对照物,只能听它说“好了”。

这两件事加上后面的验证,可以一次说完:

「执行已确认的最小修复方案,不扩大变更范围。先核对 Git 状态,列出待提交与待修改的文件,排除存放真实密钥和配置的文件(具体文件名按本项目所用框架的约定判定)、生产数据、用户信息和日志,随后创建一个标注为“问题修复前”的检查点。

修改前先执行已确认的复现流程;无法稳定复现时,按观察标准留存修改前证据。

变更范围限于已证实原因确需调整的位置,不顺手整理无关代码,不顺手重做周围功能。

修改完成后重新执行完全相同的复现流程或观察标准,再验证本次改动可能波及的正常功能。

将修改前后的证据、受影响功能的验证结果和实际变更内容写回审计文档。

任一项未通过即停止追加补丁,携新证据返回原因假设表重新判定。」

最后那句是这一步的关键。

原来的错误消失了还不算完,还要看这次改动有没有把别的地方带坏。

只有原问题通过、受影响的功能也正常,这次修复才算成立。

如果结果还是不对,就把新证据补回文档,回到假设表重新判断。

不能假装前面那个根因判断没发生过,让它接着往代码上堆东西。

六、最后

一个 Bug 难修,不代表整个项目都不行。

真正危险的,是你和 AI 一起进入“再试一次”的循环。

改一次,猜一次。

再坏一个地方,就再补一层。

最后谁都说不清,项目为什么还能运行,又为什么随时可能出问题。

三轮以后停下来,不是认输。

是把“碰运气改代码”,重新变成“拿证据解决问题”。

那四步你记住顺序就行:

第一步,让它停手,换个新对话,先补一份真源。

第二步,把现象钉死,翻出前三轮失败记录。

第三步,逼它拿出能同时解释三件事的根因,还要先排除服务商那边的问题。

第四步,只改证实过的那一处,改完自己验一遍。

当你学会在 AI 乱试以前踩下刹车,难缠的 Bug 就不再只能靠运气解决。

该内容为@openbeetles原创内容,未经授权,禁止转载,违者必究!