ETH苏黎世联邦理工&伯克利大学联手破解AI写代码的"盲区"
创始人
2026-07-28 01:33:43

这项由ETH苏黎世联邦理工学院、INSAIT索菲亚大学、加州大学伯克利分校共同完成的研究,于2026年7月以预印本形式发布,论文编号为arXiv:2607.13921,发表在计算机编程语言领域(cs.PL)。感兴趣的读者可通过该编号在arXiv上查阅完整论文。

一、当AI写代码遇上"亡羊补牢"的困境

程序员们有一个共同的经历:写了几百行代码,满心期待地按下编译按钮,结果屏幕上喷出几十条红色报错信息。这时候你才意识到,问题早在第10行就埋下了,但后面那200行全是建立在这个错误假设上的废物。这就是所谓的"错误雪球"效应——一个小错误,在没人察觉的情况下不断滚大,最终演变成一场灾难。

现在,AI写代码遇到了同样的问题,甚至更严重。当今最强大的AI大语言模型(可以理解为一种自动完成代码的超级智能)在生成代码时,是从左到右、一个字符一个字符地往外输出的,就像一个人在写作文时从第一个字写到最后一个字,中间完全不回头检查。一旦写出了一个错误的假设,后面所有的内容都可能建立在沙滩上。

研究团队把目光投向了Rust这门编程语言。Rust是近年来极受追捧的系统编程语言,它最大的特点是有一套极其严格的安全规则——这套规则能在程序运行之前就发现潜在的内存安全问题。然而,正因为规则太严,AI在生成Rust代码时频繁"踩雷",生成的代码往往根本无法通过编译器的检查。

目前解决这个问题主要有两条路。第一条路叫做"事后反馈":等AI把整个代码文件写完,再拿去给编译器(可以理解为检查代码合法性的裁判)检查,如果不通过,就把错误信息反馈给AI,让它重来一遍。这条路最大的问题就是"亡羊补牢"——错误早就犯了,反馈却来得太晚,AI可能已经在错误的基础上又写了几百行代码。第二条路叫做"约束解码":在AI每输出一个字符的时候,就实时检查这个字符能不能要,如果不合法就强制它换一个。这条路听起来很美,但问题是它需要把Rust编译器从头到尾重新实现一遍,工程量极其庞大,而且完全不支持那些只对外提供接口、无法查看内部机制的商业AI(如GPT、Claude等)。

研究团队的贡献,就是找到了这两条路之间那条几乎被所有人忽略的中间路径。

二、"密封"这个天才想法:让不完整的代码也能被检查

核心思路其实出奇地简单,一旦听懂了,你会觉得"这为什么之前没人做过"。

假设AI正在写一个函数,才写到一半,后半截还没出来。正常的Rust编译器面对这种半截代码会直接报语法错误——"这不是一个完整的程序,我没法检查"。研究团队的解决方案是:在这半截代码后面,机械地补上一些"占位符",把它变成一个语法上完整的程序,然后再交给编译器检查。

这个"补全"的过程,研究团队给它取了一个名字,叫做"密封"(Sealing),而执行这个操作的工具叫做"密封器"(Sealor)。密封器不是要猜测程序员接下来想写什么,它只是做最简单的机械补全——把没有关闭的大括号关上,把还没写完的表达式用一个"万能占位符"代替,让整个代码文件在语法层面上看起来完整。

用一个更直观的类比来理解:密封器就像一个脚手架工人。当一栋楼还没建完的时候,为了检测现有结构的承重能力,他们会用脚手架临时撑住那些还没建好的部分,让检测工程师能够进场检查已建部分有没有问题。检测完成后,脚手架拆掉,建筑继续施工。密封器做的就是这个"脚手架"的工作——让编译器能够对半截代码进行有意义的检查。

为了让密封后的代码尽可能不引入新的假错误,研究团队设计了两种占位符。第一种叫做`holediv`,对应于Rust里的`panic!`——这个表达式的语义是"程序在这里崩溃,永远不会正常返回"。因为不会正常返回,编译器就不会要求后续代码满足各种借用和类型约束,从而避免了很多因为"后续代码还没写"而产生的假报错。第二种叫做`holeval`,它是一个泛型函数,调用时会被类型推断自动匹配成周围代码期望的任何类型,相当于"这里放一个任意类型的合法值"。有了这两个占位符,密封器可以在保持编译器检查有效性的同时,最大限度地减少因为"代码还没写完"而引入的误报。

三、不能误判:密封器必须满足的两个保证

密封器要能真正发挥作用,必须满足两个关键性质,研究团队用数学语言对它们进行了严格的定义。

第一个性质叫做"完备性"(Completeness)。用大白话说:只要一段半截代码存在某种合法的写法能让它变成完整的程序,密封器就绝对不能把它拒绝掉。这是最重要的保证——如果AI正在写一段本来可以成功的代码,密封器却提前给它判了死刑,那AI就会无缘无故地被打断,开始朝错误的方向修改,最终越改越乱。完备性保证了密封器的"不冤枉好人"原则。

第二个性质叫做"健全性"(Soundness)。用大白话说:如果密封后的程序通过了编译器检查,那就说明原来那段半截代码确实存在某种合法的延续方式。换句话说,如果密封器接受了某段代码,这个接受是有实际依据的,不是瞎猜的。健全性保证了密封器的"不放过坏人"原则,当然,这个原则可以在一定程度上放松——因为密封器即使漏掉了一个错误,后面还有机会再查。

研究团队明确指出,这两个性质并不需要同时在所有情况下都完美成立。完备性是更重要的——绝对不能冤枉好代码。健全性则可以在特定的、重要的情况下保证即可。比如,他们证明了在"语句边界"这个时刻(也就是AI刚好写完一条完整的语句、准备开始下一条的时候),密封器是完全精确的:它接受就代表能继续,它拒绝就代表真的有问题。

四、从理论到实践:先在小型语言上打磨,再攻克真实Rust

为了确保这套方法的理论基础坚如磐石,研究团队没有直接上来就对着真实的Rust动刀,而是先在一个叫做"羽毛量级Rust"(Featherweight Rust,简称FR)的迷你版语言上进行了完整的数学证明。

FR就像是Rust的精简玩具版——它保留了Rust最核心的特性:变量的移动语义(用过一次就没了,就像把一个苹果递给别人,自己手里就没了)、借用机制(暂时把东西借给别人用,用完还回来)、共享借用和独占借用的互斥规则(要么很多人同时读,要么只有一个人写,不能同时读写)、以及词法生命周期(借出去的东西的"有效期"跟代码块的范围绑定)。虽然是玩具版,但这些特性已经足够验证密封器的核心思路是否正确。

研究团队不仅设计了FR的密封器(命名为SFR),还用Lean这个定理证明工具把所有证明全部机械化地检验了一遍。所谓机械化证明,就是把数学推导翻译成计算机能检查的代码,让计算机来验证每一步逻辑是否正确,完全排除人为失误。在这个过程中,研究团队还意外发现了原始FR论文中的几处错误——包括类型系统中对代码块规则、变量声明规则、赋值规则的细节缺失——并在自己的机械化版本中进行了修正。

在FR上打稳基础之后,研究团队把同样的方法论移植到了真实的Rust语言上,构建了名为SRS的Rust密封器。真实Rust比FR复杂得多,需要处理的问题涉及方方面面:代码块和语句的密封规则(用`holediv`强制分支收敛,用`holeval`填充值位置)、条件语句(if/else中缺失的分支用`holediv`代替,使整个表达式类型合法)、循环语句(循环体用`holediv`结尾,避免编译器对循环体的跨迭代一致性检查)、函数调用(先查询函数的参数个数,再用`holeval`补全缺失的参数)、字段访问(对部分写完的字段名,先保留接收者,用引用形式防止意外移动)等等。

真实Rust还有一类特殊的挑战:某些错误只能在代码写完之后才能真正判断。比如,一段代码可能引用了一个还没写到的函数,或者某个类型现在还不明确、要等后续代码推断出来。对于这类"未来依赖型错误",密封器会暂时压制它们,等代码全部生成完毕后再彻底放行,确保不会因为"后半段还没写"而误杀那些本来合法的前半段。

五、七大模型、两类任务:实验结果说话

研究团队对生成的这套"生成式编译"系统进行了大规模实验评估,横跨七个当前最强的编程AI模型:三个商业黑盒模型(Claude Opus 4.8、GPT 5.3 Codex、Gemini 3.5 Flash)和四个开源模型(Kimi K2.7 Code、GLM 5.2、Qwen 3.5 397B参数版、Qwen 3.5 9B参数版)。

实验选择了两类极具挑战性的任务。第一类叫做"Translation",也就是C语言转Rust:给AI一份C语言写的库,让它翻译成对应的Rust代码。这个任务之所以难,是因为C语言的内存管理方式和Rust截然不同,翻译时需要对数据结构的所有权进行大量的重新思考和设计。第二类叫做"UpdatedAPI":给AI一个使用了某个Rust第三方库的代码框架,但这个库的API已经在AI训练结束之后更新了,AI需要根据编译器反馈的错误信息,自己推断出新API的用法并修复代码。

实验对比了三种方案。纯LLM方案是直接让AI生成代码,不做任何编译检查。PC方案(Post Compilation,事后编译)是等代码全部生成完再检查,出错就反馈给AI重新生成,最多允许若干次循环。GC方案(Generative Compilation,生成式编译)则是在生成过程中实时检查,一旦发现无法继续修复的错误就立刻反馈给AI重新开始这次生成,同时还保留了最后几轮事后编译反馈作为兜底。

结果相当显著。在没有任何编译反馈的情况下,AI生成的代码中有高达65.9%无法通过编译,最极端的情况(Qwen 9B在Translation任务上)达到了85.5%的失败率。引入事后编译反馈后,失败率降到了20.7%,说明编译反馈本身确实很有用。而加入生成式编译之后,失败率进一步降到13.1%。在全部14个"模型×任务"组合中,生成式编译在编译错误率上有13个比事后编译更好或持平,其中9个差异在统计上显著。

功能正确性方面也有明显提升。生成式编译在14个组合中的11个取得了最高的功能正确率。最亮眼的成绩包括:GLM 5.2在UpdatedAPI任务上从53.3%提升到71.7%,Kimi K2.7在Translation任务上从39.9%提升到53.9%。

出乎意料的是,生成式编译不仅没有让整体耗时增加,反而降低了平均耗时。相比纯LLM,事后编译平均增加了233秒的额外耗时,而生成式编译只增加了135秒。原因在于:生成式编译会在发现无法修复的错误时立刻叫停当前这次生成,避免AI把一个错误的文件写到底——写到底再检查,不仅浪费了生成这段代码的时间,还要再花时间理解错误、重新生成。对于Qwen 9B,生成式编译直接将Translation任务的平均耗时从879秒砍到了357秒,节省了一半多。

六、深入解剖:生成式编译到底做了什么

实验结果之外,研究团队还对生成式编译的工作机制进行了细致分析,揭示了它为什么能取得这样的效果。

首先是"错误雪球"的遏制。在使用生成式编译的所有任务中,85.3%的任务在生成阶段(最多10次重启)就完全解决了问题,从未需要动用事后编译这个兜底手段。其中55.4%的任务在生成阶段就直接拿到了正确答案。这说明大多数情况下,早发现、早纠正,代价远低于等到最后再处理。

其次是错误报告的"瘦身"效果。当一个完整文件被提交给编译器检查时,一个核心错误可能引发一连串的连锁报错,最终报错列表里几十条信息全部堆在一起。研究团队统计发现,事后编译产生的错误报告平均包含13.8条具体报错信息,在Translation任务中有时甚至达到几百条。而生成式编译在代码写到一半就发现了根源错误,此时产生的报告平均只有5.5条信息,其中65%的报告只含1到2条。报告越精简,AI就越容易找到真正的问题所在,修复的效率就越高。

最关键的分析是"错误检测时机"。研究团队对961个AI生成的错误文件做了仔细的复盘:他们先记录Rust编译器在完整文件上报告的第一个错误出现在哪一行,然后把同样的文件逐段喂给生成式编译,记录生成式编译是在哪一行最先发出警报。结果显示,在中位数情况下,生成式编译发出警报的时间只比最理想情况(假设存在一个神奇的全知裁判能在错误刚发生时就立刻察觉)晚了3行。四分之一的情况下,生成式编译甚至能在错误刚发生时就准确发出警报,一行都不差。

相比之下,另一个对照方案GCfn(只在完整函数写完后才检查)平均比最理想情况晚14行,而事后编译自然只能等到整个文件写完才给反馈,平均晚了89行。

更重要的是,生成式编译平均在文件完成33.3%的时候就发现了无法继续的错误,而这个理论下限是32.7%——两者几乎一致。换句话说,生成式编译避免了AI在明知无望的错误路径上浪费剩余66.7%的代码生成。

在被检测出的错误类型上,类型不匹配错误(E0308)是最常见的,占所有报告的三分之一以上。此外,生成式编译还能捕捉到更复杂的借用检查错误,包括冲突借用(E0502,也就是同时存在读借用和写借用的情况)、从借用值中移出(E0507,试图永久拿走一个只是临时借用的东西)等Rust特有的复杂错误。

七、系统的实际工程细节

研究团队把整套系统做成了实际可用的工程实现。密封器本身用Rust语言编写,代码量约5000行,与Rust编译器前端的约60万行相比,实现成本低得多。密封器基于rust-analyzer(Rust语言的官方语言分析工具)来解析半截代码的语法结构,然后按照密封规则生成密封后的完整代码,最后交给rustc(Rust的正式编译器)检查。

与AI交互的那一层用Python编写,约3000行代码。它负责从AI的流式输出中实时接收代码片段,触发密封器和编译器,并按照论文中描述的并发逻辑运行:AI继续生成代码的同时,编译器在后台检查上一个快照,如果发现问题就立刻发出信号,让AI停下来并给出错误信息。

由于密封后的代码和原始代码不完全一样(多了占位符,少了后半段),编译器报告的错误位置也是针对密封后代码的。为了让AI看到的错误信息对应原始代码,系统在密封过程中会维护一个位置映射表,把密封代码中的位置反向映射回原始代码中的位置,确保AI收到的反馈是针对它自己写的内容的,而不是针对系统自动补充的占位符。

整个系统有329个测试用例覆盖密封器和推理行为,并且包含了7个不同AI的接口适配层,以及用于评估的基准测试管理代码,合计约8000行Python代码。

---

归根结底,这项研究解决的是一个"反馈来得太晚"的根本问题。AI写代码就像在黑暗中画画——它不断往前走,却迟迟得不到反馈,于是一个小偏差最终变成了一幅面目全非的图。生成式编译做的事,相当于在这条走廊里每隔几步就打开一盏灯,让AI知道自己有没有走偏,而不是等走到头才发现自己一直走在错误的路上。

这个思路不只对Rust有意义。任何有严格静态规则的语言,只要能为密封器定义合适的占位符和密封规则,都可以复用这套框架。研究团队还指出,未来可以探索自动化地从语言规范中生成密封规则,而不是像现在这样手工编写——这将使整套方法能够随着语言的演进自动更新,甚至在全新语言发布之初就能为AI提供即时的编译反馈支持。

对于普通用户来说,这项研究意味着:未来当你用AI帮你写Rust代码时,你可能会发现AI的生成质量变得更稳定、编译错误更少、需要你手动修正的次数更少。这不是因为AI变聪明了,而是因为它终于有了一个实时陪跑、随时提醒的编译器伙伴。有兴趣深入了解技术细节的读者,可以通过arXiv编号2607.13921查阅完整论文。

---

Q&A

Q1:生成式编译和事后编译反馈有什么本质区别?

A:事后编译是等AI把整个代码文件写完再检查,出了问题再反馈。生成式编译则是在AI写代码的过程中实时检查,一旦发现不可挽回的错误就立刻告诉AI,避免AI在错误的基础上继续写下去。实验显示,生成式编译平均在文件完成33%时就发现了问题,而事后编译要等到100%完成后才能发现,节省了大量无效的代码生成。

Q2:密封器会不会产生误报,误判本来合法的代码?

A:研究团队对密封器设计了"完备性"这一核心保证:只要一段半截代码存在任何合法的延续方式,密封器就绝对不会拒绝它。这意味着密封器不会误杀好代码。在"语句边界"这个特殊时刻,密封器甚至是完全精确的,拒绝就代表真的有问题,接受就代表确实能继续。整套证明已经用Lean定理证明工具完整机械化验证。

Q3:生成式编译方法能用于Rust以外的编程语言吗?

A:可以。这套框架是语言无关的,只需要为目标语言实现一个对应的密封器——定义在代码不完整时如何用占位符补全、如何避免引入假错误。研究团队已经为理论上的简化版Rust和真实Rust分别实现了密封器,未来有望通过自动化方法从语言规范中生成密封规则,从而将这套方法扩展到其他严格类型系统的编程语言。

相关内容

热门资讯

海外巨头扩产验证存储高景气,半... 截至收盘,中证云计算与大数据主题指数上涨1.9%,中证芯片产业指数上涨0.4%,中证半导体材料设备主...
原创 首... 《电鳗财经》电鳗号/文 全球ETF市场正经历一场静默的革命。相关数据显示,截至今年上半年,全球主动...
【受AI热潮推动,贝莱德韩国E... 【受AI热潮推动,贝莱德韩国ETF连续第三周录得资金净流入】在全球部分最大科技公司的财报季到来前,投...
财中ETF风向标|电池ETF大... 今日A股市场集体上涨。在宁德时代披露半年报并宣布巨额股份回购注销计划的提振下,电池板块情绪显著回暖。...
ETF周评:黄金股ETF集体上... 上周A股市场整体呈现温和上涨态势,各大主要指数全线飘红,市场赚钱效应有所回升。其中,科创板表现最为抢...