AI 写代码越来越强,但它不知道我们上周修了什么 bug,把 Bug 重新引入生产环境了

13 小时 32 分钟前
 huoru

三个月前,我们花了两周时间修了一个 bug 。

用户账单偶尔会出现 ¥0.01 的误差。复现不稳定,线上偶发,客服一直在手动补差价。翻遍了日志,最后定位到一个经典问题:浮点数精度。

0.1 + 0.2 在 IEEE 754 里不等于 0.3,这个大家都听说过,但真的踩到还是很痛。

修复方案是把所有金额改成以「分」为单位存整数,展示时再除以 100 。改动不复杂,但涉及的地方很多,改完之后代码里到处是 amount * 100Math.round()/ 100,看起来有点丑,但能用,bug 消失了。

然后上个月,我让 Agent 帮我重构结算模块,优化一下代码结构。

它看到那堆乘除,觉得多此一举,顺手给「简化」了,改回了直接用浮点数。逻辑更清晰,代码更短,单元测试全过。

那个 ¥0.01 的 bug 悄悄回来了。


这件事让我开始认真想一个问题:

我们有代码审查,但我们需要「意图审查」。

这不是 AI 写了烂代码

大家讨论 AI 写代码,通常聚焦在输出质量上:架构合不合理、有没有 bug 、测试过了没有。

但我说的这个问题不一样。

Agent 那次重构质量是好的。逻辑清晰,可读性提升了,测试也没挂。它只是不知道「那堆看起来多余的乘除,是两周 debug 换来的」。

从它的角度看,price * 100 然后 / 100 是纯粹的噪音——数学上等价,白白增加了阅读负担。优化掉,天经地义。

你想想一个工作了五六年的老工程师,碰到这段代码会怎么做?

大概率不会直接删。他会皱着眉头想:「这里为什么要转成整数?不可能是手滑写的,一定有原因。」然后去翻 commit ,或者去问写这段的人。

这叫对不熟悉代码的谦逊感——被坑多了,知道看起来多余的代码往往不是真的多余。

AI Agent 没有这个。它被训练成「顺着干」,而不是「先问为什么」。代码看起来可以简化?简化它。逻辑看起来绕弯子?拉直它。它只会优化表面,不会追问历史。

新项目里这没问题,在一个有历史的真实代码库里,这会出事。

现有的方案都差点意思

每次我说起这个问题,大家都会推荐一些工具。我挨个想过,说说为什么都不够用:

注释:「// 不要修改这里,浮点精度问题」写上去当然有用。但你得先意识到这里需要警告,才会去写。当时修 bug 的人在想的是「终于修完了」,不是「三个月后的 Agent 可能会把这个改回去」。漏写的注释保护不了任何人。

AGENTS.md / CLAUDE.md:同理,你想到的禁忌才能写进去。但大多数坑是事后才知道是坑的,你没法提前把所有决策都整理成文档。

ADR / RFC:门槛太高,大多数团队第一个季度之后就不维护了。就算维护着,也是给人看的文档,不是供 Agent 在改代码前按需查询的。

Wiki / Notion / Confluence:文档会和代码脱节。「金额统一用分存储」这件事,可能在某个内部文档里提了一句,但 Agent 不会在重构代码前主动去翻 Confluence ,就算翻了,也是一堆非结构化的文字,未必能命中。

PR 描述:当时修 bug 的 PR 描述里可能写了原因,但埋在 GitHub 历史里,没人去翻,Agent 更不会主动去看。

每一个工具都是对真实问题的局部回应,但没有一个能在 Agent 动手之前,把「这里曾经踩过什么坑、为什么要这么写」这件事,可靠地送到它面前。

我们缺的是「意图审查」

现有的代码审查,回答的是:「这个变更实现得好不好?」

看 diff ,查正确性、风格、测试覆盖率。这个流程很成熟,也很必要。

但代码审查解决不了另一个问题:「这个变更,在已有的历史背景下,方向对不对?」

这个问题需要审查者记住代码库的历史,记得这堆乘除是两周 debug 换来的,记得某个绕弯子的写法是填过坑之后留下的疤。大多数审查者没这个上下文,就算是原作者,三个月后也可能忘了当时为什么。

所以我觉得我们需要一层新的审查,叫意图审查,发生在代码被改之前,而不是之后。

它问的是:

对人类工程师来说,这些审查以非正式的方式发生:发条消息、瞄一眼 commit 历史、和写那段代码的人聊三十秒。

对 AI Agent 来说,这根本不会发生。没有「去问问当时踩过坑的人」的等价操作。Agent 读当前代码,看起来可以优化,就优化了,历史上踩过的坑对它来说是不可见的。

意图审查要怎么做

要真的能用起来,得满足三点:

第一,决策必须是结构化的。

「金额统一用分存储,避免浮点精度」这句话写在 Wiki 上供人阅读没问题。但 Agent 需要的是:决定了什么、为什么这么决定、涉及哪些文件、哪些操作是被明确禁止的。自由文本把这些结构藏起来了,Agent 每次都要重新解析一遍,还不一定能命中。

第二,决策必须住在代码旁边。

Wiki 会漂移,Notion 会被遗忘,Slack 消息串会被淹没。唯一能和代码永远保持连接的是 git 本身。决策活在 git 里,就能跟着代码一起被 clone 、被 fork 、被带到三个月后还没接手过这块的人面前。

第三,查询必须自动发生,在改代码之前。

如果需要提醒 Agent 「先查一下历史决策」,它就不会查。这个步骤必须内嵌在正常工作流里,就像它在重构前会先 grep 符号定义一样,查历史的摩擦要低于直接动手的摩擦。

我做了个工具

我最近在开发一个叫 Mainline 的东西,把团队决策以结构化记录的形式存进 git 本身,让 Agent 在改代码前可以查询。

每条「意图记录」长这样:

如果当时修浮点 bug 的时候封存了一条意图记录,三个月后 Agent 重构结算模块,运行 mainline context billing,就会看到:「金额统一用分存储,直接用浮点运算会导致 ¥0.01 偶发误差,已确认线上踩坑,禁止还原。」

那次「简化」大概率就不会发生了。

用了一个月,有几点出乎我意料:

摩擦不在我以为的地方。 我以为工程师会抗拒写这些记录,结果没有——因为 Agent 负责起草,工程师只是过目和调整。真正的摩擦在于:什么时候该封存一条记录?封太频繁是噪音,封太少会漏掉重要的坑。

收益来得比预期晚。 第一周感觉纯粹是额外负担。第三周开始出现「这段为什么要这么写?」的时刻,答案就在日志里。第六周,Agent 开始不用提示就把历史决策当作上下文来用。

两个人协作比一个人用难得多。 一个人用的时候,意图记录是写给自己的备忘。两个工程师同时工作时,它变成了一个协调协议——你得知道对方封存了什么,你们的方向有没有冲突。

工具是开源的( Apache 2.0 ),目前小范围私测,地址在 mainline.sh

https://github.com/mainline-org/mainline

说回这件事本身

AI Agent 现在在很多团队里写相当比例的新代码。代码审查的负担没有降低,反而在升高——因为审查 AI 写的代码认知成本更高,你没办法像问同事一样问它「你为什么这么改」。

现在的情况是,每次代码审查都得身兼两职:一边看实现,一边猜方向对不对。大多数时候,方向验证是静默失败的。审查者不知道那堆乘除是修 bug 留下来的,点了 approve ,坑就回来了。

这个问题靠更好的 prompt 解决不了,靠更大的上下文窗口解决不了,靠更强的模型也解决不了。

这是一个结构性问题:团队踩过的坑住在哪里?

现在它住在人的脑子里、Slack 里、PR 描述里——Agent 不会主动去看的地方。

要让 AI 真正能在一个有历史的代码库里可靠地工作,我们需要把这些知识搬到它会可靠地去看的地方。对大多数团队来说,那就是 git 。

我们有代码审查。我们需要意图审查。


你们有没有被 Agent 悄悄还原过某个 bug 修复?现在是怎么防的?

4841 次点击
所在节点    推广
98 条回复
DT37
13 小时 1 分钟前
写的真不错!现在都粗过,信任 测试。
huoru
12 小时 58 分钟前
@DT37 那你是怎么处理的啊
DT37
12 小时 43 分钟前
@ChristopherWu 不好意思,是我表达问题,我现在也是都走测试。如果测试通过就觉得 OK 了(不能完全信任测试),其实也存在各种没有预料到的问题,粗过是看代码逻辑。
kneo
12 小时 42 分钟前
别说 AI 了,这种代码你换个同事一样给你优化掉。你以为你的同事,那位“工作了五六年的老工程师”,真像你想象的那么优秀?

当时的 fix 就是块狗皮膏药,我看不像是 AI 写的,倒像那位“工作了五六点老工程师”写的。

有空让 AI 帮你在假装反思不如认真向 AI 请教下如何正确 fix 这个问题。
huoru
12 小时 34 分钟前
@kneo 是啊,就应该让 Ai 去修,带上更多上下文
freemoon
12 小时 23 分钟前
其实还是测试的问题,你都修复过了,为何还没有完善测试用例呢
allanwell
12 小时 17 分钟前
修改后不更新到 Claude.md 吗?
huoru
12 小时 15 分钟前
@allanwell 开发懒啊。。有时 agent 自己有内存记忆了
现在都没规范,而且很多问题总不能全放 claude.md 里吧
loryyang
12 小时 13 分钟前
这个有几个思路:
1. 单测回归
2. changelog
另外,人 + AI 混合编码反而更容易造成混乱,因为人很多事情不留文档
huoru
12 小时 11 分钟前
@loryyang AI 也不太行。。随时大小便留下文档,留下了就是一堆,相当于没有文档。。
JYii
12 小时 10 分钟前
这不是 AI 写了烂代码

只要是做金融相关,都知道金额不可能简单处理,有单独工具处理。AI 拉屎为什么不承认,而且还拉了两次,一定规模公司,今年绩效早没了
aureole999
12 小时 8 分钟前
你这文章不是 ai 写的段子?金额相关的用整数大整数不应该是常识么。claude 也不会犯这种错误
JZen
12 小时 8 分钟前
遇过类似的问题,项目遇到一个坑,开了一个 session 让 AI 修了好久才修好。
后面开新 session 做新功能时,AI 又踩到这个坑了,又要回去找原来的 session 把坑填回去。
现在我是针对某个模块专门写开发文档,把这个模块的坑记录上,以后 AI 要改这个模块就读一下这个文档,并且把新的坑记录上。
guidao
12 小时 8 分钟前
修了 bug ,没补测试呀。看来 AI 时代还是需要测试驱动。
huoru
12 小时 5 分钟前
@JZen 试试 mainline ,agent 自动记录到 git notes ,而且会同步到项目以及同事上。就不用再维护文档了
elehayym1618
12 小时 5 分钟前
补单测啊,修复的 bug 为什么没有单元测试的覆盖,没有单元测试覆盖的 bug 修复你修了也等于没有修
cowcomic
11 小时 56 分钟前
我的经验是这类问题要留注释
这属于关键注释,就算不给 AI 看,没注释人看也迷糊,翻 gitlog 太麻烦
cocong
11 小时 51 分钟前
现在的 AI 是概率模型,随着时间的积累,错误只会越来越多,终究是离不开人。
winnerczwx
11 小时 48 分钟前
这种问题如果你当时把方案给 AI 让他根据方案修, 它大概率会把价格计算抽成一个公用方法, 需要计算价格的地方全部调这个方法.

重点来了, 你需要在这个公用方法上明确写明注释(你可以自己写, 也可以让 AI 写), 这个地方的改动是为了修复 xxx bug

后续 AI 读到这边大概率就不会乱改了.
BenHunDun
11 小时 44 分钟前
好奇,是否可以封装函数成为工具类, 和针对这个问题,能够有够多的测试用例完成覆盖?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://study.congcong.us/t/1211151

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX