资讯动态

报错别直接丢给AI:最小复现示例让AI改代码不再越改越乱

发布时间:2026/10/13 8:39:56 来源:尧图企业网站定制
先问一句你有没有经历过这种场景——让AI改一段代码第一次报错A它给你改了跑一下变成报错B再让它改变成报错C改到第五轮你发现项目里多了好几个没用的函数、几处重复的逻辑连原来能跑的部分都开始报错了。你开始怀疑AI是不是能力不行但真正的问题其实是你从一开始就喂了一个错误的问题给AI。我自己也被这个问题折磨了小半年。直到某天凌晨三点对着一个越改越乱的接口我把报错从头到尾读了一遍突然意识到一件事报错不是拿来直接丢给AI的报错是要先被压缩成最小示例AI才能真正帮上忙。那天之后我养成了一个习惯——任何报错先花十分钟缩成一个可以独立运行的小例子再开口问AI。效果不是好了一点点是好了非常多。这篇文章就讲讲这个思路为什么AI改代码会越改越乱最小复现示例为什么能终结这个循环以及具体怎么把一个几十层深的项目报错压成两三段代码。适合所有正在用AI辅助写代码、但又觉得AI经常越帮越忙的开发者。1. 为什么AI改代码会越改越乱不是模型变笨了先说结论AI在“改代码”这件事上并没有你想象的那么强大。它既没有完整的项目记忆也没有边运行边观察的能力。它所有的判断都建立在你给它的那段文字和代码上。你的上下文给得越乱它猜得就越离谱。1.1 上下文碎片化你以为AI看到了全部其实它只是在猜这个是很多人的第一直觉误区。你会觉得我把报错贴给你把相关代码段也贴给你再加上我对需求的描述已经够多了吧但AI看到的并不是一个“项目”它看到的是一堆彼此之间可能有冲突、有缺漏的文字片段。举个例子你说“用户列表接口超时帮忙看下”然后贴了controller层的代码、service层的代码、还有一段配置文件。AI要怎么处理它只能自己去推断这些片段之间的关系。它不知道这个接口在真实运行环境中是怎么被调用的不知道数据库里有多少数据不知道Redis缓存有没有生效更不知道那段配置是不是已经废弃了。于是AI就处在一个高度不确定的“猜谜”状态。每一次修改它都会基于自己的猜测建立一个新的假设。这些假设之间不一定兼容甚至会和之前改过的逻辑冲突。一轮、两轮没问题第三轮开始你面对的就不再是“修改代码”而是一个自己给自己叠buff的混乱现场。说白了你喂给AI的上下文越碎片化AI的每一次修改就越像是赌博。1.2 错误漏斗效应你看到的报错往往只是表层另一个容易忽略的点是生产环境里的报错很少是“单纯的错误”。报错信息是一条链外层是框架的报错中层是业务逻辑的问题最里面才是真正的根因。我记得有个项目的报错前端看到的是HTTP 500后端日志里是空指针异常再往下看才是某条SQL查出来的数据是null然后那个null被直接塞进了对象。如果你把这个500的报错丢给AIAI大概率会告诉你“加个try-catch”。它能解决吗能让请求不报500了但数据还是不对甚至可能因为catch吞掉了错误后面连排查点都找不到了。这个我管它叫“错误漏斗效应”同一时间浮上来的错误表象可能有几十个但真正的根因往往就一两个。你越是在表象层反复修改新问题冒出来的速度就越快。因为你改的任何一层都在扰动下面层的输入。所以AI看起来像是越改越乱其实是它一直被误导去修表象压根没有碰到内核。2. 最小复现示例到底做了什么把“推理题”变成了“证明题”那为什么要缩小成小例子核心原因是小例子能同时砍掉“上下文碎片化”和“错误漏斗效应”这两个恶化因素。2.1 它先替AI清掉了大半的“隐性冲突”当你把一个报错缩成一个最小示例时你会主动做这些事把外部依赖换成固定值把网络请求换成mock数据把框架配置删掉把业务逻辑简化成直白的输入输出。表面上你是在“简化”本质上你是在消除所有让AI产生歧义的背景信息。我之前遇到过一个问题让AI帮我优化一个数据清洗的函数结果它改完之后把空值处理逻辑彻底删了。我气得不行后来才发现我贴过去的代码里空值处理逻辑在一个完全无关的旧分支里写着AI以为那是废弃代码。这个场景太典型了——不是AI不想好好干活是你给的信息让它以为自己在干另一件活。小例子没有这个问题。里面没有几十个变量、没有状态依赖、没有运行时上下文。AI只需要处理一件事给定这段输入这个函数为什么不符合预期。它不再需要同时回答“问题是什么、代码哪里错、怎么修才不破坏别的功能”三个问题它只回答最后一个。任务从“推理题”变成了“证明题”出错面小了非常多。2.2 缩到多大才算够我自己的判断标准这可能是大家最关心的问题。不是所有问题都能缩成三行代码的尤其是涉及框架、数据库、消息队列的问题。我的经验是缩到你可以在30秒内向一个同事口头复述清楚并且对方不需要看完整项目就能理解“哪里出错了”的程度。具体一点有三个硬指标一是能独立运行不依赖原项目里其他模块。二是输入数据是写死的不依赖数据库、缓存、外部API。三是预期行为和实际行为之间的差异一句话能说清楚。这里多说一句不要追求“越小越好”而走极端。你完全没必要把Spring Boot整个应用缩成一个main函数那是另一种浪费。只要上面三条达到AI需要的信息就已经足够少了。2.3 压缩过程本身就在帮你发现真相有个非常有意思的副产物很多时候你还没走到问AI那一步问题就自己浮出来了。压缩报错的过程本质上是“把代码里的自由度一个个删掉”的过程。你删掉外部依赖用固定值传参把循环体拆出来单独跑每删一次程序的行为就更可控一些。大部分真正的bug在这样反复剥离的过程中会直接暴露因为你会发现“哎我换成固定值之后这个报错消失了”“咦这一步算出来的数不对啊”。这就是所谓的橡皮鸭效应。你按步骤把一个复杂的漏洞拆解时其实是在用逻辑强制自己重新走一遍代码路径。很多藏在业务逻辑深处的“低级错误”比如字段名拼错、类型传反、状态没更新在这一步就能抓出来。我自己大概有三分之一的问题是在压缩过程中解决的根本没走到AI那一步。3. 把报错压缩成小例子的标准四步流程那具体怎么压我总结了一套固定流程现在一直用大概花十到二十分钟视情况而定。3.1 冻结环境先把变量锁死这一步很多人忽略。你以为代码一样但环境不一样跑出来的结果就是不一样。所以第一件事不是改代码是先把环境“冻结”下来。我的做法是单独建一个虚拟环境把项目依赖装好然后记录当前Python版本、依赖版本、数据库schema版本和关键配置项。如果用的是容器化开发这一步更简单直接用当前的镜像id记录就行。然后在压缩过程中坚决不做任何升级依赖、改配置的操作。环境一固定报错的可复现性就有了保障。一旦你边排查边升依赖报错可能会从一个变成另一个到时候你根本分不清是代码问题还是环境问题。3.2 剥洋葱把业务代码换成假数据接下来处理代码层。原则是从外到内逐层剥离外部接口先mock掉、数据库查询替换成内存字典、配置文件里的连接串删掉一步一步把项目代码“变成”一个普通脚本。具体操作上我会找一个最简单的主干路径从入口函数进一路走到报错点把所有跟主路径无关的分支全部删掉或注释掉。删完外部依赖后再把你怀疑出问题的那个函数单独拎出来放到一个新建的测试文件里配上几组写死的输入数据。这一步的关键是别贪心。一次不要尝试同时处理两个问题先锁定报错信息里说的那个位置做一个最小信心测试跑通了再往内走一层。3.3 二分定位找“第一次算错”的那个点代码剥离到一定程度你可能还要进一步定位尤其是在报错不明显、只是“结果不对”的情况下。我推荐二分法而不是逐行看代码。先从入口到出口把整条链路画出来找中间的一个判断点打印出这个点上的关键变量值。如果这个值已经不符合预期问题就发生在它之前如果它是对的那问题就发生在它之后。这样每跑一次排查范围就缩一半。几轮下来问题点通常会非常集中。注意这一步不要加太多日志。很多人一上来就在几十个地方加print结果一跑输出几百行反而分不清重点。我一般只加三个断点入口值、中间关键值、出口值。三个值一对方向就明确了。3.4 写出一份AI能直接执行的复现说明最后一步是整理“成果”把它变成一份可以直接投喂给AI的包。这不是让你写一篇长报告恰恰相反越精简越好。我的格式基本是固定的第一行一句话说明现象——“当传入参数X时函数返回了Y期望是Z”。第二段可运行的最小代码片段包含必要的import、输入数据和触发报错的那几行。第三段完整报错栈或实际输出结果。这样一份材料AI不需要问任何前置问题就能开始分析。如果你的问题是框架层的框架的启动入口和报错栈还是要保留但业务逻辑务必替换成假数据。如果你的问题是业务逻辑层的只要保留函数体和输入输出就行。在我实践了一阵子之后一个很真实的感受是压缩报错其实60%的时间不是在为AI节省时间而是在为你自己节省时间。因为你亲手把环境、代码、输入输出都理清楚了AI只需要做最后的推理判断效率自然高。4. 一次从乱到治的完整实录接口超时排查手记讲理论太多容易飘我给你还原一个我实际踩过的坑。这个案例很典型几乎每个后端开发者都会遇到。4.1 乱在哪儿三轮修改五个新问题某天某项目里一个“根据部门ID查询用户列表”的接口在数据量上来之后开始超时。我顺手把报错和整个service层的代码丢给了AIAI很快给出了第一轮修改建议——说要加缓存。我改了部署不超时了但第二天发现数据不更新了。因为缓存没有失效策略。于是第二轮我告诉AI“缓存导致数据不更新帮我加失效逻辑”。AI加了失效逻辑这次数据更新了但出现了新的问题在某些并发场景下缓存击穿接口耗时反而比以前更不稳定。第三轮我继续让它调它改了一版加锁逻辑结果锁粒度过大接口直接退化成串行请求白天高峰期又超时了。到这个时候项目里多了三个新的缓存的工具方法、一个分布式锁的配置、以及一套我快看不懂的嵌套判断。接口状态比最初还要糟糕。我当时的念头就是这AI怎么越改越乱。4.2 压缩之后发生了什么问题浮出水面冷静下来之后我把这个问题用上面那套流程压缩了一遍。环境冻结没花太多时间就是测试环境当前的依赖版本组合。然后我把代码里跟部门查询相关的分支剥离出来去掉权限校验、去掉操作日志、去掉列表转树形结构的部分只保留最核心的“根据部门id查用户列表”这个动作。外部依赖全换成假数据数据库查询保留但只保留一张表。接着我跑了一次看到耗时还是挺高。再往内拆单独把那个循环体抽出来测试我突然看到问题了查询是在for循环里执行的——每查到一个部门就发一次数据库请求。部门多了之后这个N1查询的耗时直接爆炸。而这个循环体就在service层那段代码的中部前面包着权限逻辑后面包着DTO转换AI从头到尾都没看出来因为我把一整个服务层丢给它时它的注意力全被大结构带跑了。我重写查询逻辑把循环外的批量查询放进去接口耗时从800ms降到了30ms。甚至没有用到任何缓存或锁——根因就是一条SQL的事。4.3 同样的AI两问两答的对比我做了一次对比测试用同一个AI分别问两个版本的问题。第一个版本我贴出整个service层代码加接口超时描述AI的回答是三段式的建议加Redis缓存、建议做异步削峰、建议换数据库连接池配置。每一句都像是正确的“大厂方案”但没有一个指向真正的N1问题。第二个版本我贴的是压缩后的最小例子几行代码、一个循环内查询、几组假数据、实际耗时数据。AI的回答非常明确——直接指出这个循环里有N1查询问题建议用批量查询替代还顺带给出了具体实现。同一台AI同一个模型对比如此明显。不是第一轮AI能力不行是我给的上下文让它根本没机会看到那个循环。5. 最小化思维的两个延伸用法理解了压缩报错的逻辑之后我发现这套思维不仅适用于修bug还能辐射到AI协作的其他场景。5.1 代码评审一次只问一个“决策单元”很多开发者喜欢把一大段代码贴给AI问“这套代码有什么问题”。这是个典型的错误用法。一大段代码里包含的隐性决策太多了数据从哪来、生命周期是什么、哪个分支是主路径、哪部分是遗留逻辑。AI全去分析一遍最后给你的都是宏观层面的空话。我现在做AI辅助代码评审一次只问一个“决策单元”。比如“这个函数在传入空list时会怎么走”“这两个分支的条件在什么情况下会重叠”“这个异常被捕获后会不会吞掉应该暴露的信号”。一个问题对应一个明确的小函数或一段小逻辑AI的回答准确率和落地性都高很多。5.2 让AI输出错误树而不是一次性答案另一个技巧是在小例子的基础上让AI先列出“可能出错的原因树”而不是直接给一个答案。比如你有一个小函数运行结果不对不要直接问“怎么修”。先问“基于这段最小示例按照从常见到罕见排序列出可能导致这种输出偏差的所有原因。”AI会输出一个结构化的原因列表你从中挑出最接近实际的那个再针对具体原因去问解决方案。这样做的好处是你不会被AI第一次给出的“看起来合理但其实是错的”方案带偏同时你也能从它的原因列表里学到一些盲区。压缩后的例子本身就小AI列原因也列得完整这个场景下它甚至有点像在给你做知识科普。6. 我现在的工作习惯先把报错缩到我能看懂为止最后分享一点个人习惯。现在我不论遇到多复杂的报错第一反应不是贴给AI而是先执行那套四步压缩流程。环境冻结、剥洋葱、二分定位、写复现说明。这个流程本身帮我解决了很多“不需要AI也能解决”的问题更重要的是它为AI准备好了极其干净的输入让AI真正回到“帮你思考”的位置上。有读者可能会问这不是把活都自己干了吗其实不是。压缩报错准备的是“问题的上下文”而AI负责的是“从上下文到结论的推理”。前者讲究的是筛掉噪音后者讲究的是推理深度。你筛得越干净AI的推理就越集中在关键点上产出的正确率也就越高。我现在甚至会为每个正在进行的项目单独维护一个小例子文件夹把每次排查时缩出来的最小复现示例按问题分类归档。下次遇到类似报错先翻这个文件夹往往不管用再新建新的。时间久了你会发现报错的种类远没有你想象的那么多你真正需要AI深度介入的场景其实越来越少了。

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

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

免费获取报价 →
↑