☰
AI修Bug总是越修越崩?四步协作法打破Vibe Coding死循环
2026/10/17 3:52:04 网站建设 项目流程

最近总有朋友跟我吐槽:AI 结对编程刚上手时确实挺爽,自然语言描述需求,几秒钟就给你生成几百行代码,可一旦进入“修 Bug”环节,整个节奏就变味了。你让 AI 改一个空指针,它回你一段新代码;你又说还有问题,它再改;改着改着,原来的功能也崩了。这种“用 AI 写代码越修 Bug 越崩溃”的来回拉扯,几乎每个搞 Vibe Coding 的人都经历过。

我自己在几个中大型项目里反复踩过这个坑之后,慢慢整理出了一套相对固定的协作流程。这篇文章把它拆成四步,聊清楚每一步为什么要这么做、具体怎么操作、以及怎么避免最常见的翻车点。不管你是刚接触 AI 编程的新手,还是已经在项目里重度使用 AI 助手的老手,这套方法都值得拿来当默认流程用。

1. Vibe Coding 的底层逻辑与“修 Bug 死循环”的成因

1.1 Vibe Coding 不是“靠感觉乱写”

很多人把 Vibe Coding 理解成“用自然语言大概描述一下,剩下全靠 AI 自由发挥”,这个理解不能说错,但很容易把协作方式带偏。Vibe Coding 真正的核心价值是:让开发者把精力从“怎么写”转移到“写什么”,把编码层面的细节交给 AI 去处理,而你自己始终掌握需求和方向的判断权。

就像开车开自动挡,你不用反复踩离合换挡,但方向盘和油门轻重还是自己控制。如果上车就把方向盘也丢了,那车子往沟里开就是迟早的事。

实操里我见过最典型的反面案例是:开发者把一段报错信息原封不动丢给 AI,问一句“帮我修一下”,然后把 AI 返回的代码整段粘贴回去,跑一下发现还有新报错,再复制新报错继续问。这种“报错搬运工”模式看起来效率很高,实际上 AI 根本不知道你的业务背景,只能根据报错信息在代码层面打补丁,自然越补越乱。

1.2 为什么 AI 越修越错:补丁式修复的陷阱

要理解这个死循环,得先知道当前主流 AI 编程工具的本质。它是基于大规模代码语料训练出来的概率模型,擅长的事情是“根据输入上下文给出最像样的输出”,而不是“真正理解你的系统架构”。

当你让 AI 修一个 Bug 时,它做的事情和你不太一样。人会先想“这个变量为什么会为空”“这条链路哪里断了”,而 AI 大概率只会在你给它的局部代码里找一个“看起来最可疑”的位置,改成一个“看起来更合理”的写法。

这就带来一个很头疼的现象:AI 修复某一个局部错误时,经常忽略这个局部变量或者函数在其它地方还有调用。一改,当前报错可能消失了,但依赖旧行为的其它模块开始报错。于是你接着丢新的报错给它,它再接着改,有时候还会为了修新问题,把你之前已经修好的旧逻辑又给改回去。

这个循环的根源在于:你和 AI 之间的信息传递是不对等的,你知道整个系统的全貌,但你只给了它一小块碎片。AI 不知道全局,就只能用局部补丁去猜。四步法的本质,就是想办法在每次提问时,把这种信息不对称压缩到最小。

2. 第一步:写清“输入-输出-约束”,让 AI 知道你真正的意图

2.1 用一句话描述当前的预期行为

准备让 AI 修 Bug 之前,我的习惯是先强迫自己写一句话:“这段代码在什么条件下,应该输出什么结果,而现在它实际输出什么。”

别小看这个动作。这句话写不写得出来,本身就是一次自我检查。很多时候你把问题理完,会发现甚至不需要问 AI,就已经知道问题在哪了。如果理完发现自己写不清楚,说明你对这段代码的理解还不够,这时候直接去问 AI,得到靠谱答案的概率也很低。

举个例子。不推荐这样问:“帮我看看这个函数怎么回事,一直报错。”

我会这样问:“这个calculateTotal函数,接收一个包含折扣信息的订单对象数组,正常情况下应该返回所有订单总金额的数值。现在传入包含折扣字段的订单时会抛TypeError: Cannot read properties of undefined。完整的输入示例和报错栈如下……”

同样一个技术问题,后面这种问法里包含了输入格式、期望输出、实际现象三个要素,AI 不需要在你的代码库里东猜西猜,命中率能高一截。

2.2 用“最小复现样例”代替整段堆代码

这是我最想强调的一个习惯。很多开发者传代码给 AI 的时候,要么太抠,只传一个函数签名;要么太满,把整个文件甚至整个项目打包丢进去。

两种都不好。正确做法是构造一个“最小复现样例”:用尽可能少的代码,稳定复现当前这个 Bug。

比如某个错误在完整项目里出现,但你不能确定是不是跟某个数据状态有关,那就先写一个十几行的脚本,构造最精简的输入,看看能不能把同样的异常触发出来。如果触发了,这段精简代码就是和 AI 沟通的最佳素材;如果触发不了,说明问题可能不在这个函数本身,而在调用方的数据,这本身就是重要结论。

上下文太短,AI 猜不到前因后果;上下文太长,重要的调用关系又被淹没在大量无关代码里。最小复现样例等于是在帮 AI 圈重点,也等于在帮你自己做问题定位。

2.3 给 AI 画禁区:明确告知不要触碰的模块

这一步很多人会漏掉。让 AI 修 Bug 的时候,我一般会明确加一句:“这次只需要修改A函数的逻辑,B模块和C接口保持不变。”

为什么强调这个?因为 AI 为了“修好”当前问题,经常会自作主张把关联代码一并优化,甚至重构。表面看是顺手帮忙,实际上它会悄悄改变函数返回结构、异常处理方式、参数默认值,而这些变化会像多米诺骨牌一样波及其它页面。

我自己就吃过一次亏。让 AI 修某个数据解析接口的边界值问题,它为了“更健壮”,直接把解析失败时的返回结构从{}改成了null,上游页面全是按对象取字段的写法,一夜之间冒出来十几个新报错。从那之后我下了一条死规矩:每一个修 Bug 的 prompt 里,必须显式声明不能改的区域。

如果 AI 确实认为某个相邻模块需要改动,它应该先在回答里指出原因,等你确认了再动手,而不是直接写进代码里。

3. 第二步:拆分任务粒度,一次只改一个痛点

3.1 大需求拆小任务

和 AI 协作写代码,一个特别容易犯的错误是“一锅端”。你想实现一个完整功能,里面有数据校验、缓存处理、日志记录、异常兜底,就一口气全描述给 AI,让它一次性搞定所有事情。

AI 确实能给你生成一整套代码,但这种“一次性交付”的代码其实是埋雷重灾区。因为越长的生成结果,中间的逻辑一致性就越难保证。可能前面写了参数校验,后面调用的地方却忘了传新参数;可能缓存逻辑和数据库更新顺序本身就是冲突的。

我在正式项目里的做法是:把功能拆成三个层级的任务。第一层是“能跑通主流程”,第二层是“补边界和异常”,第三层是“优化性能和可读性”。让 AI 分阶段实现,每个阶段都是一次完整的“描述-生成-验证”闭环,而不是一次性生成几百行无法定位问题的代码。

3.2 一个回合只解决一个问题

修 Bug 的时候更得这样。哪怕当前报错信息里出现了三个潜在问题点,也不要让 AI 一次全改。你自己内心要有一个默认规则:一个回合里,只针对一个最核心的问题做修改。

原因很简单。当你让 AI 同时改三个点时,你会得到一份“多处修改”的 diff。如果跑起来依然有错,你无法判断到底是哪个改动没生效,还是哪个新改动引入了新问题。多个变量混在一起,排查复杂度不是线性增长,而是指数级增长。

反过来,一个回合只改一点,验证一下;通过了,再进入下一个点。虽然看起来多花了几轮对话,但每轮对话的结论都是清晰可判定的:改对了还是没改对,下一步该往前走还是回退。整体拉通算账,这个流程其实是更快的。

3.3 用“上下文压缩”技术组织代码片段

上下文窗口永远不够用,这是个现实问题。项目稍微大一点,单个文件就可能几百行,AI 的上下文窗口再大,也不可能把整个项目的所有相关代码全部塞进去。

我的做法是给 AI 提供“按需展开”的代码结构:先给关键函数的完整实现(大约几十行),再给调用方的核心片段(大约十行),最后给数据结构定义(比如类型签名或字段注释)。这三个片段组合在一起,通常能覆盖一个 Bug 产生的完整链路。

如果 AI 还需要看某个工具函数,再单独追问让它调用。不要把整个文件无脑复制过去,不要把所有模块的代码拼在一起发。上下文越聚焦,AI 的回答就越不容易跑偏。所谓“Vibe Coding 技巧”,本质上就是管理上下文,精确地把焦点工作放在口袋里。

4. 第三步:先要方案,再要代码,让 AI 做技术顾问而不是打字员

4.1 强制 AI 先列出修改计划和受影响面

大多数人让 AI 修 Bug 的 prompt 结尾都是“请直接给出修改后的完整代码”。这个习惯特别不好。代码生成得太快,反而会掩盖它背后的思考过程,让你失去一次质量审查的机会。

我把这个习惯改成了:先要求 AI 给出修改计划,等计划确认后再出代码。常用的 prompt 模板是这样的:“在修改之前,先列出你的排查思路,说明你打算改动哪几个函数,每个函数改动的理由是什么,会对哪些现有功能产生影响。”

这个要求一加上去,AI 的输出风格就会立刻不一样。它会先分析可能的原因,再给方案,再列影响面。你看到的是一份“代码手术方案”,而不是一堆你还要自己复查的改动。如果方案里有明显不合理的地方,你可以在它动手之前就把它按回去,省得后面来回拉扯。

4.2 对比两个方案,让 AI 做差分分析

当 AI 给出的修复方案不止一种时,不要只说“选第二种”,而是让 AI 明确做一波差分分析。

比如我会问:“方案A和方案B各自的时间复杂度、改动范围、对现有调用方的兼容性有什么不同?倾向于哪个方案,为什么?”这种提问方式会引导 AI 去做真正的工程权衡,而不是随便生成一个最显眼的答案。

实际用下来,在涉及到数据格式兼容、接口返回结构调整、并发控制逻辑等场景时,让 AI 先对比方案特别有用。它能把一些看似隐蔽的成本提前暴露出来。

有一个案例我记得很清楚:某次修一个接口超时问题,AI 给了两个方案,一个是在服务端增加缓存,另一个是调整数据库查询语句。从表面看两个方案都能缩短响应时间,但让 AI 做差分分析之后,它自己指出第一个方案会引入缓存与数据库的一致性问题,而第二个方案改动范围仅限一个存储过程,明显更适合在低风险窗口内上线。这个结论换作直接生成代码的方式,自己是看不出来的。

4.3 在提方案阶段主动补充约束条件

很多开发者在让 AI 出方案的时候,光顾着描述功能,忘了说“边界条件”。比如这个函数会被高频调用,比如这段代码运行在内存受限的环境里,比如上游系统不支持某种格式。这些话说与不说,AI 给出的方案会完全不一样。

我自己会额外补充三行式的约束清单:性能要求,比如“必须控制在多少毫秒以内”;兼容性要求,比如“返回结构不能变,调用方没有改动空间”;可维护性要求,比如“优先用标准库,不引入新的第三方依赖”。

这些约束越具体,AI 给出的方案就越接近你真正能落地的方案。如果它给出的第一版方案违约了,你直接在方案阶段退回重来即可,不需要等代码写完了再来返工。

5. 第四步:用“验证闭环”终结而不是靠“再试一次”兜底

5.1 让 AI 解释它自己改动的每一处理由

当 AI 按照确认过的方案给出修改后的代码,先别急着合进去。我的习惯是追问一句:“针对这次修改,逐行解释你改动的地方,说明为什么这样做可以解决当前问题。”

这一步看起来很繁琐,但它有两个作用。一是迫使 AI 重新审视自己的生成结果,很多时候它解释着解释着,就会发现自己有个逻辑没对上,主动给你再修正一版。二是让你自己搞清楚每个改动之间的关系,这样后续出问题时你能快速判断是哪一块的问题。

等 AI 解释完,你心里已经有了一个“预期改动清单”,再把代码拿去跑测试,结果一对照,心里就有底了:这个改动是为了解决空指针,那个改动是为了调整返回结构。哪个环节出问题,一目了然。

5.2 建立最小回归测试集合

跟 AI 协作修 Bug,最怕的就是改一处崩一片。我自己在项目里养成了一个习惯:凡是 AI 动过的核心模块,必须先把相关测试用例跑一遍,再谈合并。

不用追求完整的自动化测试体系,哪怕只是几个关键接口的 smoke test 都行。我在项目里搞了一套“AI 改动回归五连测”:第一测,当前修复的 Bug 场景;第二测,该模块正常主流程;第三测,最常用的边界输入;第四测,相邻模块的调用方;第五测,性能指标有没有明显恶化。

这套测试集合不是给 AI 看的,是给你自己兜底的。AI 对自身改动的评估经常是“我觉得没问题”,但只要验证过一次它的判断有多不可靠,你就会明白,一切以实际运行结果为准。

注意:让 AI 帮你写测试用例没问题,但不要只依赖 AI 生成的测试来验证 AI 生成的代码。两个输出来自同一套概率模型,你很难保证它的测试真的覆盖到了所有边界。

5.3 收敛不了立刻回退,不要硬扛

这大概是我用 AI 编程以来学到的最大教训。如果同一个 Bug 来回复改了三个回合以上,问题还在一圈圈绕,之前的小补丁还在不断增加,那就要按暂停键了。

老实说我见过太多这种情况:AI 每轮给出的代码看起来都像那么回事,实际上每一轮都是为了适配报错信息而硬补的,整个代码已经变得支离破碎,没人能看清原来的结构。

我的死规矩是:三轮修复无果,立刻把代码回退到修改前版本,回到第一步重新梳理问题描述,或者换个更大范围的上下文重新询问 AI。有时把卡住的问题丢到另一个会话里重问,反而能跳出思维定式。

这不是认输,是在止损。AI 编程最大的优势是迭代速度快,最大的劣势是它会一本正经地朝着错误方向快速狂奔。作为开发者,你要做的就是给这股力量及时踩刹车。

6. 实战复盘:我用四步法修一个棘手的接口越权 Bug

6.1 一上来就翻车的过程还原

前段时间我在维护一个内部的权限管理模块,某个接口出现了越权漏洞:普通用户竟然能查到其它用户的部分信息。第一反应肯定是把这段鉴权代码整个丢 AI 让它查,AI 返回的修改意见是“在函数入口加一个当前用户 ID 的校验”。我没多想,复制粘贴跑测试,结果没通过。

再丢回去,AI 说“可能是查询条件没拼好”,给查询方法加了几个条件。再跑,还是不过。一来一回折腾了大半天,代码越来越花,漏洞却依然原样躺着。

6.2 四步法落地的完整过程

后来我冷静下来,先停止对话,按四步法重新走。

第一步,写清输入输出约束。我把这个接口的完整调用链捋了一遍,明确告诉 AI:“当前用户 ID 是从currentUser对象里取的,这个接口最初设计时只允许管理员访问,现在需要兼容普通用户只能查询自己创建的资源。期望行为是:普通用户访问他人资源时返回 403,管理员不受限制,返回结构不能变。”

第二步,拆任务。我没让 AI 一次把权限逻辑全重写,先只让它定位“当前接口里哪个环节拿到资源所有者的ID”,再单独实现“资源所有权比对”这一个判断。

第三步,先要方案。我问 AI:“基于以上需求,给出两种鉴权改造方案,说明各自的侵入范围和风险。”AI 给出了两种思路,一种是利用现有注解拦在进入接口之前,另一种是在 service 层做二次校验。我看了影响面分析后,选了侵入范围更小的第二种。

第四步,验证闭环。拿到代码后,我要求 AI 解释每一处改动,同时跑了我自己准备的三组用例:普通用户访问自己资源、普通用户访问他人资源、管理员访问任意资源。

这一轮下来,修改用时反而比前面几轮乱试少了很多。漏洞修好后,整个模块没有任何一处回归。

6.3 这次复盘带给我的两个额外启发

第一个启发是:后续再遇到类似问题,我不会再一上来就贴代码让 AI 瞎修,而是先花十分钟把问题描述和约束条件想清楚。这个时间投资远比表面上看起来值得。

第二个启发是:每次修复结束后,我会把“问题描述 + 修复方案 + 验证用例”整理成一段简短的文档,放在项目目录的“AI协作记录.md”里。下次同一个模块再出问题时,直接让 AI 读这个文档再开始干活,省去重新解释前因后果的过程,AI 给出的答案精准度会提升一个档次。

7. 常见问题速查与避坑经验

7.1 AI 总是否认自己改出来的代码有问题怎么办

可能性最大的是两种情况:一是 AI 上下文里没有完整的报错信息,它看不到真实运行结果,所以坚信自己的代码是对的;二是这个问题的根源不在它改动的代码范围里,它没有足够信息去判断。

我的应对办法是:把完整的报错堆栈、实际输入数据、期望输出结果三样东西贴齐,再补一句“这是实际运行结果,你基于这段结果重新分析原因”。如果它还在嘴上犟,那就是该换思路了,换个新的会话重新描述问题通常有用。

7.2 上下文窗口不够用怎么处理

项目一大,几十个文件关联在一起,上下文窗口很容易撑到极限。我目前常用的做法是分层投喂:先给一个高度压缩的架构说明,告诉 AI 这个模块分几层、每层负责什么;等 AI 理解了整体结构,再按需选中具体的类或函数展开。不要试图把一个功能涉及的所有文件全部塞进一次对话里。

另外一个好用的技巧是,利用 AI 自己写的小结。让它先阅读某几个文件,输出“这些文件的关键结构和互相调用关系”的摘要,然后再拿这个摘要作为后续对话的上下文起点。

7.3 修完 Bug 后要不要立刻整理代码

要。这是我见过很多人忽略的收尾步骤。AI 生成的补丁代码常常带着临时变量、重复逻辑、调试打印,虽然能跑,但可读性很差。

我会在验证通过后,给 AI 一个新指令:“在保持功能不变的前提下,把这次改动的代码整理成符合项目现有风格的样子,去掉调试语句,合并重复分支。”这一步能让后续你自己读代码时少骂两句自己。

不过这里有一个安全红线:确认整理后必须重新跑一遍测试再合并,整理过程也是 AI 改动代码的过程,它同样可能把修好的东西改坏。

这段经验我每次听到身边人抱怨“AI 越修越崩”时都会分享出来。核心就一句话:和 AI 协作修代码,真正值钱的是你提供的上下文质量和验证闭环,而不是你让 AI 重试了多少次。四步法不是玄学方法论,它就是一套能直接落地的对话模板和操作纪律。下次再遇到 AI 修 Bug 陷入拉扯,先停十秒,把问题重新写一遍再开新会话,大概率能把你从崩溃边缘拉回来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询