宅中地 - 每日更新
宅中地 - 每日更新

贊助商廣告

X

ETH蘇黎世聯邦理工&伯克利大學聯手破解AI寫代碼的「盲區」:讓編譯器在代碼生成過程中實時陪跑

2026年07月27日 首頁 » 熱門科技

這項由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分別實現了密封器,未來有望通過自動化方法從語言規範中生成密封規則,從而將這套方法擴展到其他嚴格類型系統的編程語言。

宅中地 - Facebook 分享 宅中地 - Twitter 分享 宅中地 - Whatsapp 分享 宅中地 - Line 分享
相關內容
Copyright ©2026 | 服務條款 | DMCA | 聯絡我們
宅中地 - 每日更新