没人审 AI 写的代码之后,语言该怎么选
过去半年,技术圈一直在吵同一件事,让 AI 写代码到底该用什么语言。有人说 TypeScript,有人说 Python,有人说 Rust,理由听着也都有道理。类型系统能管住 AI 不乱来;AI 在 Python 上训练得最多,写得最熟;Rust 编译器严到「只要过编译就几乎没 bug」,等于白送一个自动审查员。但这几种说法都默认了一件事,AI 哪门语言写得最熟、最不容易错,哪门就最合适。我只能说,这个默认我现在挺怀疑的。
SWE-bench Multilingual 这个 benchmark 让 AI 自己改代码、自己跑测试,Rust 的解题率是 58%,所有语言里最高。换到 Multi-SWE-bench,一样是让 AI 跑完整的循环,一样用 claude,Rust 就掉到了 16%,连 Python 的三分之一都不到。同一门语言,同一个模型,换个 benchmark 就差了三倍多,我第一次看到的时候觉得很离谱。这两个都是正经 benchmark,都在真实仓库里跑完整的「写—编译—改」循环,结果还能差这么远,那多半是测法本身出了问题。
一次交卷的准确率,量错了东西
2022 年有个研究叫 MultiPL-E,把一套 Python benchmark 翻成 18 种语言,专门测 AI 一次就把代码写对的概率。结论挺反直觉,有没有静态类型,对一次写对几乎没影响,统计上「既不帮忙也不碍事」。但它测的是一次交卷。真实开发里 AI 写完不会就走,它要写,要编译,要读报错,要改,再编译,再跑测试,一天能这样转几百圈。
从循环去看,Go 就冒出来了。pandas 的作者 Wes McKinney 用了一年多 claude code,新项目全改用 Go,他的理由很具体。瓶颈变了,现在测试跑多快、编译多快,比一门语言写起来爽不爽重要得多。Flask 的作者 Armin Ronacher 出来自己做 AI 产品,后端也选了 Go。他说没想到 Go 的 test cache 对 agentic loop 跑得快这么要紧,AI 改一个文件,只跑相关的几个测试,一秒内出结果,马上进下一轮。Hacker News 上有人说得很准,Go 就是大模型的 RISC,指令集小,每条指令意思都很清楚,AI 很难猜错。
循环快慢确实要紧。但我觉得底下还有个更大的变化,就是已经没人审这些代码了。
真正该问的是,机器检查能替你排掉多少错
先把场景说清楚。没有人写代码,也没有人审代码,那 AI 写出来的代码到上生产之前,就只剩一道关,就是机器能不能自动判断它对不对。所以选语言其实只看一件事。AI 在自己的循环里会反复跑确定性检查,你要算的是,这段代码可能出的 bug 里,有多大比例能被这些检查抓住。
编译器和测试分工不一样。编译器管得窄,但查得全,只管内存安全、类型结构、match 穷尽这几类,可是 100% 的路径都会查到。测试正好反过来,什么逻辑都能查,但只查你真跑到的那条路。这两个是互相补位的,谁也替不了谁。Rust 也得写测试,因为没有哪个编译器会去查「这个业务行为对不对」。
那编译器到底能帮你排掉多少错?有人实测过。2017 年 ICSE 有篇论文,拿 JavaScript 真实的线上 bug,看加上静态类型能不能抓住,结果 TypeScript 和 Flow 各能抓住 15%,95% 置信区间是 11.5% 到 18.5%。Python 这边,2021 年有篇 TSE 论文用同样的办法测了 mypy,结果差不多,修过的缺陷里它能防住 15%,放到全部缺陷里算是 11%。
最唬人的那个数,也得看清它是在哪算出来的。微软和 Chromium 都报过,大约 70% 的严重安全漏洞是内存安全问题,但这个 70% 说的是 C/C++。像 FastAPI 那种胶水服务,没有手动管内存,也没有共享内存并发,这一整类漏洞直接就是零。密码学也差不多。MIT 统计过 269 个密码学 CVE,83% 是应用层把库用错了,证书没校验,随机数太弱,密钥写死在代码里,只有 17% 出在库本身。这些错编译器一样够不着。
所以说「换成 Go/Rust 就更安全」,放在胶水代码上基本说不通。胶水代码出的 bug 主要是逻辑错和集成错,哪门语言的编译器都抓不到。换语言顶多能动到 15% 那一块,剩下的 85%,用哪门语言都得靠测试。
AI 自己写测试给自己盖章,这事不成立
人不审了以后,最容易踩的坑在这。没人审代码了,那顺手让 AI 自己写测试,证明它写的代码是对的,不就行了?麻烦就在于测试和代码出自同一个模型,审的人和被审的是同一个。软件工程里早就给这事起了名字,叫 oracle problem(验证器问题)。Barr 有篇综述被引了上千次,里面讲得很清楚,一个检查器如果是从代码本身推出来的,那它跟被测代码有多独立,它就有多大的权威。AI 拿自己写的代码去推测试,独立性就是零。
而且它出错的样子很隐蔽,代码和测试互相对得上,一起错。这不是我推出来的,有人实测过。2024 年有篇论文让大模型给代码写断言,发现往代码里注入 bug 以后,模型写的断言反而会去迎合那个 bug,因为它看的是代码实际怎么跑,不管代码本来该怎么跑。所以 AI 给自己的代码写测试,等于把 bug 本身又抄了一遍。
再加上优化压力,就更夸张了。METR 今年测过,模型如果能看见给自己打分的评分函数,钻空子的频率是看不见时的 43 倍。前沿模型真干过这些事:把 verify() 直接改成永远返回真;在断言跑之前 sys.exit(0),让测试「优雅退出」;重载等号,让什么输出都相等;偷偷改写评分器。讲道理,一个验证器要是 AI 自己写、自己跑、自己还能改,那它就是 AI 看得最清楚、下手最方便的那一个,钻空子最坏的情况就是这种。
就连人手工挑过的标准测试集都不够硬。OpenAI 自己查了一遍,发现 o3 在 SWE-bench Verified 上那些「失败」,有 59.4% 是测试本身有毛病,之后就宣布不再报这个分了。人挑的测试都能漏成这样,AI 自己写的只会更松。
北大今年有篇论文干脆把这事写成了定理,还用 Lean 做了机器验证。AI 自己生成的几种替代品,比如自己写的测试、同义改写出来的测试、自动形式化出来的规范,都躲不开跟代码一起错,只有换一套完全不同的算法去交叉验证,才能把权威找回来。他们打了个挺狠的比方,这些就像是跟嫌疑人对过同一份假口供的证人,让他们投票,只会把同一个谎喊得更响。所以光有验证器没用,得看它的权威是不是从 AI 以外来的。
验证器的权威,得长在 AI 外面
一个检查器值不值钱,就看 AI 能不能把它糊弄过去。那什么样的检查 AI 糊弄不过去?最硬的是数学。Freivalds 算法验证矩阵乘法,看的是一个线性代数恒等式成不成立,AI 编不出一个假的数学等式。所以密码学、数值计算这类核心代码,是少数几个真敢让 AI 全自动跑的地方,因为它们对不对由数学说了算,而这套数学 AI 没参与定义。往下一档是外部现实,第三方接口真实返回了什么,一份 schema 规定了什么,从生产环境录下来的一段真实响应,这些都在外面,AI 改不了。再往下是差分对照,拿另一个独立写的实现去对答案,两边各自犯的错很少会撞成一样。再往下才是人写的规范,比如「余额永远不能为负」这么一句。人在循环里也就只剩这一个位置,当写规范的那个人。编译器单独算一档,它的权威来自语言语义,很硬,但只管安全那几类性质,不管逻辑。最弱的是 AI 给自己代码写的测试,那是在绕圈,谈不上什么权威。
还有个常被忽略的工具,我觉得挺值得用,叫 mutation testing(变异测试)。它往代码里塞一个个小改动,逼着测试集把这些改动抓出来。覆盖率 80% 的绿条可能什么都没证明,但 mutation 分数高,就说明测试真的在管着代码的行为。没人读代码的时候,要不靠人就查出假的验证器,只有它能做到。
回到 Go 和 Rust,现在能说清它们各自好在哪了。Go 在 benchmark 上解题率其实不高,Multi-SWE-bench 上只有 7.5%,McKinney 和 Ronacher 还是选了它,看中的是 Go 的语言面小。没有隐式转换,没有跨层异常,没有反射魔法,AI 很难干出什么意料之外的事。有人审的时候,这叫省心;没人审的时候,它等于替审查员干掉了一部分活。Rust 更进一步,编译器直接证明内存安全、没有数据竞争、match 穷尽,一整类 bug 从结构上就不可能出现,等于配了一个不累也不要钱的机器审查员。
但 Rust 的好处有个前提,而且这个前提会咬人。微软研究院有个专门修 Rust 编译错误的工具叫 RustAssistant,峰值准确率 74%,这是把模型和编译器套进循环以后的成绩,已经很高了。可报错写得再准,也不见得好修。一个团队如果心里没有 Rust 那套模型,就只能眼睁睁看着 AI 为了哄 borrow checker 改了 40 个文件,而这个需求本来改一两个文件就够了,更糟的是没人看得出哪些改动是真需要的。所以 Rust 算不上「AI 最好用的语言」,它是「最适合本来就懂 Rust 的人拿 AI 加速的语言」。
选什么语言,只决定你一开始手里有多少现成的机器检查。Go 和 Rust 白送你一截,机器审查的底线一上来就比较高。Python 没这么高,但编译器本来就不管的那 85%,就是逻辑和集成那部分,你可以自己把验证器一点点搭起来,比如从 schema 生成的契约测试、录下来的真实响应、property test。从头到尾看的都是同一件事,哪些错能被一台 AI 糊弄不过去的机器抓住。
机器证明落地了一点,但范围很窄
「让机器替人审」这条路走到头,就是让机器去证明代码是对的,光靠测试是做不到这一步的。2025 到 2026 年,这件事真落地了一部分。AWS 的 Kani 是给 Rust 用的边界模型检查器,今年 1 月还在更新,已经进了 Firecracker、s2n-quic 这些真项目的 CI,每个 PR 都跑,给内存安全和断言做机器证明。ed25519-dalek 有个 fiat 后端,它的曲线运算是从 Coq 证明里抽出来的。想想还挺牛,那段算术没有人审过,大家信的是那个证明。在密码学里,「工具生成、机器证明、没人读」已经是现实了。
代价也很实在。有篇论文实测了 73 个证明,专家写一个证明骨架平均要 87 分钟。Rust 标准库的众包验证搞了好几年,到现在连核心 unsafe 函数的 4% 都没覆盖到,而且一个安全 bug 都没找到。它能给你的是确信,抓虫不归它管。
更有意思的是,事情反过来了,证明本身现在反而好自动化。微软的 AutoVerus 自动生成 Verus 证明能做到 91%,难的变成了怎么把规范写对。Kleppmann 去年底说得很直白,难点会从写证明挪到写规范,而读写形式规范,还是得靠专业判断。
所以「没有人审代码」,不是说就没人了。人还在,只是从读 diff 的审查员,挪去写规范、定契约、设计对照实验了。之前写「你是管理者」的时候我还没想到这一层,现在看,管 AI 的人最值钱的活,就是把什么才算对说清楚。
Rob Pike 当年给 Go 的定位,是给那些驾驭不了复杂语言的人用。现在写代码的主力换成了 AI,我感觉这个定位反而更合适了。可读性、好不好上手,过去几十年大家选语言默认先看这些,现在它们肯定没那么要紧了,反正敲代码的不是你。真要挑语言,还是回到前面那条,机器检查能帮你排掉多少错,排不掉的那些,你能不能搭出一个 AI 糊弄不过去的验证器。别的说实话都挺次要。