ARTICLE · 1061876
我的AI助手修好了一个根本不存在的bug,还自信地提交了
一个工程师做了个 AI Agent,专门自动修复文档里跑不通的代码示例。
它第一次跑真实仓库,就"修好"了一个根本不存在的 bug:
它把一行能正常工作的代码,一字未改,然后作为"已验证的修复"发布了出去。
它测试了一个片段,判定它是坏的,"修复"的方式是原样返回同一段代码,再测一遍,判定通过,然后往你的分支推了一个"空改动"提交。
思路本身没错:证据门禁
这个叫 DocuPatch 的 Agent,设计上其实很讲究。
它扫描 GitHub 仓库的 Markdown 文件,提取 Python 代码块,在沙箱化的 Docker 容器里跑;把真正坏掉的送去做修复;在同一个沙箱里重新验证修复是否有效;只有这之后才发布分支或 PR。
作者管核心设计原则叫"证据门禁"(evidence-gating):没有沙箱运行证明它是坏的,就不送给大模型;没有沙箱复跑证明修好了,就不发布。
这个原则本身是对的,也扛住了考验。真正没扛住的,是沙箱本身。
它是怎么骗过自己的
第一次测试:tqdm 仓库。 两个文件被"修复"并发布,纸面上是个胜利。但作者打开 diff 一看——改动前后两行一模一样。模型什么都没改,只是逐字节重新生成了同样的代码。
这不是"修复器"的 bug,而是测试层的一次假阳性。 那段 tqdm 代码是再标准不过的用法,它压根没坏。那沙箱为什么会说它失败?
作者当时没深挖,记了个"可疑"就往下走了——因为他想要第二个数据点再下结论。
第二次测试:click 仓库。 这次的模式藏不住了。
六个不同的文件,每个都跑三轮"反思循环",每一次的失败都是同一个错误:ModuleNotFoundError: No module named 'click'。
这就是那个"破案线索":如果六个不同文件、六次独立的模型尝试,全都栽在同一个 import 错误上,那它们的共同点就不是代码——而是底下那个环境。
真正的根因,和一次"跑偏"的插曲
翻到沙箱代码,真相大白:它把仓库挂进容器,然后把 PYTHONPATH 直接指向仓库根目录。
如果包文件夹正好在顶层,这没问题;但如果包藏深一层,就完蛋。
而 click 恰好如此:它的 pyproject.toml 把源码放在 src/click/,而不是根目录下的 click/——这是一种极其常见的现代 Python 打包惯例。指向根目录的 PYTHONPATH,压根找不到 click 文件夹。
更妙的是,作者手动验证时发现了第二层问题:就算指对了文件夹,import 通了,click.__version__ 依然会崩——因为现代 click 通过 importlib.metadata 取版本号,而那需要"真正安装"后产生的元数据,光靠 PYTHONPATH 的路径技巧是变不出来的。
于是两个 bug 叠在一起:路径指错了 + 路径技巧根本替代不了真安装。
还有一段插曲也很值得记:作者花了大量时间,坚信这是 Windows 上 Docker 挂载的问题,反复测试引号、路径格式、命令行参数。而真正的原因是——他的测试文件夹是空的。 早先一次 git clone 静默地没把内容拉下来,他却从没检查过。dir 返回空,不是"还没答案",那就是答案,他路过了三次才真正停下来看。
修法:别再猜路径
真正的修复不是"更聪明的路径猜测",而是根本不猜:用 pip install -e . 让项目自己的配置决定包在哪,同时写入真实的安装元数据。
作者还立了个很实用的规矩:如果没有打包元数据、只能退回 PYTHONPATH 模式,那么当片段因为项目自身包无法解析而失败时,应该标记为"环境错误(ENV_ERROR = 我们判断不了)",而不是"失败(FAIL)"。 承认"测不准",比给出一个假结论更诚实。
修完之后重跑 click,六个原本全挂的文件终于得到真实机会——其中一个暴露了 click 自己文档里一个真实存在、此前从未被发现的 bug:示例代码里把参数 fin 写成了 input,读起来很像变量名,可一旦真跑就会失败。
DocuPatch 抓住了它、确认是真的失败、生成修复、复跑验证通过,然后才发布。这才是这个项目真正的价值兑现——在一个真实案例上,而不是合成的玩具上。
给你的判断卡
① 先看分类,再信结论。 一个"通过/已发布"的标签,它的含金量只等于背后那道检查。tqdm 那个空改动,作者只有打开了 diff 才发现——别信绿灯,看证据。
② 重复出现的同一失败,指向一个共同根因。 六个文件、六次独立尝试、同一条错误信息——这是系统级信号,不是六个各自的 bug。遇到这种模式,去查环境,别一个个改代码。
③ 先观察,再理论化。 作者在 Docker/Windows 路径理论上烧掉大量时间,最后发现是测试文件夹空的。在你构建精妙假设之前,先确认手里的事实是真的。
④ 允许系统说"我判断不了"。 ENV_ERROR 这个设计很值得学:当环境不可靠时,诚实输出"无法判定",远好过给一个看似确定的假答案。这与很多 AI 产品的通病恰好相反。
一句话封装:这个 Agent 最大的价值,不是它修好了什么,而是它先把自己是怎么骗过自己的全招了。一个会承认"环境有问题、我判断不了"的系统,比一个永远自信的系统可靠得多。
原文:Malak Nabeel Khan《I built an AI Doc-Fixing Agent. Here's every way it fooled itself first.》,转载改写自 Medium · 项目地址 github.com/malaknabeelkhan/docupatch-agent