logo

AI改代码越改越错?根因是信息层级错位

作者:4042026.11.07 00:38浏览量:0

简介:把报错原样贴给AI,连改三轮仍是错的;换成一句架构级约束,一次改对。本文从一次真实的并发缺陷排障出发,拆解症状级反馈为何失效、架构级约束为何有效,剖析AI自写代码与自写测试的“同源盲区”如何制造虚假的通过报告,并给出一套可落地的分层协作清单,帮你把AI的代码能力用在正确的地方。

一、一次典型的“越改越错”现场

我曾让大模型从零实现过一个带异步语义的 C 语言 HTTP 客户端库:约两千行,编译通过、自测用例全绿。性能验证时却暴露了问题,用并发接口(语义类似 Promise.all)同时发起三个理论耗时 300ms 的请求,实际墙钟时间 905ms。请求被完整地串行化了。

我把报错和现象描述交给模型去修。第一版改完,仍然串行;第二版换了一种写法,还是串行;第三版干脆编译失败。三轮迭代,每一步都很“努力”,每一步都离目标更远,标准的“越改越错”。

之后我换了沟通方式:不再贴任何报错,只给了一句架构级描述:

fetch 不要自己管理事件循环。创建连接、注册到全局事件循环、返回一个 pending 状态的 Promise,所有 I/O 由全局事件循环统一驱动。

一次改对。九个文件协同重构,905ms 降到 302ms。

同一个模型、同一份代码,三轮症状级反馈无果,一句架构级指令生效。差异不在模型的编码能力,而在输入信息的层级。

二、报错是症状,不是诊断:信息层级决定修复方向

排障信息天然分层:

  • 症状层:报错堆栈、异常数值、失败输出;
  • 行为层:期望行为与实际行为的偏差;
  • 机制层:组件之间的控制流与数据流;
  • 架构层:职责边界、所有权与不变量。

报错信息停留在症状层,它的语义是“这里出了问题”,而不是“系统为什么走到这一步”。模型以报错文本为主要上下文时,搜索空间被锚定在出错位置附近,自然生成局部修改:改返回值、调换调用顺序、加重试、补空值判断。这些修改在局部全部合理,但若根因是组织方式错误,局部收敛永远到不了全局正确解。

这类缺陷还有个欺骗性极强的特征:每一行代码都是对的。DNS 解析正确、socket 创建正确、HTTP 序列化正确、Promise 包装正确,逐行 review 挑不出任何毛病。错的不在代码,而在代码的组织方式:每个请求在自己的执行流里原地等待 I/O 完成,三个请求必然排队。模型在错误的地基上缝补,每一针都对,缝出来的衣服照样不合身。

用伪代码对比这两种组织方式:

  1. /* 反例:每个请求自己驱动 I/O,控制流被锁死在单请求内部 */
  2. Promise *fetch_async(const char *url) {
  3. Promise *p = promise_new();
  4. Connection *c = connection_open(url);
  5. while (!connection_done(c)) {
  6. connection_poll(c, 300); /* 等待期间,其他请求无法进入 */
  7. }
  8. promise_resolve(p, connection_read(c));
  9. return p; /* 返回时结果已就绪,“异步”名存实亡 */
  10. }
  1. /* 正例:注册 + 挂起,控制权上交全局事件循环 */
  2. Promise *fetch_async(const char *url) {
  3. Promise *p = promise_new();
  4. Connection *c = connection_open(url);
  5. event_loop_register(global_loop(), c, on_io_ready, p);
  6. return p; /* 立即返回 pending,调度交给事件循环 */
  7. }

两份代码的差别不在任何单行,而在“谁拥有控制流”这个架构决策上。

需要说明的是,症状级反馈并非总是低效。拼写错误、缺失导入、API 误用这类“局部即根因”的问题,直接贴报错是最快路径。真正失效的场景是:症状与根因在空间上分离,错误暴露在 A 处,根因藏在 B 处,甚至藏在“B 从未被定义”这件事本身。架构级缺陷天然属于后者。

三、那句一次生效的指令,做对了什么

它不是更详细,而是更高层。拆开看,它同时给出了四类决策信息:

  1. 职责边界:fetch 不负责驱动 I/O;
  2. 所有权归属:全局事件循环是唯一调度者;
  3. 交互协议:注册、挂起、立即返回 pending;
  4. 不变量:并发原语的语义不允许被内部实现破坏——并发请求必须真正重叠执行。

这正是设计文档该承载的内容。换句话说,这次修复有效的本质是:把本该由人做出的架构决策显式完成,再把清晰的约束交给模型施工。模型的强项恰好是“在明确约束下跨多文件一致地落实细节”,九个文件一次重构到位就是证据。它缺的从来不是执行力,而是决策输入。

四、更隐蔽的坑:代码与测试的“同源共谋”

修完并发问题后继续 review,又发现一个更吓人的事实:bench 模式下 16576 次 HTTP 请求全部失败;而此前的测试报告写着,16576 次请求,0 错误,通过。

根因是两处缺陷叠加:

  • 实现侧:每次调用被无条件计入成功计数,不检查响应状态、不传播错误;
  • 测试侧:断言只比较计数,把“调用了多少次”等同于“成功了多少次”。

两处为什么同时失守?因为实现与测试出自同一个模型。模型对“正确的错误处理应该长什么样”存在系统性盲区,这个盲区同时污染了实现与验证两侧。这对应测试方法论里最危险的一种情形:验证与被验证对象高度相关。相关的一致性不等于正确,两把刻度一致的坏尺子互相校准,读数完全一致,量出的长度却都是错的。

  1. /* 反例:断言写“现状”,只要循环跑完就必然通过 */
  2. let ok = 0;
  3. for (const t of tasks) { await client.send(t); ok++; }
  4. assert(ok === tasks.length); /* 恒真:与请求是否成功无关 */
  1. /* 正例:断言写“期望”,把正确性的定义交给外部 */
  2. assert(results.every(r => r.status >= 200 && r.status < 300));
  3. assert(elapsed < PARALLEL_BUDGET); /* 并发时延必须显著低于串行预算 */

更麻烦的是闭环效应:把这份“全绿”的自测报告再喂给模型,它从自己的输出里找不到任何异常信号,修改建议会被大量正面证据否决。此时无论从实现侧还是测试侧观察,系统都“正常”。唯一能打破闭环的,是回路之外的观测,人工 review、真实流量、独立指标。

生产实践中,这意味着要为 AI 生成的代码建立外部真值:真实压测数据、线上日志、监控告警这类通用可观测能力(例如百度智能云提供的日志服务、监控告警等通用类目),让“是否正常”由回路外信号裁决,而不是由同源的自测报告裁决。

五、修正协作模式:一套可落地清单

把“AI 写 → 报错 → 贴报错 → 再改”的循环,升级为分层协作。以下六条按投入产出排序:

1. 架构先行,代码后行。 动手前先和模型对齐四件事:模块职责、控制流归属、错误传播策略、并发语义,最好落成半页接口契约。契约的价值在于把搜索空间从“任意看起来合理的代码”压缩到“符合约束的代码”,后续所有修改都有锚点。架构决策是人的核心职责,模型负责在约束内施工,这份分工比任何提示词技巧都重要。

2. 反馈按层级组织。 贴报错时同时给出三要素:期望行为、实际行为、机制层假设。“三个 300ms 的请求应在约 300ms 内完成,实际 905ms,怀疑请求未重叠执行”,这一句的信息量远超一屏堆栈。期望行为为模型提供了正确性的定义,机制假设为模型指示了搜索方向。

3. 修复方向错了就升层,不要平级加码。 同一层级的连续失败(换了三种写法仍串行)是强信号:根因不在这层。此时应停止“再试一次”,改为显式重构职责归属,甚至先让模型输出设计方案、经人确认后再动代码。

4. 测试与实现隔离生成。 断言的期望值来自人或独立上下文:先写验收标准(延迟预算、成功率、错误必须传播),再让模型实现。断言永远写“应该发生什么”,不写“正在发生什么”——后者极易产生恒真断言,提供虚假信心。必要时让另一个看不到实现的独立会话来生成测试。

5. 引入回路外的真值。 自测全绿不等于可用。压测数据、真实流量、日志与监控告警,这些回路外信号是对抗同源盲区的最后防线。部署在云上的项目,直接接入通用的日志服务与监控告警类目即可,投入远低于事后排障的成本。

6. 人工 review 看结构,不逐行挑错。 对模型生成的代码,逐行挑错收益很低,单行质量通常不差;review 重心应放在:模块边界是否清晰、错误路径是否贯通、控制流归属是否唯一、并发语义是否被内部实现破坏。能发现“每行都对但整体不对”的,目前只能是人。

六、结语:瓶颈已从“写代码”迁移到“定义与裁决”

模型写错后越改越错,多数时候不是能力不足,而是协作契约错位:你持续供给症状级信息,问题却出在架构级;你用同源的自测结果做验证,盲区就被系统性放大而非暴露。工程角色正在迁移,从“逐行写代码”转向“定义约束、裁决真值、守住架构边界”。把这三件事留在人手里,模型的执行能力才会成为真正的杠杆;放弃这三件事,再强的模型也只能在错误的地基上,一遍遍缝补那件永远不合身的衣服。

发表评论

活动