マルチモーダル・フュージョン検索:あらゆるクエリに最適な検索エンジンを選ぶ
> 「parseConfigはどこで定義されているか」のようなクエリと「認証はどう動いているか」のようなクエリでは、求められる検索の種類が異なります。Maguyvaは意図を分類し、それに応じて4つの検索モダリティに重みを付け、重み付き相互順位融合で結果を統合します。
検索クエリは、一枚岩ではありません。
「parseConfigはどこで定義されているか」は、正確なシンボル ―― 素早く得られる1つの正確な場所 ―― を求めています。「認証はどう動いているか」は意味を求めています ―― ある概念を説明する、関連コードの広がりです。「この関数を変更したら何が壊れるか」は依存関係グラフを求めています。「文字列ECONNREFUSEDを見つけて」はリテラルな一致を求めているのであって、気の利いたことは何もいりません。
Grepはリテラルな一致には優れており、一部の参照探しにも有用ですが、依存関係グラフではありませんし、意味を理解することもありません。埋め込み(embedding)は意味的な側面をカバーしますが、正確な文字列や影響分析には向いていないツールです。ほとんどのコード検索ツールは1つのエンジンを選び、あらゆるクエリにその選択と付き合わせます。Maguyvaは選びません。あなたがどんな種類の問いを立てたのかを見極め、その問いにふさわしい比率で4つの検索エンジンをブレンドします。
4つのモダリティ
内部には、コードを見つけるための4つの独立した方法があります:
- semantic(意味検索) ―― Voyageのバイナリ埋め込みによるベクトル検索。意味でコードを見つける。
- text(テキスト検索) ―― トライグラムマッチング。リテラル、エラー文字列、正確な識別子を見つける。
- structural(構造検索) ―― ASTクエリ。定義、シグネチャ、言語構文を見つける。
- graph(グラフ検索) ―― 依存関係グラフ。呼び出し元、呼び出し先、影響範囲を見つける。
それぞれが異なる種類の問いに強みを持っています。肝心なのは、目の前のクエリに対してそれぞれをどれだけ信頼するかを見極めることです。
意図分類
検索を実行する前に、軽量な分類器がクエリを6つの意図のいずれかに振り分け、確信度スコアを付けます。これはホットパス上で動作し、加わる遅延はわずか1〜2ミリ秒程度であるべきなので、意図的に軽量に作られています ―― 順序付きのヒューリスティクスで、最初に一致したものが採用されます:
def、class、func、import…で始まる → find_definition(確信度0.95)- 「who calls」「usages of」「references to」→ find_references(0.90)
- 「影響」「影響範囲」「何が依存しているか」→ 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)
それぞれの意図は、4つのモダリティにまたがる重みプロファイルを持っています。これが実際の数値です:
| 意図 | semantic | text | structural(AST) | graph |
|---|---|---|---|---|
| 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)。「認証はどう動いているか」は意味ベクトルに傾きます(0.5)。「これに依存しているのは何か」はほぼグラフだけです(0.7)。「ECONNREFUSEDを見つけて」はほぼトライグラムだけで(0.9)、埋め込みモデルは完全にオフになります ―― 正確な文字列に対して、意味的類似度はまさに誤ったツールだからです。
高速経路と、統合された経路
分類器の確信度が高く(スコア0.85以上)、クエリが通常のものであれば、Maguyvaは統合(フュージョン)を完全にスキップし、単一の支配的なモダリティへ直接ルーティングします。「Xはどこで定義されているか」には4つの検索エンジンは不要で、今すぐASTインデックスが必要なだけです。この直接経路はfusion_strategy: "direct"として返されます。
あいまいなクエリはすべて統合を経由します。4つ(デフォルトのプリセットでは3つ)のモダリティが並列に実行され、それぞれが独自のランク付けされたリストを返し、私たちはそれらを結合します。
重み付き相互順位融合
性質の異なる検索エンジンを統合するのは、見た目以上に難しい作業です。コサイン類似度0.82と、トライグラムスコア137と、グラフ中心性0.004は同じスケール上にないため、単純に足し合わせることはできません。相互順位融合は、生のスコアを捨て、各エンジンが割り当てた順位だけを残すことで、この問題を回避します。あるモダリティからの結果の寄与度は次の通りです:
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、セマンティック検索、依存関係分析を1つのクエリ入口の裏側に置くことを可能にしています。システムが問いの形を読み取り、それにふさわしい検索エンジンを静かに組み立てます。埋め込みをスコアリングするモデルも重要ですが、それを使わないべきタイミングを知っていることも同じくらい重要です。クエリごとに正しいツールを選ぶこと自体が1つの品質であり、私たちはそれを呼び出し側に押し付けるのではなく、自分たちで引き受けたいと考えています。
// you bring the question. it brings the tools.
関連記事
Maguyva開発ログのその他の記事
コード検索をvoyage-4-largeへアップグレードした理由_
私たちはコード埋め込みをvoyage-4-largeへ移行しました ―― 現在、公開RTEBコード検索リーダーボードのトップに立つモデルです。正直に言うと:私たちが受け入れているトレードオフ、実際に何をインデックス化しているか、そしてなぜプレミアムな埋め込みにお金を払うのか。
言語の再帰的自己改善:約280言語にわたるコードインテリジェンスの磨き上げ_
私たちは約280言語のコードインテリジェンスに対応しています。人間の手でそのすべてを監査することはできません。そこで私たちは、言語の再帰的自己改善ループ ―― 抜き取り検査、LLM-as-judge、1点修正、再検証 ―― を構築し、抽出が単に「グリーン」であるだけでなく実際に正しくなるまで、隔離されたエージェント群でこれを回し続けています。
エージェントオブザーバビリティ:フック、Alloy、Grafana_
Claude CodeとCodexを1つのGrafanaスタックにOpenTelemetryとAlloyで配線し、トレースとログを使ってエージェントの挙動の問題を根源から見つけて直す。