這項由加拿大滑鐵盧大學、英屬哥倫比亞大學、NVIDIA、Verdent AI和Vector研究院聯合開展的研究,以預印本形式於2026年7月14日發布,論文編號為arXiv:2607.12463。有興趣深入了解的讀者可通過該編號在arXiv平台查詢完整論文。
在AI寫代碼這件事上,最難的其實不是"從零開始寫",而是"出錯之後能不能自己修"。
當一個AI編程助手在真實的代碼倉庫里工作時,它的日子並不好過。它需要先查看文件,再嘗試修改,然後運行測試——測試失敗了,報錯資訊撲面而來,它得讀懂這些錯誤,判斷是哪裡出了問題,然後再次修改。這個"行動→收到反饋→繼續"的循環,對AI來說是個真正的挑戰。主流的代碼語言模型在訓練時,基本上是按照從左到右、從上到下的方式閱讀代碼,它們對"先做一件事,收到外部結果,再繼續"這種節奏本質上缺乏感知。
研究團隊注意到了一個有趣的規律:AI助手在執行任務時經歷的"行動→收到工具返回結果→基於結果繼續"這個三步循環,和普通代碼里一個函數調用的結構幾乎一模一樣。當你寫了一行 `result = process(data)`,這裡發生的事情是:調用前的代碼設定了意圖和參數,`process` 函數被調用,函數內部做了一些你在外部看不到的計算,把結果返回給你,然後你用這個結果繼續往下寫。這四個步驟——背景、行動、外部計算的返回結果、後續處理——和AI助手執行任務的四個步驟是結構上完全相同的。
既然如此,能不能利用網際網路上海量存在的普通代碼,來訓練AI理解這種"行動-反饋-繼續"的邏輯呢?這就是這項研究的核心思路。
**一、問題所在:現有訓練方式留下了一個缺口**
要理解這項研究在填補什麼空白,先來看看AI編程助手是怎麼被訓練出來的。
整個流程大致分兩個階段。第一階段叫"預訓練",是把模型暴露在海量代碼面前,讓它像背課文一樣學會預測"下一個詞是什麼"。這個階段的訓練用的是網際網路上能找到的所有代碼,規模巨大,但方式很簡單——從左到右,一個接一個地預測。
第二階段叫"智能體後訓練",是專門針對"修復真實代碼bug"這類任務,準備一批AI執行任務的完整軌跡(行動記錄)來訓練模型。這個階段是最近幾年讓AI代碼修復能力大幅提升的關鍵。R2E-Gym、SWE-Smith、SWE-Lego這些知名的訓練框架都屬於這類方法。
兩個階段之間存在一個空檔:模型在第一階段培養了閱讀代碼的基本能力,但這種從左到右的訓練方式,天然地只讓模型練習了"往前看",沒有好好練習"收到外部結果之後怎麼繼續"。第二階段的任務軌跡數據雖然很有針對性,但數量有限,且很貴——需要大量人工或AI來生成這些軌跡。
研究團隊的想法是在兩個階段中間插入一個"中間訓練"階段,利用更廉價、更大量的普通代碼來建立一種慣性:讓模型在真正接觸任務訓練之前,就已經養成了"根據前後文推斷中間缺失內容"的思維方式。
這種"填空"訓練方法學術上叫"填中間"(FIM,Fill-in-the-Middle)。它的基本操作是:把一段代碼的中間部分遮住,讓模型根據前面的代碼和後面的代碼,把中間這部分補出來。這和完形填空非常相似,只不過不是一個詞,而是一整段代碼邏輯。
問題在於,之前的代碼模型雖然也用過這種填空訓練,但那些訓練都是隨機挖一段出來填,就像隨機撕掉一本書的某幾頁,有時撕掉的是一個完整的故事情節,有時撕掉的只是一句話中間的幾個字,參差不齊,對培養"理解函數調用結構"這種具體能力幫助有限。
**二、核心創新:按照函數來挖,而不是隨機挖**
研究團隊設計的方法叫"函數感知填中間中間訓練"。關鍵區別在於,它不隨機挖一段代碼,而是有針對性地挖掉一整個函數的函數體,讓模型根據調用這個函數的代碼、被這個函數調用的其他函數、以及函數本身的簽名和文檔,來推斷這個函數應該怎麼寫。
這個挖法之所以重要,是因為一個函數就是一個完整的"外部計算單元"——它收到參數,做一些調用者看不見的內部工作,返回結果。這和AI助手調用工具(比如執行終端命令、搜索文件)時的情形完全類似:AI看到命令返回了什麼,要根據這個返回結果決定下一步怎麼做。
為了選出"值得挖掉"的函數,研究團隊設計了一套雙重評分體系。可以把它理解成在一棟樓里選哪個房間做示範單位:房間得有足夠的內容展示(不能太簡單),但參觀者進來之前能從門口的標牌、周圍房間的布局推斷出裡面大概是什麼樣子(不能完全不可推斷)。
評估"值不值得挖"用的第一個指標叫"複雜度分",衡量的是這個函數本身有多少內容。計算方式綜合了三個維度:代碼行數(越長越複雜)、圈複雜度(代碼里有多少if/for/while這樣的分支,分支越多邏輯越複雜)、以及最深嵌套層數(if裡面套for裡面再套if這樣的層疊有多深)。這三個維度各有權重,綜合成一個0到2之間的分數。
第二個指標叫"可推斷分",衡量的是周圍代碼有多少線索可以幫助推斷出這個函數該怎麼寫。線索來源有五種:調用這個函數時傳入了什麼參數(越具體越好)、這個函數內部調用了哪些同文件里的其他函數(越多說明邏輯越有跡可循)、函數名和參數的類型註解有多描述性(名字越清楚越容易猜)、有沒有文檔字符串(有說明更好推斷)、以及這個函數在類里有多少兄弟方法共享狀態(兄弟越多上下文越豐富)。
最終的選取分數是把複雜度和可推斷性用類似調和平均數的方式結合起來——要求兩者都不能太低。一個函數如果很複雜但完全不可推斷,或者很容易推斷但太過簡單,都不是好的訓練材料。此外還有一個"難度懲罰":如果一個函數複雜度遠超可推斷性,說明即使給了全部上下文也很難推斷出來,這種函數會被降權,因為讓模型去猜一個連人類都猜不出來的答案,只會產生噪音。
除了單個函數,研究團隊還設計了"多函數組合"的挖法:同時挖掉2到3個有相互調用關係或同屬一個類的函數。這是因為真實的代碼修復任務經常需要同時修改多個相關函數,這類訓練樣本能幫助模型練習跨函數的邏輯推理。兩個函數的組合大約占訓練數據的15%,三個函數的組合占5%,其餘80%是單函數。
**三、用AI來生成思考過程**
光有"挖空填補"還不夠。研究團隊注意到,真實的AI助手在修代碼時,通常是先思考(輸出一段分析),再動手寫代碼。如果訓練數據里只有代碼本身,模型就只練了"動手",沒練"先想清楚再動手"。
所以在構建訓練樣本時,研究團隊引入了一個額外步驟:對於每一個被挖空的函數,先讓另一個強大的AI(谷歌的Gemini 3 Flash)只看前後代碼(不看被遮住的函數體),生成一段推理過程,說明這個函數應該做什麼,然後再給出對應的實現代碼。
之後用另一輪Gemini 3 Flash對這個生成的(推理過程,代碼實現)配對進行質量審核,評分維度包括:這個函數從上下文推斷出來是否合理可行(如果依賴完全無法從代碼中感知的外部知識,就標記為不可行)、代碼的正確性、可執行性、API使用是否恰當、可讀性和完整性。只有通過審核的樣本才進入訓練數據。
被遮住的真實函數體只用於審核,不出現在訓練目標中。模型學到的是:看到前綴和後綴,先輸出一段推理,再寫出實現。
這樣構建的訓練樣本格式是:`[前綴代碼]` + `[後綴代碼]` + `[推理過程] [函數體代碼]`。前兩塊是輸入,後兩塊是模型需要生成的目標。
**四、數據從哪裡來**
研究團隊從GitHub上精心挑選了968個Python代碼倉庫,作為中間訓練的數據來源。最初考察的候選庫大約有2000個,經過手動質量篩查後縮減。所有與SWE-Bench(用於評估AI修復真實GitHub Issue能力的標準測試集)來源倉庫有重疊的都被移除,每個倉庫只保留了在SWE-Bench測試集基準提交時間節點之前的代碼,確保不存在測試數據泄露。
最終篩出大約78000個符合條件的Python文件,從中生成了約40萬條填空訓練樣本,合計約26億個token(使用Qwen2.5-Coder的分詞標準計算)。其中單函數樣本約32萬條,雙函數組合約6萬條,三函數組合約2萬條。每個樣本的函數體平均長度約為34行代碼。這40萬條樣本全部配備了Gemini 3 Flash生成的推理過程。
這968個倉庫覆蓋了10個類別,從頭實現的項目、領域專用工具、算法庫、科學計算、小型框架、可視化與遊戲、教育類項目、編譯器、數據處理和網路安全都有涉及,許可證方面超過80%是MIT、Apache 2.0或BSD等寬鬆許可,其餘也均允許至少用於非商業研究。
**五、訓練流程:中間插了一個階段**
具體的訓練方式是:先取已經經過指令微調的基礎模型(研究中用了Qwen2.5-Coder-7B-Instruct、Qwen2.5-Coder-14B-Instruct和Qwen3-8B),在這26億token的填空數據上做一輪中間訓練,訓練目標只是推理過程加函數體這個部分,前綴和後綴不參與損失計算。訓練使用模型原生的填空特殊符號,打包到模型的原生最大上下文長度,跑一個epoch。
這個中間訓練階段用的超參數:學習率1e-5,餘弦學習率計劃,預熱比例10%,權重衰減0.05,每設備批量大小1,梯度累積16步,等效全局批量128,序列長度32768,混合精度bf16。
中間訓練完成後,再正常接上智能體後訓練階段(R2E-Gym、SWE-Smith或SWE-Lego),和沒有中間訓練的基準對比。
之所以不直接評估只做了中間訓練的模型,是因為填空訓練之後模型的指令跟隨能力會下降,沒法公平地和經過完整指令微調的基準比較。所有公布的數字都是完整流程(中間訓練加後訓練)結束後的表現。
**六、測試結果:每個配置都在變好**
研究在三個維度驗證了這個方法的有效性。
第一個維度是不同模型規模。在Qwen2.5-Coder-7B-Instruct上,中間訓練加R2E-Gym後訓練,在SWE-Bench-Verified上提升了2.8個百分點,在SWE-Bench-Lite上提升了3.67個百分點。在14B的版本上,同樣的流程帶來了3.0和4.0個百分點的提升。這說明更大的預訓練模型並沒有自然地"吸收"這種結構性偏置,中間訓練帶來的改進在兩個規模上都實實在在地存在。
第二個維度是不同的後訓練流程。在同一個7B基礎模型上,換用SWE-Smith代替R2E-Gym作為後訓練框架,中間訓練在SWE-Bench-Verified上的提升高達5.3個百分點(雖然在Lite上只提升了0.5個百分點,說明具體數字取決於後訓練流程和評測集的組合,但方向上始終是正向的)。
第三個維度是不同的基礎模型家族。換成Qwen3-8B加上SWE-Lego後訓練,中間訓練在SWE-Bench-Verified上提升了3.2個百分點,在Lite上提升了5.4個百分點。由於這裡同時換了基礎模型和後訓練框架,研究團隊謹慎地說這只能說明方法"不局限於Qwen2.5-Coder加R2E-Gym的特定組合",而不是對所有模型家族的普適性保證。
**七、意外收穫:通用能力的保留**
智能體後訓練有一個代價通常不被人提起:專攻代碼修復任務之後,模型在其他方面的能力往往會大幅退步。研究團隊專門測試了14B模型在六個額外基準上的表現,結果令人警醒。
只做R2E-Gym後訓練(不加中間訓練),模型在LiveCodeBench(純代碼生成能力測試)上比原始指令模型下降了13.1個百分點,在BFCL(函數調用能力測試)上下降了7.4個百分點,在FullStackBench-EN上下降了6.08個百分點,在τ-bench(模擬真實場景的工具使用測試)上下降了2.3個百分點。六個基準平均下來,後訓練之後模型失去了4.81個百分點的綜合能力。這是為了SWE-Bench成績支付的隱性代價。
加入中間訓練之後,情況發生了顯著變化。LiveCodeBench回升了11.1個百分點,OJBench(競賽編程測試)回升了1.94個百分點(僅距原始指令模型0.46個百分點),FullStackBench-EN回升了0.53個百分點,τ-bench回升了3.9個百分點,BFCL回升了2.4個百分點,Terminal-Bench 2.0回升了1.25個百分點。六個基準的綜合平均從16.04上升到19.56,同時SWE-Bench的增益也得到了保留。
更有意思的是τ-bench和BFCL的提升。這兩個測試里沒有任何Python代碼編輯相關的內容,而中間訓練的語料庫里也完全沒有工具使用相關的訓練數據。兩者的提升只能解釋為:函數調用結構和工具調用結構在某種深層次上是等價的,中間訓練建立的"接收外部返回結果並繼續"的認知慣性,通用地改善了模型處理任何"行動-反饋-繼續"循環的能力。
**八、逐一拆解:每個設計決策貢獻了多少**
為了驗證每個設計決策是否真的有用,研究團隊在7B模型上做了三組對照實驗,每組控制其他變量只改變一個因素,使用20萬條樣本的固定預算以確保可比性。這些實驗的絕對數字低於主實驗(因為數據量更少),但組內的相對大小是可比較的。
第一組對照是"加不加推理過程有多大用"。完全不加推理過程,只做填空結構訓練,平均分比基準提升1.18個百分點。換成讓被訓練的模型自己生成推理過程(而非用Gemini),提升增加到1.68個百分點的回收。用Gemini生成的推理過程,總提升是2.43個百分點。也就是說,填空結構本身貢獻了大約一半的改進,推理過程提供了額外的增益,但Gemini相比模型自身推理的額外貢獻只有0.75個百分點。這說明這個方法不僅僅是一個"蒸餾Gemini"的方案,填空結構本身就在干實事。
第二組對照是"怎麼選函數有多重要"。隨機挖函數只比基準提升了0.78個百分點;讓Gemini來判斷挖哪個函數,提升到了1.88個百分點;只用程序依賴圖關係來篩選(有調用關係的函數更傾向被選),提升到了1.68個百分點;加上複雜度過濾或可推斷性過濾各自有額外改進,兩者都用效果最好,達到2.43個百分點。這說明函數選擇的質量是決定中間訓練效果的關鍵變量,而且複雜度和可推斷性提供了互補的不同維度資訊。
第三組對照是"同時挖多個函數有沒有用"。只挖單個函數:提升2.43個百分點。加入15%的雙函數組合樣本:提升到2.73個百分點。加入5%的三函數組合(替換同等比例的單函數):提升到2.53個百分點。同時加入雙函數和三函數(80%/15%/5%的組合,也就是主實驗中用的配方):提升到2.93個百分點。多函數組合在幫助那些金標答案需要同時修改多個函數的任務上效果更明顯,但三個函數同時挖的邊際收益因為可推斷性大幅下降而受到限制。
**九、深入軌跡:模型到底學到了什麼**
為了理解改進從哪裡來,研究團隊分析了14B模型在SWE-Bench-Verified上的行為軌跡。
研究團隊定義了一個叫"從錯誤中恢復"的指標:一條軌跡被認為包含"負面觀察",如果在執行過程中任何工具的輸出匹配了錯誤模式(Python異常回溯、"未執行替換"、shell錯誤等);"恢復率"是指,在包含負面觀察的軌跡中,最終仍然成功提交了有效修復的比例。
基準模型(只有R2E-Gym後訓練)有88.8%的軌跡碰到了負面觀察,加了中間訓練的模型這個比例是91.8%——也就是說兩者看到的錯誤數量基本相當。但基準模型的恢復率是24.8%,加了中間訓練的是28.8%,高出了整整4個百分點(相對提升16%)。
加了中間訓練的模型還表現出更傾向"疊代驗證"的工作風格:在成功解決的任務上,平均編輯操作次數從3.3次上升到7.4次,平均軌跡步驟數從15.1步增加到23.6步。代價是碰到步驟上限的軌跡比例從7%上升到45%,但這些"超步"主要發生在未解決的任務上;在已解決的任務上,額外的步驟都轉化成了更正確的修復。
按照金標答案的補丁類型來分層分析,結果進一步驗證了研究的核心假設。在341個只需要修改單個函數的任務上,中間訓練的提升是2.1個百分點;而在88個需要同時修改同一文件里多個函數的任務上,提升高達9.1個百分點,是單函數任務的4倍多。這正好對應了中間訓練的內容——多函數組合填空訓練讓模型更擅長跨函數的邏輯推理。
在71個需要跨文件修改的任務上,兩個模型的表現幾乎一樣(約11.3%),沒有差異。研究團隊解釋說,他們的填空訓練是在單個文件內部進行的,跨文件協調能力沒有被直接訓練,所以在這類任務上沒有體現出優勢。
失敗模式的分布也有明顯變化。基準模型平均每輪評估有約11條軌跡以"空補丁"結束(AI完全沒有提交任何修改就放棄了);加了中間訓練之後,這個數字下降到約1條,基本消失。定位錯誤(提交了修改但沒修改對文件)輕微減少(131條降到約126條),補丁錯誤(改了對文件但測試還是沒過)基本不變(約227條)。所以增加的15個成功解決的任務,主要來源是那些"AI放棄了但其實有希望"的案例。
研究團隊的解釋是:在填空訓練中,模型總是被要求在前綴和後綴之間生成一個非空的內容。這種"必須生成內容"的慣性在後訓練之後存活了下來,讓模型不容易放棄、不容易直接交空卷。
**十、說說局限性**
研究團隊在論文中明確指出了四個邊界。
首先,整個訓練語料庫和測試基準都是Python。跨語言的遷移能力沒有被直接測試,Java、C++、Rust能不能受益,還不知道。FullStackBench-EN的間接證據顯示對多語言編碼有一定幫助,但不夠直接。
其次,默認配方依賴Gemini 3 Flash生成推理過程。雖然消融實驗表明模型自身生成的推理過程可以回收大部分收益,但對於想要完全開源復現的團隊來說,需要一個同等能力的開源教師模型,這不是隨手可得的。
第三,在非Qwen2.5-Coder模型上的驗證只有一個配置(Qwen3-8B加SWE-Lego),而且這個配置同時換了基礎模型和後訓練框架。所以只能說方法不局限於特定組合,但不能說它在所有模型家族上都保證有效。
第四,整個方法建立在代碼有良好模組化結構的假設上。如果是單體腳本、自動生成的代碼或Jupyter Notebook這種沒有清晰函數邊界的代碼,選函數的流程就找不到足夠的候選目標,這個場景沒有被系統研究。
說到底,這項研究提供的是一個可以在現有訓練流水線里"插入"的額外步驟,不需要修改後訓練框架本身,只需要在進入後訓練之前多跑一輪中間訓練。代價是額外的計算(研究團隊完整復現整套實驗大約需要5760個GPU小時,在8張H100上跑約30天),換來的是在代碼修復任務上穩定的3到5個百分點的提升,以及對通用代碼和工具使用能力的大幅保留——這種"修了一件事,順帶修了另外幾件事"的結果,在AI訓練里並不常見。
歸根結底,這項研究的出發點來自一個樸素的觀察:AI修代碼時需要的那種"看懂工具返回了什麼,然後繼續"的能力,其實在普通代碼里到處都是,只是之前的訓練方式沒有把這個結構顯式地暴露給模型。通過按照函數邊界來挖空填補,配合推理過程的訓練,再放在任務訓練之前的正確時間節點上,這種現成的信號就被有效地利用起來了。
有興趣深入了解細節的讀者,可以通過arXiv編號2607.12463查閱原始論文,研究團隊的代碼和數據集也已在GitHub的TIGER-AI-Lab/FIM-Midtraining倉庫公開發布。
---
Q&A
Q1:SWE-Bench是什麼,用它來測試AI修代碼能力靠譜嗎?
A:SWE-Bench是一個用真實GitHub Issue來測試AI代碼修復能力的標準測試集,分為Verified(500個經人工驗證的問題)和Lite(300個問題)兩個版本。測試題目都是從真實開源項目里抽取的,有標準的參考修複方案,評判方式是提交的修改能不能讓原來失敗的測試通過。這是目前業界公認的衡量AI代碼智能體能力的主要基準之一,被普遍認為比合成測試更能反映真實能力。
Q2:函數感知填中間中間訓練需要多少計算資源,普通研究團隊能復現嗎?
A:研究團隊使用8張NVIDIA H100 80GB顯卡組成的單節點伺服器,完整復現所有實驗(三個基礎模型的中間訓練、三套後訓練框架及其對應的中間訓練版本、消融實驗和多輪評估)大約需要5760個GPU小時,折合約30天。數據生成階段還調用了Gemini 3 Flash的API來生成推理過程,這部分有額外的API費用。代碼和數據集已在GitHub開放,感興趣的團隊可以選擇性地復現部分實驗而非全套,計算成本會相應降低。
Q3:為什麼只做SWE-Bench成績好的後訓練模型,在普通代碼生成測試上反而變差了?
A:這是專項後訓練的常見副作用,本質是"過度專業化"。後訓練用的數據都是AI修復GitHub Issue的完整行動軌跡,模型學習的是"在真實代碼倉庫里查文件、定位問題、做修改、看測試結果"這套特定工作流。這種分布的數據反覆訓練,會讓模型的參數逐漸向這個窄分布傾斜,擠壓掉一部分通用代碼能力和工具調用能力。中間訓練建立的函數調用結構慣性在某種程度上充當了通用能力的"錨點",所以加入中間訓練之後,專項能力提升的同時通用能力的退化也得到了部分抑制。






