多模態融合搜尋:為每個查詢挑選正確的檢索方式
> 像「parseConfig 定義在哪裡」這樣的查詢,需要的搜尋方式和「auth 是如何運作的」截然不同。Maguyva 會先分類查詢意圖,據此為四種檢索模式加權,再以加權版的 Reciprocal Rank Fusion(倒數排名融合)演算法整合結果。
搜尋查詢從來不是單一種東西。
「parseConfig 定義在哪裡」想要的是精確的符號 — 一個精準的位置,而且要快。「authentication 是如何運作的」想要的是意義 — 是能解釋一個概念的相關程式碼分佈全貌。「如果我修改這個函式會壞掉什麼」想要的是依賴關係圖。「找出字串 ECONNREFUSED」想要的是字面比對,不需要耍任何花招。
Grep 在字面比對上表現優異,對某些參照搜尋也很有用,但它不是依賴關係圖,也不理解意義。嵌入模型涵蓋了語意層面,但對精確字串比對與影響分析來說,卻是用錯了工具。多數程式碼搜尋工具只會選定一種引擎,讓每個查詢都得將就這個選擇。Maguyva 不做這種取捨,而是先判斷你提出的是哪一類問題,再依該問題應得的比例,混合四種檢索方式。
四種模式
在底層,有四種各自獨立的方式可以找到程式碼:
- 語意(semantic) — 對 Voyage 二元嵌入向量進行向量搜尋,依意義找出程式碼。
- 文字(text) — 三元圖(trigram)比對,找出字面值、錯誤字串、精確識別碼。
- 結構(structural) — AST 查詢,找出定義、簽章與語言結構。
- 圖形(graph) — 依賴關係圖,找出呼叫者、被呼叫者與影響範圍。
每一種模式各擅長不同類型的問題,關鍵在於:針對眼前這個查詢,該給予每一種多少信任。
意圖分類
在任何檢索執行之前,一個輕量分類器會先將查詢歸入六種意圖之一,並附上信心分數。這個分類器刻意做得很精簡 — 依序套用啟發式規則,第一個命中者勝出 — 因為它運作在關鍵路徑上,只能增加一兩毫秒的延遲:
- 以
def、class、func、import⋯⋯ 開頭 → find_definition(信心 0.95) - 「who calls」「usages of」「references to」→ find_references(0.90)
- 「impact」「blast radius」「what depends on」→ impact_analysis(0.90)
- 加了引號的
"string"或像traceback這樣的錯誤代碼 → exact_match(0.85–0.90) - 一個
CamelCase或snake_case形式的識別碼 → find_definition(0.60–0.80) - 「how」「why」「explain」「architecture」→ understand_code(0.75)
- 都不符合 → understand_code,低信心(0.40)
每種意圖在四種模式上都有各自的權重設定,以下是實際數值:
| 意圖 | 語意 | 文字 | 結構(AST) | 圖形 |
|---|---|---|---|---|
| find_definition | 0.2 | 0.1 | 0.6 | 0.1 |
| find_references | 0.1 | 0.2 | 0.2 | 0.5 |
| understand_code | 0.5 | 0.2 | 0.2 | 0.1 |
| find_similar | 0.4 | 0.3 | 0.2 | 0.1 |
| impact_analysis | 0.1 | 0.1 | 0.1 | 0.7 |
| exact_match | 0.0 | 0.9 | 0.1 | 0.0 |
因此「parseConfig 定義在哪裡」會大幅偏重 AST(0.6);「auth 是如何運作的」會偏重語意向量(0.5);「什麼依賴這個」幾乎完全倚重圖形(0.7);「找出 ECONNREFUSED」則幾乎完全倚重三元圖比對(0.9),並完全關閉嵌入模型 — 因為對精確字串來說,語意相似度正是用錯了工具。
快速路徑,以及融合路徑
當分類器信心足夠 — 分數 ≥ 0.85 — 且查詢屬於一般情形時,Maguyva 會完全跳過融合,直接路由到單一主導模式。「X 定義在哪裡」不需要四種檢索器,它現在就需要的是 AST 索引。這條直達路徑會回報為 fusion_strategy: "direct"。
其餘一切模糊不清的查詢,都會經過融合程序。四種(或在預設集下為三種)模式會並行執行,各自回傳自己的排名清單,再由我們加以整合。
加權版 Reciprocal Rank Fusion
融合異質的檢索器,比聽起來還要困難:0.82 的餘弦相似度、137 的三元圖分數,以及 0.004 的圖形中心性,彼此並不在同一個量尺上,因此不能直接相加。Reciprocal Rank Fusion(倒數排名融合)繞過了這個問題 — 捨棄原始分數,只保留每個引擎所給出的排名。某項結果來自某一種模式的貢獻值為:
contribution = weight × 1 / (k + rank + 1)
其中 rank 是它在該模式清單中的位置,k 則是一個平滑常數。若某項結果被一種以上的引擎找到,各模式的貢獻值就會加總 — 檢索器之間的共識,自然會浮上頂端。我們在預設集中使用 k = 40,在 thorough 中則使用 60(quick 僅執行語意搜尋,因此融合機制在那裡從不啟動)。RRF 原始研究對一般用途檢索採用 k = 60;我們的預設值略微更銳利一些,讓排名靠前的模式間共識能獲得多一點權重 — 而我們不建議手動調整這個數值。
除此之外,結果還會帶有一項圖形重要性加成。一個樞紐節點 — 整個程式碼庫都仰賴的函式 — 即使在相同的文字相關性下,也應該排在不起眼的葉節點之前,因此我們會將每項貢獻值乘以:
boost = min(1 + 0.3 × ln(1 + centrality), 1.5)
中心性數值來自管線預先計算好的 PageRank/度數指標,且此加成上限鎖定在 1.5 倍,避免一個熱門函式完全蓋過另一個相關性更高、卻較不起眼的函式。最後,我們會將來自 vendor、build 與 archive 路徑的結果降權,並針對每個檔案去除重複,只保留最佳片段。
仍不完美之處
意圖分類器是一疊正規表示式,並非經過訓練的模型。它對常見形式的查詢處理得相當好 — 引入這項機制的決策紀錄顯示,零結果率從約 15% 降到 5% 以下 — 但它終究是啟發式的,一個真正模糊的查詢會落入 understand_code 與偏向語意的混合方式。這是一個安全的預設值,而非聰明的解法。我們沒有把它換成訓練過的分類器,因為這個精簡版本已經夠快、夠好,而且一個錯得自信滿滿的分類器,比一個誠實的備援機制還要糟糕。權重本身是人工選定的先驗值,並非從我們並未蒐集的點擊資料中學習而來。
問問題,而不是選工具
代理人不該需要自己判斷該用 grep、嵌入模型還是呼叫圖 — 它應該用直白的方式提出問題,並得到正確的答案。多模態融合,正是讓 find_symbol、語意搜尋與依賴分析,能共同藏身於單一查詢介面之後的原因:系統會讀懂問題的形狀,靜靜地為它組出正確的檢索方式。負責為嵌入向量評分的模型固然重要,但知道何時不該使用它們同樣重要。為每個查詢挑選正確的工具,本身就是一種品質,而這正是我們寧可自己扛下、也不願丟給呼叫端處理的事。
// you bring the question. it brings the tools.
延伸閱讀
更多來自 Maguyva 開發日誌的內容
我們為何將程式碼搜尋升級至 voyage-4-large_
我們將程式碼嵌入模型換成了 voyage-4-large — 目前在公開的 RTEB 程式碼檢索排行榜上排名第一。這是誠實的版本:我們做了什麼取捨、我們實際索引的是什麼,以及我們為何願意為高階嵌入模型付費。
語言遞迴自我改進:在近 280 種語言中打磨程式碼智慧_
我們為近 280 種語言提供程式碼智慧支援,沒有人力能逐一手動稽核。因此我們打造了一套語言遞迴自我改進迴圈 — 抽查、LLM 擔任評審、修正單一問題、重新驗證 — 並以一支隔離代理人艦隊持續執行,直到擷取結果真正正確,而不只是綠燈通過。
代理人可觀測性:Hooks、Alloy 與 Grafana_
我們將 Claude Code 與 Codex 接入同一套 Grafana 技術堆疊,搭配 OpenTelemetry 與 Alloy,再運用追蹤與日誌,從源頭找出並修正代理人的行為問題。