對比AI程式碼審查中的上下文檢索方法
Compare The Market公司比較了兩種上下文檢索方法(GKG和RAG)用於AI程式碼審查。透過嚴格的評估,發現基於知識圖譜的GKG方法在程式碼審查質量指標上優於RAG,能夠更準確地理解程式碼結構和關係,減少誤報。
在Compare The Market公司,我們有一個內部AI工具,用於自動審查合併請求(MR),目標是加快開發人員接收程式碼反饋的速度,並提高整個組織的MR吞吐量。該工具在MR開啟後的幾分鐘內提供智慧的初輪審查,效果良好,但我們希望它更好。早期的審查者傾向於保守,由於孤立地審查程式碼更改,常常出現誤報。我們希望讓審查者瞭解更改在更廣泛系統中的位置,例如,刪除的函式可能看起來是死程式碼,除非知道它被另一個服務動態呼叫。
為此,我們面臨一個基本架構決策:代理應如何檢索程式碼庫的上下文?我們有兩個主要選項:
- GitLab知識圖譜(GKG):一種程式碼分析引擎,使用Tree-sitter AST解析構建結構化的程式碼實體和關係知識圖譜,儲存在Kuzu圖資料庫中。支援精確查詢,如“查詢此函式的所有呼叫者”或“顯示類層次結構”。
- 檢索增強生成(RAG):一種向量相似性搜尋方法,將程式碼分塊,建立嵌入,並檢索語義相似的程式碼片段。
我們基於直覺選擇了GKG——我們的假設是,程式碼審查需要程式碼關係的結構理解,而不僅僅是語義相似性。審查函式更改時,需要知道什麼呼叫它、它呼叫什麼,以及它如何融入整體架構。RAG擅長查詢“相似”程式碼,但相似性不等於程式碼審查的相關性。
本文驗證了這一直覺。透過使用Databricks上的MLflow進行嚴格評估,我們比較了四種方法,發現GKG在程式碼審查質量最重要的指標上優於RAG。資料證實我們的架構決策是正確的。
四種方法
我們評估了四種配置:僅基線、GKG、RAG、GKG+RAG。
GKG整合
去年,GitLab推出了GitLab知識圖譜(GKG)的測試版,這是一個模型上下文協議(MCP)伺服器。API索引倉庫並構建程式碼庫的結構化可查詢表示,將依賴關係對映到圖中的節點,理解函式定義及其用法,跟蹤繼承層次結構,並捕獲模組之間的交叉引用。結果形成程式碼的語義對映——不僅是一列檔案,而是一個關係網路。透過MCP伺服器提供的工具,AI代理可以即時查詢此圖,例如:“這個函式在哪裡被呼叫?”或“如果我改變此方法簽名,會有什麼影響?”
由於GKG仍處於測試階段且未作為原生GitLab CI/CD功能提供,我們構建了一個單獨的側車服務——一個輕量級Docker容器,包裝官方GKG二進位制檔案,在CI管道中與審查者一起執行。工作流程包括:當MR管道啟動時,側車容器掛載專案原始碼並索引整個程式碼庫,從頭構建知識圖譜;索引完成後,它在本地埠啟動GKG MCP伺服器,暴露一組工具呼叫;我們的AI審查者連線到MCP伺服器並在審查工作流中使用這些工具。
GKG構建一個符號圖,其中節點表示程式碼實體(類、函式、變數),邊表示關係(呼叫、繼承、匯入)。當GKG索引倉庫時,它建立互動式圖視覺化,顯示整個程式碼庫結構。每個節點型別代表不同的程式碼實體:橙色(目錄)、綠色(檔案)、紫色(定義,即程式碼中定義的類、函式和方法)、藍色(匯入符號,即外部依賴和匯入)。邊顯示關係:哪些檔案包含哪些定義,哪些函式呼叫其他函式,哪些模組匯入哪些符號。
例如,考慮一個簡單的UserService類。GKG將其對映為一個圖,顯示類、其方法以及呼叫這些方法的所有檔案。當AI審查者需要了解更改的影響時,它會查詢GKG。例如,如果有人修改validate_input(),代理會問:“誰呼叫這個函式?”這種精確資訊使審查者能夠評估對validate_input()的更改是否會破壞任何呼叫者——僅憑差異本身無法做到這一點。
RAG整合
我們的RAG實現使用LlamaIndex進行智慧程式碼分塊,並使用OpenAI嵌入進行向量相似性搜尋。索引管道掃描倉庫中的程式碼檔案,使用LlamaIndex的CodeSplitter(基於AST感知的分塊)將程式碼分割成語義單元(函式、類),然後透過OpenAI text-embedding-3-small生成嵌入,並儲存在FAISS向量儲存中。檢索管道根據查詢嵌入檢索最相似的程式碼塊。
CodeSplitter採用圖優先方法:首先構建程式碼的AST圖,然後利用結構理解建立語義上有意義的分塊。與樸素文本分塊不同,它識別語義邊界:函式邊界(每個函式成為一個塊)、類邊界(類定義及其方法)、邏輯分組(相關程式碼保持在一起)。
然而,RAG在程式碼審查中可能遇到根本限制:它依賴向量空間中的語義相似性,這對於自然語言效果良好,但對於程式碼有以下固有侷限:
- 無法進行符號解析:無法區分同名的函式定義和函式呼叫。
- 無法跟蹤引用:審查函式更改時,無法可靠地找到所有呼叫者。
- 分塊邊界問題:即使有AST感知分塊,重要上下文可能被分割到不同塊中。
- 精確度與召回率的權衡:語義相似性最佳化可能返回“相關”但不相關的程式碼,給LLM帶來噪音。
評估設定
評估生成式AI審查者不同於評估分類器或搜尋引擎。程式碼審查沒有規範的正確答案——兩位專家工程師審查同一MR會寫出不同但同樣有效的反饋。因此,標準準確率指標不適用。質量是多維度的:審查可能識別正確的風險但誇大嚴重性,可能完美校準但錯過關鍵問題,或發現正確問題但指向錯誤程式碼。
我們構建了一個包含79個真實MR的金標準資料集,每個條目由專家註釋了多維度地面真相:預期摘要點、預期問題、預期內聯評論(與特定檔案和程式碼行繫結)、預期分數範圍。這些MR代表真實的生產複雜性,涵蓋不同程式碼庫、更改大小和型別。
所有四種方法在同一79個條目上進行評估,每個生成的審查在五個核心質量維度上評分:
- 覆蓋率:輸出中包含多少預期摘要點、問題和內聯評論?
- 精確度:審查者發現的內容中有多少是真正必要的?這懲罰過度生成。
- 內聯評論位置準確性:程式碼審查本質上是空間性的;問題需要附加到正確的檔案和相關的程式碼更改。
- 分數校準:審查者分配的嚴重性分數(0-10)是否在人類認為合理的範圍內?
- 結構有效性:模式檢查確保必需欄位存在且格式良好。所有方法在此維度上均透過。
對於細微標準,如摘要是否“覆蓋相同點”,文本匹配失敗。我們使用LLM作為自動法官,根據參考期望和生成輸出計數預期點的命中數。
評估在MLflow和Databricks上執行,包括資料準備、比較和迭代最佳化。
結果與結論
GKG在覆蓋率和精確度上明顯優於RAG。GKG整合使審查者能夠查詢知識圖譜,驗證初步關注點,產生更準確、一致的反饋,並顯著減少誤報。RAG在語義相似性任務上表現良好,但不足以滿足程式碼審查的結構化需求。結合GKG和RAG並未顯著優於單獨使用GKG,表明GKG提供了審查所需的核心上下文。
這一架構決策使我們的AI程式碼審查工具從依賴表面語義轉變為理解程式碼庫的真實結構,從而為開發人員提供更可靠、更值得信賴的審查反饋。