简介:本文通过一个真实案例,剖析 AI 编程工具在修复缺陷时“越改越错”的根本原因:开发者提供的是报错症状而非架构诊断。文章提出“信息层级”概念,说明为什么换一种描述方式后,同一个 AI 能从连续修复失败变为一次改对,并给出面向 AI 的高质量缺陷描述方法,帮助开发者提升人机协作效率。
先说一个亲身经历。
我让 AI 从零开始写一个 C 项目,最终代码量大约两千行,可以编译,可以运行,基础测试也通过了。但随后我发现一个性能问题:三个耗时约 300ms 的请求,理论上并发执行应该在 300ms 左右完成,实际却花了 905ms。很明显,请求被串行执行了。
于是我把现象和报错信息描述给 AI,让它修复。第一轮修改之后,问题依旧,还是串行。第二轮修改,换了一种方式,仍然是串行。第三轮修改更糟糕,程序直接报错,连运行都成问题。这就是非常典型的“越改越错”过程。
后来我换了一种沟通方式。不再把报错信息丢给它,而是直接说明一个架构层面的约束:
“异步请求不要自己管理事件循环。创建连接之后,注册到全局事件循环,返回一个 pending 状态的 Promise。所有 I/O 操作由全局事件循环统一驱动。”
这一次,AI 只改了一轮就修对了。涉及 9 个文件,耗时从 905ms 降到 302ms,基本接近理论并发时间。
同一个 AI,同样的代码库,为什么前面修改三轮都不对,后面换了一句话就一次通过?核心原因并不在于 AI 能力波动,而在于我们提供的信息层级不同。
很多开发者在让 AI 修代码时,习惯把报错信息、异常堆栈或者失败日志直接贴过去。这种方式在简单场景下有效,比如某个函数返回值写错、变量名拼写错误、类型不匹配等。但在复杂缺陷面前,这种做法的效率会急剧下降。
原因在于:报错信息描述的是“症状”,而不是“病因”。
比如“这个函数返回值不对”“这里抛出了空指针异常”“请求耗时超出预期”,这些描述告诉 AI 的是“哪里表现不正常”,但没有告诉它“根因可能在哪一层”。
AI 收到症状之后,通常会选择最直接、最局部的修复路径:修改这个函数内部的逻辑,增加空值判断,或者调整调用处的参数。这种修改方式对于局部缺陷有效,但对于结构性缺陷,往往只是把一个错误换成另一个错误。
前面提到的串行问题就是一个典型案例。从表面看,耗时异常只表现为“请求没有并发”。如果只告诉 AI “耗时不对”“请求串行了”,它很可能在调用层做调整:改循环方式、调整调用顺序、尝试增加线程池。但因为底层事件循环的驱动方式本身就不对,这些局部修补自然无法解决问题,甚至会引入新的错误。
这就像医生看病,只告诉医生“头疼”,医生可能开止痛药,但真正的问题可能是高血压、颈椎病或者睡眠不足。症状信息是必要的,但并不足够支撑一次精准修复。
要理解为什么信息层级如此重要,需要先理解 AI 修复代码的工作方式。
AI 在拿到一个修复任务时,并不是真正“理解”了整个系统的设计意图。它基于我们输入的信息,在已有的代码空间中搜索一个满足条件的修改方案。我们提供的信息越表面化,搜索空间就越大,方案偏离正确方向的可能性也越高。
具体到修复缺陷这个任务,AI 需要同时满足三层约束:
当我们只提供报错信息时,实际上只强化了第二层约束,而且是一种非常模糊的强化。“请求太慢”这个描述,并没有告诉 AI 是调度问题、资源竞争问题、锁粒度问题还是 I/O 模型问题。AI 只能围绕“让它看起来变快”去试。
但当我明确说明“由全局事件循环统一驱动 I/O”时,相当于直接给出了架构约束。这样一来,整个搜索空间被大幅缩小。AI 不再需要猜测应该用线程池还是协程,不再需要尝试调整调用顺序,而是直接朝着给定的结构方向重构代码。
这就是同一套模型产生截然不同结果的原因:输入的信息层级决定了搜索的精度。
在真实的开发协作中,我们把面向 AI 的信息大致分为四层。
也就是“程序崩了”“返回值不对”“页面空白”“耗时太长”。这一层信息告诉 AI “结果不符合预期”,但它不包含任何关于原因的判断。
结果层信息是必要的起点,但如果只有结果层信息,AI 只能从最表面开始猜测。面对复杂项目时,这种猜测很容易命中错误方向。
症状层比结果层更具体一点,包含报错类型、异常堆栈、失败日志、错误码、触发条件等。比如“在第三次循环时抛出空指针”“响应时间接近线性增长”“并发数提升后吞吐不变”。
症状层可以帮助 AI 缩小排查范围,但仍然停留在“客观表现”层面。它们没有解释系统为什么会出现这种现象。
诊断层要求信息提供者对问题有一定判断,比如“怀疑是锁粒度过大导致并发瓶颈”“可能是事件循环被阻塞”“看起来是生命周期管理问题”。诊断层信息相当于一个初步假设,AI 可以围绕这个假设去验证和修复。
诊断层信息在很多场景下已经足够高效。它把“找问题”变成了“验证问题并解决问题”。
架构层描述的是系统应该遵循的设计约束,比如“所有 I/O 必须异步驱动”“请求层不能直接管理线程”“缓存写入必须走统一入口”。架构层信息不一定直接指向具体代码位置,但它定义了正确的代码应该长什么样。
架构层信息的一个巨大价值是:它解决的不只是当前 bug,还会引导 AI 生成符合项目长期演进方向的实现方式。
回到开头那个案例,我最后给出的说法就属于架构层信息。它没有告诉 AI “哪个文件写错了”“哪个变量不对”,而是直接说明正确模型应该是什么。AI 依据这个模型重新组织代码,自然就把缺陷修掉了。
有人会问:如果我知道诊断和架构,为什么不自己改?
这里需要澄清一个常见误区:提供高价值信息,不等于提前知道完整答案。你只需要认识到问题属于哪一层级,或者排查方向在哪里,就可以显著影响 AI 的修复效果。
以开头那个性能问题为例,在修复之前,我并不确定具体是哪个函数写错了,也不确定 9 个文件里哪些调用需要调整。但我能判断出,耗时接近三者之和,说明请求大概率没有通过统一的异步调度机制运行。这个判断并不需要读完整份代码,只需要理解常见的阻塞式调用模型与异步事件驱动模型的区别。
换句话说,开发者所依赖的并不是“更聪明的 AI”,而是“更准确的约束表达”。AI 优势在于快速生成与局部搜索,开发者的优势在于方向性判断和结构感知。两者结合起来,效率才会真正释放。
提升人机协作效率,并不需要每次都给 AI 写一份完整的设计说明书。可以从以下几个方面逐步改变表达习惯。
不要只说“这里报错了”,而是说:
“这里报错了,我怀疑是状态被提前释放导致的,可以优先检查生命周期管理部分。”
这种方式直接提升信息到诊断层。AI 修复路线会更集中,错误尝试也会明显减少。
很多时候“不要怎么做”比“要怎么做”更容易表达。比如:
“不要通过增加固定延时来规避竞态条件,应该从同步机制上解决。”
这类负向约束虽然不能直接给出答案,但可以阻止 AI 采取表面有效的错误方案。
当异常表现比较复杂时,可以先说清楚正确行为应该是什么:
“无论发起多少个并发请求,总耗时应该接近单次请求的最大耗时,而不是各请求耗时之和。”
这种描述给出了清晰目标,AI 可以据此反向判断当前实现是否合理。
对于基础组件、网络层、调度器等核心模块,建议直接给出设计约束。比如:
“所有连接必须由全局事件循环管理,业务代码不应直接持有事件循环句柄。”
这类语句简短,但信息密度非常高,可以涵盖几十个文件的实现边界。
AI 编程工具的能力,并不仅仅取决于模型本身的参数规模或训练数据多少。在现实开发中,同等重要的还有我们向它传递信息的方式。
报错信息只能描述局部现象,难以支撑复杂缺陷的精准修复。而当我们学会用诊断性语言和架构性语言来描述问题时,AI 的修复效果会呈现出显著差异。同一套工具,同样的代码,输入信息不同,产出质量可能从“反复失败”跨越到“一次通过”。
这也是今后开发工作中值得刻意训练的一种能力:在面对复杂系统时,能够把模糊的失败现象,转化为清晰的方向性约束,并把它准确表达出来。这种能力最终会影响我们与 AI 协作的每一个环节——从代码生成,到缺陷修复,再到架构调整。