资讯动态

别让AI瞎改代码:把报错缩成最小例子,效率翻倍

发布时间:2026/10/15 7:37:24 来源:尧图企业网站定制
我一直觉得让AI帮你改代码这件事最讽刺的一幕就是你看着它改完第十轮代码比刚开始那一版还多了一千行报错却从一个变成三个最后你关掉对话窗口默默按下git回滚。这种“AI改代码越改越乱”的现象几乎每个把AI辅助开发当日常的同事都遇到过。以前我也觉得这玩意儿就是个玩具直到后来我认真琢磨了一阵子发现一个特别朴素但极其有效的破解办法——别把报错丢给AI先把报错缩成一个小例子。这个思路听起来像是废话但执行起来真的能救命的。我现在的习惯是只要AI在同一个问题上连续改了两轮还没收敛我就立刻喊停先花十几分钟把报错相关的代码剥离出来当成一个独立小标本再喂给它。你可以把AI理解成一个特别认真但特别容易分心的大夫你带一个浑身是病的病人进去它反而不知道该开什么药但你带一个只有某个指标异常的小样本让它看它通常能给出非常精准的处方。这篇文章我想把这件事讲透AI为什么越改越乱、怎么把报错缩成最小例子、剥离代码的具体操作步骤以及我在实际过程中踩过的坑。不管你是刚接触AI编程的新手还是已经被AI折磨过很多轮的“资深经验者”这套方法都应该能帮你把AI的可用性往上拉一大截。1. 先搞清楚一件事AI为什么越改越乱很多人遇到AI越改越乱的情况第一反应是“AI太笨了”“模型能力不行”。但我自己用下来的真实体感是多数情况下AI不是不会改而是它在错误的地基上做修改或者说它根本就没弄明白你到底要它改什么。1.1 上下文越长AI的注意力越容易被稀释如果你把一个2000行的项目文件直接扔给AI说“帮我修一下里面的报错”模型确实会把2000行全都读进去但它在生成修改建议时真正能做到“重点关照”的其实就那么几百个token。你可以类比成让你在200页的书里找错别字——你肯定能找到但你改着改着很容易把本来对的句子也顺手改歪了因为你的注意力被大量无关内容稀释了。AI也一样。它在面对一个巨大的代码库时经常会出现两种情况一种是没有注意到某处关键逻辑直接改动了一个看似相关但实际无关的函数另一种是改到一半自己都被自己绕糊涂了开始重复输出前后矛盾的补丁。尤其是当你把好几轮报错消息、好几个文件的代码全部堆在同一个聊天窗口里上下文一复杂模型出错的概率会呈指数级上升。1.2 局部修改变成全局重构这是我最想吐槽的一点。AI在没有明确边界约束时特别喜欢“顺手优化”。你让它修一个变量空指针的问题它修完了还要顺便把你的循环改成列表推导式把你的函数拆成两个把你的配置项改名。改完以后报错确实没了但你的代码风格、变量命名、调用接口全变了下次你自己维护的时候痛苦得想撞墙。更麻烦的是这种“顺手的优化”经常引发连锁反应。因为它改了函数的入参类型调用方没跟着改于是新的类型报错出现了。AI看到新报错又开始改调用方改完调用方另一个依赖这层调用的模块又开始报错……于是你眼睁睁看着一个局部问题被AI一路滚雪球变成了连环车祸现场。1.3 它会预设一个不一定成立的“业务场景”AI在训练时见过海量代码所以它在改代码时脑子里是有“经验图式”的。比如它看到一个叫get_user的函数会天然假设这函数返回的是一个字典或者ORM对象看到一个配置文件会天然按主流框架的格式去理解。但你的实际项目未必是这个结构AI的这些假设一旦落错它给出的修改方案就是南辕北辙的。拿我遇到过的一个真实例子来说某次我让AI修一个接口超时报错它连续改了三次一直致力于把数据库查询改成异步批量怎么都压不下延迟。后来我仔细看报错发现慢的根本不在数据库层而是第三方API的响应时间。AI之所以一直兜圈子就是因为它在代码里看到了数据库查询就开始往自己熟悉的性能优化套路上靠根本没仔细分析那个报错堆栈。1.4 核心结论先缩小例子再让AI动手上面这些现象的共同点是什么AI在一个充满噪声的环境里做手术而你应该给它一个洁净的手术台。剥离出小例子本质上是在帮AI排除掉无关变量把它的全部“注意力”都集中到真正出问题的那一小块逻辑上。你给它看得越少它越能看清问题你什么都给它看它就什么都看不清。2. 最小化例子的三个原则缩小、保真、固定很多人听我说“把报错缩成小例子”第一反应是那不就是我手动写一个最小复现代码吗这我也懂啊。但你真正去看他们操作的时候会发现大部分人缩得不对。缩多了问题跑了缩少了跟没缩一样缩得既不真实也不稳定AI改完照样一头雾水。我自己摸索下来合格的“缩小例子”要同时满足三个原则。2.1 和当前报错无关的代码一律删干净听起来简单做起来真的需要决心。很多人舍不得删代码总觉得“这块虽然跟报错无关但它被别的模块引用了删掉以后结构不完整”。可是你要记住你是在造一个能触发相同报错的最小标本不是在给AI做项目导览。相关性的判断标准很简单如果你删掉这段代码报错依然能稳定出现那它就属于被删砍的对象如果删掉后报错消失了那说明这段代码恰恰是问题链条上的一环得保留。实际操作时我通常沿用“调用链倒推法”从报错堆栈最底部的那一行代码开始把相关的函数、变量、数据依赖逐层往上找凡是调用链上的就留下不在调用链上的一律注释或删除。这样剥出来的例子少的只有三四十行多的通常也不会超过两百行。2.2 数据要真实不要用“完美假数据”这块是新手最容易翻车的地方我见过太多人把真实的数据换成{name: test, age: 18}这种干巴巴的假数据丢给AI然后AI怎么改都不对为什么因为真实数据里的特殊性被抹掉了。举个例子你真实的代码在解析用户输入的日期格式时被一个空字符串卡住了。你为了“简化”把小例子里的日期填成2024-01-01这种标准格式AI当然找不到问题它甚至会教你把字符串转成日期的代码重写一遍。可问题是真实场景里那个日期可能是2024/01/01也可能是20240101还可能直接就是个None。这些“脏东西”恰恰是报错的根源你把它洗掉了AI看到的就只是个外形相似但内含不同的问题。所谓“保真”不是保代码长一样而是要保报错触发机理一致。我建议的做法是直接把你线上代码处理失败的那条原始输入原封不动贴到小例子里哪怕它看起来又长又奇怪。真实数据本身就是给AI指路的最好路标。2.3 锁定现场指出版本、依赖和环境差异小例子不只是代码层面的缩小还包括运行环境的锁定。有些报错是可复现的有些则是环境相关的——比如某个库在你本机是2.x版本在别人机器上是1.x版本行为完全不同。你在小例子里如果没有标明版本AI按照它自己的经验改出来的方案很可能满足不了你当前的运行环境。所以我的小例子一般会带一个极简的“现场描述”Python 3.10、某库2.1.3、操作系统是Linux容器、报错发生在定时任务里。这个描述不用很长三五行就行但一定要让AI知道它面对的是什么样的环境前提。你会发现只有锁定了现场AI才能给你真正能落地的方案而不是给你一套放之四海而皆准但你的环境跑不起来的废话。3. 实操怎么把报错缩成一个小例子理论讲完了下面是最重要的实操环节。我把整个剥离过程总结成了四步按这个流程走基本可以在十五分钟内把一个大项目的报错压缩成一份可以直接喂给AI的精炼小例子。我拿一个真实项目里遇到的场景来演示你就知道具体怎么操作了。3.1 第一步把报错文本变成“诊断清单”我很少直接复制一串原始报错丢给AI。我更习惯于先做一道填空题这个报错发生在哪个模块、什么操作、什么时候开始出现的、上一次正常是什么时候、改了哪个东西之后出现的。把这些信息都填完你对问题本身就有了一个完整定位。这道工序很值得做。因为当你认真把“什么时候开始报错”“改了什么东西之后开始报错”这些问题写下来的时候很多问题其实自己就有答案了。比如有次我填完这个清单突然想起来那个报错之前我刚升级过某个库的版本结果一验果然是升级把接口改掉了——跟具体业务代码八竿子打不着。这个清单同时也是给AI的“病历”。AI拿到的不只是一坨报错字符而是一个结构化的问题描述它不需要再从满屏堆栈里瞎猜可以直接进入诊断环节。3.2 第二步沿着调用链倒着剥这一步是整个流程的核心。我的做法很简单从报错堆栈最下面的那一帧开始往上逐层找调用关系。报错在哪个函数触发的这个函数是谁调用的调用它的代码又从哪里来……一路把整条链条上的代码抠出来。举个例子我之前修过一个定时任务的报错报错信息是TypeError: unsupported operand type(s) for : int and NoneType堆栈指向的是某个calculate_metrics函数。但真正的问题不在它身上而是它的参数来自上游的load_daily_data函数某个字段在数据库里为空返回了None。我只盯着calculate_metrics改改多少轮都白搭。直到我沿着调用链倒剥上去把load_daily_data返回的那条“货真价实”的数据也放进小例子里AI一眼就看到问题根源了。剥洋葱的时候有个原则哪个函数相关的代码保留哪些引用关系必须保留都要以能否复现报错作为唯一判定标准。不需要整条链路完整保留只需要让报错能稳定重现就行。有些中间层函数甚至可以不用完整搬过来用几行假装实现一下也可以——前提是这个“假中间层”不会改变报错的表现形式。3.3 第三步给外部依赖做“替身”真实项目里一个报错往往牵扯到数据库、外部API、消息队列、缓存服务器等外部依赖。你不可能把这些全牵扯到小例子里那样就又变成了一个大系统工程。但完全删掉外部依赖又会让小例子失去真实性。解决思路是给外部依赖做“替身替身”即用固定的假数据、假返回值在小例子里模拟出外部依赖的行为。注意这里的“假”是指不真实连接数据库、不真实调用外部接口而不是指数据内容造假。你要让AI看到代码逻辑在“真的遇到这个返回值时会触发问题”只是这个返回值是写死在代码里的。演示一下假设原来的代码从一个数据库里读用户列表然后把活跃用户挑出来计算平均分。剥离成小例子时我建议这样写# 这里是“替身替身”——模拟从数据库读出的真实数据 # 这一步是从数据库调出来的真实结果我原封不动地贴在这里 user_scores [95, 87, None, 92, 78, 100, None, 63] def calculate_average(scores): total 0 for score in scores: total score return total / len(scores) avg calculate_average(user_scores)这几行代码里没有任何数据库连接但保留了一个极其关键的真实特征用户分数列表中混着两个None。这正是线上触发TypeError的元凶。你把这段代码原样贴给AI它会在几秒钟内告诉你不能对None执行需要加一个过滤或默认值处理。而如果你把这个None换成普通数字AI永远只能给你一套“看起来挺对但用不了”的通用教程。3.4 第四步在提问时明确“手术边界”小例子准备好了最后一步是给AI提要求。我强烈建议你在提问时就把手术边界画清楚——你这次只让它修这个报错不允许它顺手优化不允许它重构别的部分不允许它改动无关函数。你可以直接在提问里写类似这样的话“这是从项目里剥离出来的最小复现代码请针对calculate_average这函数修复TypeError。注意只改这个函数内部的逻辑不要改动函数签名不要优化其他部分。”这比单纯丢一段代码加一句“帮我看看吧”要有效得多。AI是习惯性的“热心肠”你限制越明确它的发挥越收敛你完全不管它就会自由发挥到脱靶。3.5 实际案例同一段报错两种提问效果为了让你直观感受到差别我把我之前遇到的一个真实场景拆成两版提问方式你可以对比一下。第一版是低效版我的项目里有个函数会报TypeError: unsupported operand type(s) for : int and NoneType这是代码帮我看看怎么修。然后贴上去的是整个项目文件两千多行里面还包含了数据库连接、配置文件、多个工具函数。结果AI改出来的东西要么是把None过滤逻辑写错位置要么是入侵了别的函数结构折腾了三轮还在原地打转。第二版是高效版几乎就是多花了几分钟做剥离下面这个最小示例是从项目里剥离出来的报错是TypeError: unsupported operand type(s) for : int and NoneType。user_scores里的None是线上数据的真实形态。请只修复calculate_average函数内部的逻辑要求兼容None值不改函数签名。运行环境是Python 3.10无外部依赖。user_scores [95, 87, None, 92, 78, 100, None, 63] def calculate_average(scores): total 0 for score in scores: total score return total / len(scores) avg calculate_average(user_scores)这次AI给出来的方案干净利落用列表推导式过滤掉None再处理空列表的边界情况最后返回平均值。全程没有多余改动拿到就能直接用。差别简直天壤之别。4. 常见问题与排查技巧实录方法看着不难但实际用起来总会遇到很多幺蛾子。我把这一年多里自己踩过的坑、以及身边同事问得最多的问题整理了一下做成一个速查表希望能帮你少走点弯路。4.1 小例子造好了但报错不出现了怎么办这是剥离过程中最常遇到的挫折尤其是刚开始练这套方法的时候。你把代码从大项目里抠出来跑一遍居然不报错了——当场心态就有点崩。处理方式很简单说明你的剥离过程中删掉了一个关键依赖。还记得前面说的判定标准吗如果删掉某段代码后报错消失说明那段代码本身就是报错成因的一部分。这时候你就要往回回溯把你刚才删掉的那部分代码或者那条数据找回来重新放进小例子里。我在实践中最常发现的一点是报错消失往往是因为我把“脏数据”换成了“干净数据”。后来我长了个记性——剥离代码之前先把触发报错的那份原始输入备份下来小例子里默认用它。这样报错消失的概率会小很多。如果无论如何折腾报错都无法在小例子里重现那你可以换个思路不一定非要真实复现报错关键是找到一个“让AI能看到同类问题”的场景。只要小例子里的代码形态和数据特征与线上问题足够接近AI依然能推断出问题所在只是准确度会打折。这种折衷方案总比丢两千行代码给AI强。4.2 小例子AI改对了但大项目里还是不行这种情况也挺常见的AI在小例子里给出的修复逻辑完全正确你满心欢喜地把同样的修法搬回大项目又出幺蛾子了。原因通常有三种第一小例子里写的代码跟大项目的真实代码有细微差别第二大项目中有其他模块也在用这个变量或函数影响到相关状态第三修复逻辑在小例子里覆盖的边界条件在大项目里出现了新的变体。针对这个问题我现在的做法是把AI的修复方案拿回来后先在原理解上理解一遍别照搬。你至少要搞清楚它这次改动的本质是什么——是加了空值过滤还是改了默认参数还是换了初始化时机然后回到大项目里检查同样的逻辑点是否存在。如果存在就按同样的思路改如果不存在说明你小例子的保真度还不够需要回去重新调整小例子。这个过程看似繁琐但每做一次你对项目代码的理解都会比之前更深一层。4.3 AI死磕一个方向就是不动真正的根源怎么办还有一种情况是你已经把缩得很好的小例子给AI了AI也给出了答案但答案完全不在点子上。比如报错明明是外部接口返回数据格式变了AI却一个劲地在让你加捕获异常的代码不解决根源只做表面容错。这种时候我通常会做两件事。第一件事把小例子里的关键数据特征再显化一点直接在一开始的注释里写明“这行数据是实际线上返回的数据它可能在某些情况下返回None请从数据源头角度分析可能原因”。第二件事明确给AI设定方向限制比如告诉它“别在这个函数里处理帮我看上游调用方是否有问题。”其实很多时候AI跑偏不是它能力不行而是它根据你贴的代码默认了“问题就出在这段代码里”。你在小例子的设计上需要给它一点“问题可能在外围”的暗示帮它跳出局部视角。4.4 追问与多轮迭代的正确姿势如果你给AI的小例子起了作用但一次修复还没彻底解决问题——比如AI给出了修复方案但你还想确认边缘情况——别让它自由发挥。我个人比较推崇每一轮只问一个具体问题的做法。第一轮修这个报错。第二轮这个修复逻辑对空列表有没有影响第三轮如果scores是一个生成器而不是列表这个过滤写法还成立吗每轮只问一个具体问题AI就不容易跑偏。如果你一次性问五个问题它大概率只会认真回答其中一个其他四个全在敷衍。5. 把最小复现这个习惯用到日常工程里写到这里我相信你已经有了一种新的感觉与其说“AI改代码越改越乱”是AI的锅不如说是我们和AI协作的方式出了问题。你给它的病人越臃肿它误诊的概率就越高你给它的病例越干净它越能发挥出训练时积累的那些真实能力。现在我的工作习惯已经变了。每次遇到比较顽固的报错我的第一反应不是“再试一轮”而是“缩减问诊范围”。不光是对AI哪怕是对我自己我会下意识地把一个问题拆成最干净的可复现单元再来定位它。以前我在一堆代码里用print和各种调试器翻来覆去地找原因现在我发现当你把范围缩到足够小的时候很多问题你自己就能看出来了——AI甚至都只是帮你确认一下判断。最后分享一个我持续在用的习惯在你的项目仓库里专门开一个debug_examples目录每次遇到值得记的报错就顺手把剥离好的小例子放进去命名成报错类型加日期。这个目录不只是给AI用的更是给你自己的“病历本”。下次遇到类似的报错翻一下目录直接就能以最快的速度定位到根因。这比任何提示词技巧都管用因为它沉淀下来的是你自己项目的真实生态。我现在的感受是AI可以是很好的助手但你需要学会怎么正确地“提问”它。把报错缩成一个小例子本质上不是为AI服务而是为你自己的工程判断力服务。这个过程一旦建立起来你会发现不止AI好用了你自己排查问题的思路也清爽了很多。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑