RAG

ハイブリッド検索の実装|BM25とベクトルをRRFで統合

ハイブリッド検索は、全文検索とベクトル検索を両方実行しただけでは完成しません。別々の尺度で付いた順位を一つへ統合し、どの質問群が改善したか、権限条件をどこで適用したか、候補数を増やした分だけ遅延が許容範囲かを確認して初めて採用できます。本稿の読後成果物は、220問を質問型別に比較した検索評価表と、RRFを使う二段階パイプラインの受入記録です。

ハイブリッド検索は単独検索の弱点を確認してから採用する

BM25などの語彙検索は、製品コード、エラー番号、法令名、引用符で囲んだ表現、社内固有の略称に強みがあります。ベクトル検索は、質問と文書で使う言葉が異なる場合や、自然文の意図が近い場合に候補を広げます。たとえば「MX-410 E27」は語句一致が有利で、「装置の温度が上がり続けるときの安全な止め方」はマニュアルの「過熱時緊急停止」へ意味で近づく検索が役立ちます。一方だけへ統一すると、もう一方の質問型で取りこぼします。

Elasticの現行公式ドキュメントは、ハイブリッド検索を全文検索とベクトル検索を一つの要求で実行して一つの順位表へまとめる方式と説明し、RRFを推奨しています[3]。これは同製品の推奨であり、あらゆるコーパスでRRFが最善という保証ではありません。自社の単独基準を超えないなら、追加された索引、埋め込み、監視、遅延に見合いません。

観測した症状追加する検索確認指標見送り判断
型番は出るが言い換えに弱いベクトル候補意味型のRecall@20改善が2ポイント未満
自然文は出るがコードが落ちるBM25候補完全一致型のMRR@10アナライザー調整だけで合格
双方で別の正解が出る順位統合混合型のnDCG@10片方が全区分で優越
正解は候補内だが順番が悪い再ランキング候補Recallと上位品質融合ではなく順位だけが原因

採用理由は「二つ使えば安心」ではなく、単独検索の異なる失敗を一つの評価集合で補完できたことです。

220問を語句型・意味型・混合型へ分ける

検索ログから220問を選び、質問の表現と正解チャンクを確定します。内訳例は、型番・規格・エラーコードなどの完全一致50問、社内略称や複合語30問、言い換えや説明文70問、語句と意味の両方を要する40問、版・部署・日付などのフィルター付き20問、原本に答えがない10問です。各問に正解チャンクを一つだけ置く必要はなく、関連度3(直接回答)、2(重要な補足)、1(周辺)、0(無関係)の段階ラベルを付けます。

評価集合には「似ているが違う」負例を意図的に入れます。「ISO 27001:2022の附属書A」を尋ねる質問へ、2013版や社内のISMS運用規程が上位に来ないかを見ます。「E27」を含む別製品、「返品」と「返金」、「保守終了」と「販売終了」のような近接語も必要です。意味検索だけが過度に広げる失敗と、語彙検索が同じ単語へ引かれる失敗を区別できます。

区分件数例正解の特徴主に見る指標
完全一致50固有文字列を含むMRR@10、Exact match recall
社内語彙30略称と正式名が異なるRecall@20、同義語の寄与
意味一致70質問と根拠の表現差が大きいRecall@20、nDCG@10
混合40型番と状況説明を含む融合後nDCG@10
条件付き20版・権限・期間で絞る条件違反0、Hit@5
回答不能10正解チャンクなし誤受理率、最大スコア分布

220はこの記事の提案母数で、特定企業の実績ではありません。検索設定を調整する44問と、最終判定に使う176問へ分けます。調整側だけでBM25のフィールド重み、ベクトル候補数、RRF定数を決め、判定側は最後まで伏せます。月次で本番の失敗質問を追加するときも、既存ラベルを変更せず、データ版を`search-eval-2026-07`のように固定します。

BM25とベクトルの単独基準を先に測る

BM25基準では、タイトル、見出し、本文、型番フィールドを分け、使用アナライザー、ストップワード、同義語、各フィールド重みを保存します。ベクトル基準では、埋め込みモデル、次元、正規化、距離関数、チャンク版、近似検索パラメーターを残します。両経路へ共通のACL・有効版フィルターを適用し、固定した176問を等しい`top_k`で出力します。文書ID、チャンクID、順位、元スコア、検索時間も記録します。

二つのスコアを直接足す前に分布を見ます。BM25は上限が固定されず、クエリや語頻度で値域が変わります。コサイン類似度は実装の変換方法によって範囲が異なります。`0.7 × BM25 + 0.3 × cosine`のような式は、正規化なしでは0.7と0.3の意味を持ちません。min-max正規化も候補集合の外れ値に左右されるため、まず順位だけを使うRRFを基準にすると、異種スコアの尺度合わせを避けられます。

baseline_run:
corpus_revision: kb-2026-07-18
query_set: search-eval-2026-07-blind
lexical:
analyzer: ja-kuromoji-v4
fields: {title: 3.0, heading: 2.0, body: 1.0, product_code: 4.0}
vector:
embedding_model: embed-ja-2026-05
index: hnsw
candidate_k: 50
common_filter:
- acl contains request.principal
- valid_from <= request.as_of
- valid_to is null or valid_to > request.as_of

単独基準の合格像を先に決めます。完全一致50問でBM25のMRR@10が0.85、意味一致70問でベクトルのRecall@20が0.92というように区分別に置きます。どちらか一方が全区分で同等以上、p95も低いなら、ハイブリッド化は見送ります。単独経路の不具合を残したまま融合すると、欠陥のある候補を増幅し、統合設定の問題に見せかけます。

RRFで異なる順位を統合する

Reciprocal Rank Fusionは、各検索結果における順位`r`へ`1/(k+r)`を割り当て、同じ文書またはチャンクについて合計します。Cormack、Clarke、BüttcherによるSIGIR 2009の原著論文は、複数の順位表を単純な逆順位の和で統合する方法を提示しました[1]。RRFはスコア尺度を合わせずに済み、学習用クリックデータがない初期実装の基準に向きます。ただし候補へ入らなかった正解を新しく発見する処理ではありません。

計算例

`k=60`とし、チャンクAがBM25で1位、ベクトルで3位なら、`1/(60+1)+1/(60+3)=0.03226`です。チャンクCがBM25で3位、ベクトルで1位でも同じ0.03226になります。片方だけ2位のBは`1/62=0.01613`です。両方で上位に出る候補を持ち上げながら、片側だけの強い結果も残します。

Azure AI Searchの「Relevance scoring in hybrid search using RRF」は2026年6月8日更新です。同資料は`1/(rank+k)`を合計する処理と、RRFの`k`がベクトル検索件数`k`とは別の値であることを説明しています。実験上の小さい定数例は60です[2]。同サービスでは全文とベクトルの複数クエリを並列実行してRRFへ渡します。自作実装で60を採る場合も、20・60・100を調整集合で比較し、判定集合の順位を見て固定します。

方式必要データ利点主な危険
RRF各経路の順位異なるスコア尺度を扱いやすい順位間の差の大きさを捨てる
正規化加重和安定したスコア分布語彙・意味の比率を細かく調整外れ値と分布変化に敏感
学習toRank十分な関連度・行動データ業務固有の特徴を学習ラベル偏り、監視、説明負荷

等重みのRRFを固定する前に、経路ごとの候補漏れと業務上の優先順位を確認します。重みを加える実装案は`score(d)=w_lex/(k+r_lex(d))+w_vec/(k+r_vec(d))`で、候補が一方にない項は0です。`k=60`、`w_lex=1.5`、`w_vec=1.0`の想定例では、BM25で1位、ベクトル側で圏外のAは`1.5/61=0.02459`です。BM25で10位、ベクトルで4位のBは`1.5/70+1/64=0.03705`となり、Aより上へ来ます。

したがって語彙側を1.5倍にしても、型番の完全一致が必ず首位になるわけではありません。型番や条文番号を絶対優先する業務では、コード検出後にBM25へ分岐するか、完全一致候補を固定する規則を別に評価します。

RRFの限界は、元スコアの差を捨て、取得打ち切りより下の候補を見られないことです。両経路を50件で切れば、BM25の51位にだけ存在する正解は融合式を変えても戻りません。調整44問で各経路の`top_n`を20、50、100へ振り、結合集合のRecall@100を先に測ります。

50から100へ増やして正解流入が1ポイント未満、融合p95が150ミリ秒以上増えるなら50を維持する、という停止線を置けます。逆に完全一致区分の候補Recallが目標未達なら、RRF定数ではなく語彙索引と候補数を直します。

権限と業務条件を候補生成前に適用する

利用者が読めないチャンクを検索した後で削る設計は避けます。語彙経路とベクトル経路の双方へ、テナント、利用者・グループ、機密区分、有効期間、言語、製品などの必須条件を同じ意味で適用します。片方だけが旧版を含むと、RRFが旧版を二経路の合意として上位へ持ち上げる場合があります。フィルター条件、ポリシー版、通過前後の候補数を各経路のログへ残します。

OpenSearchの現行`hybrid`クエリ資料は、複数サブクエリへ共通の`filter`を適用できること、ハイブリッドクエリの再スコアがシャード側で各サブクエリに行われた後、調整ノードで正規化・統合される処理順を説明しています[4]。これは同製品における実装順です。別製品ではフィルターや再スコアの位置が違うため、API名だけでなく、権限外チャンクがどの段階にも現れないことを負例で確かめます。

試験投入条件期待結果違反時の扱い
別部署の同名文書意味も型番も完全一致両経路で取得0件公開停止
期限切れ手順旧版の語句一致が強い現行版のみ融合対象有効期間フィルター修正
ACL欠損チャンク属性がnull拒否・隔離取り込みを停止
大量グループ所属主体が200 groupを持つ正答とp95の双方が合格主体解決方式を再設計

フィルター後に片方の候補が10件未満になる場合、空き枠を権限外候補で埋めません。許可済み集合の中で探索幅を広げるか、結果不足として返します。RRFへ渡す候補数が経路ごとに大きく違うと、片方の裾野だけが増えるため、`top_n_lexical=50`、`top_n_vector=50`のように対称な基準から始め、質問群別に変える場合は理由を設定へ残します。

取得・統合・重複排除を一つの処理へ実装する

処理順は、要求検証、主体解決、共通フィルター作成、二経路検索、ID正規化、RRF、重複範囲の統合、必要なら再ランキング、生成用コンテキスト作成です。語彙検索が文書単位、ベクトル検索がチャンク単位なら、同じIDで融合できません。両方をチャンク単位に揃えるか、文書順位を子へどう配るかを定義します。おすすめは検索と引用の最小単位を同じ`chunk_id`へ合わせる方法です。

async function hybridRetrieve(request) {
const principals = await resolvePrincipals(request.userId);
const filter = buildMandatoryFilter(principals, request.asOf);
const [lexical, vector] = await Promise.all([
bm25Search(request.query, { filter, limit: 50 }),
vectorSearch(request.queryEmbedding, { filter, limit: 50 })
]);
assertNoForbiddenHit(lexical, principals);
assertNoForbiddenHit(vector, principals);
const fused = reciprocalRankFusion(
[dedupeByChunkId(lexical), dedupeByChunkId(vector)],
{ rankConstant: 60 }
);
return collapseOverlappingSpans(fused).slice(0, 30);
}

重複排除では、同一チャンクIDだけでなく、同じ原本の重複範囲を見ます。固定幅の重なりによって、ほぼ同文の二チャンクがBM25とベクトルの両方に現れると、上位枠を消費します。`document_id + revision + source span`の重なりが80%以上なら高い方を残す、といった規則を評価データで検証します。隣接チャンクを結合する場合は、結合後にトークン上限と引用位置が崩れないようにします。

タイムアウトも経路別に扱います。ベクトル検索が落ちた場合にBM25だけで続行するなら、応答へ`retrieval_mode=lexical_fallback`を記録し、高リスク業務では生成を止めます。サイレントに片経路だけへ縮退すると、品質低下を観測できません。検索失敗率が5分窓で2%を超えたらアラート、10%を超えたらハイブリッド回答を停止するなど、業務影響に合う閾値を置きます。

並列実行は待ち時間を短くしても、計算資源を一経路分にはしません。容量の概算は`必要CPUコア=(ピークQPS×(BM25のCPUミリ秒+ベクトルのCPUミリ秒))÷1,000`です。想定例で300 QPS、各経路が4ミリ秒と12ミリ秒なら`300×(4+12)÷1,000=4.8コア`、障害吸収係数1.5を掛けると7.2コアです。

検索用に6コアしか確保できなければ、増強、対象質問の限定、単独経路への縮退のいずれかが必要です。この式はI/O待ち、キャッシュミス、シャード偏りを含まないため、採否は同じ負荷生成器でのCPU、p95、エラー率を合わせて決めます。

質問群別の指標と遅延で採否を決める

候補生成ではRecall@20またはRecall@50を、利用者へ見せる上位ではnDCG@10とMRR@10を測ります。BEIR原著は多様な検索課題を横断してゼロショット検索を評価し、主指標としてnDCG@10を用いています[5]。公開ベンチマークの点数を自社KPIにするのではなく、関連度を段階評価し、上位ほど価値が高い業務にnDCGを用いる根拠として参照します。

次の表は説明用の架空データです。176問の判定集合を三方式へ流し、同一インフラで3回測定した中央値という想定にしています。数値は導入効果ではありません。

方式完全一致MRR@10意味型Recall@20混合型nDCG@10p95権限違反
BM25のみ0.910.760.68180ms0
ベクトルのみ0.700.930.74310ms0
RRF0.890.920.82420ms0

取得処理の遅延予算は、並列実行を前提に`T_retrieval≈T_auth+max(T_BM25,T_vector)+T_fusion+T_dedupe`と分解します。想定例で主体解決40ミリ秒、BM25 180ミリ秒、ベクトル310ミリ秒、融合25ミリ秒、重複排除35ミリ秒なら`40+max(180,310)+25+35=410ミリ秒`です。取得SLAが600ミリ秒なら、ネットワーク揺らぎと再試行へ残る予算は190ミリ秒です。

ただし各工程のp95を足しても、一つの要求で同時に尾部遅延が起きる確率は表せません。この計算は設計時の配分に使い、最終値は176問を混ぜたエンドツーエンドのp95・p99で確定します。

採用線を「全区分で最良単独方式との差が2ポイント以内、混合型nDCG@10が5ポイント以上改善、p95 600ms以内、権限違反0」とするなら、この例のRRFは候補になります。完全一致で0.02落ちているため、型番を検出した質問だけBM25を強める案を調整44問で試せます。ただしルーティングを追加すると保守対象が増えるので、改善が1ポイント未満なら一律RRFを選ぶ判断もあります。

平均値に加え、質問ごとの勝敗を保存します。RRFが改善した質問数、悪化した質問数、順位不変、正解消失を数えます。混合型40問で改善18、悪化2なら原因を読めますが、改善10、悪化9では平均点が上がっても不安定です。重要質問に重みを付ける場合は、障害停止や契約条件などの対象を事前指定し、結果を見てから重みを変えません。

失敗を語彙・ベクトル・統合へ切り分ける

誤回答を見たら、生成文ではなく二つの順位表から確認します。BM25に正解がなくベクトルにはあるなら、語彙辞書やアナライザーの問題か、想定どおり意味経路が補完した結果です。両方のtop50に正解がなければ、RRF定数を触っても回復しません。どちらかに1位で存在するのに融合後20位以下へ落ちたとき、候補数、重複、ID対応、RRF実装を調べます。

観測原因候補確認方法変更対象
BM25だけ正解なし分かち書き、同義語、フィールド解析tokenと説明スコアアナライザー・辞書
ベクトルだけ正解なしチャンク、埋め込み、ANNExact検索と近似検索を比較分割・モデル・索引
両方にあるが融合で低下ID不一致、重複、定数RRF項を候補ごとに表示統合ロジック
正解が候補内で回答誤り再ランキング・生成最終コンテキストと引用後段処理
特定部署だけ空結果権限属性、同期遅延共通filterとACL版認可・取り込み

語彙とベクトルを同時に再設定しないことが重要です。一つの失敗質問へ複数変更を入れると、別の区分の回帰を追えません。修正ごとに変更ID、対象区分、予想効果、調整44問の差、判定176問の差を残します。固定集合で全体が上がっても、権限付き20問や回答不能10問が悪化したら公開しません。

辞書更新と再索引の条件を運用へ組み込む

BM25側の同義語辞書、分かち書き辞書、フィールド構成を変えると、語彙順位が変わります。辞書を検索時だけ展開できる構成なら全量再索引を避けられる場合がありますが、索引時解析へ組み込んだtokenが変わるなら再作成が必要です。ベクトル側は、埋め込みモデル、次元、正規化、チャンク規則、検索対象本文を変えた場合に再埋め込みと別索引構築を行います。RRF定数や候補数だけの変更は通常、文書ベクトルの再生成を必要としません。

変更再索引必須の回帰テスト監視する本番指標
検索時同義語構成による社内語彙30問、完全一致50問ゼロ件率、辞書ヒット率
索引アナライザー語彙索引を再作成語句型と負例索引時間、語彙順位
embedding modelベクトル全量を別構築全176問と権限試験Recall、p95、容量
RRF rank constant不要混合40問、勝敗内訳経路別寄与、順位変動
ACL項目全チャンクへ属性更新拒否負例、条件付き20問属性欠損、拒否件数

運用ログには、クエリID、匿名化した質問区分、コーパス版、語彙索引版、ベクトル索引版、埋め込み版、フィルター版、各経路の候補IDと順位、融合スコア、最終取得ID、段階別時間を含めます。質問本文や機密チャンクをそのまま長期保存できない場合は、ハッシュ、カテゴリ、ID、スコアへ限定し、監査に必要な期間を情報管理部門と決めます。

再評価の開始条件は、主要辞書の追加が月20語を超える、質問区分の構成比が10ポイント変わる、単独経路のRecallが合格値を二週続けて割る、embedding提供元がモデルを更新する、検索基盤の主要版を上げる、のいずれかです。毎日の小変更で全量評価を回すのではなく、変更をまとめて版管理し、ステージングで176問を通してから切り替えます。

単独検索へ戻す条件を先に定める

ハイブリッド検索が向かないのは、質問のほぼ全てが型番検索で、適切なアナライザーを設定したBM25が品質目標を満たす場合です。文書が数千件しかなく、Exact語彙検索で十分な業務、埋め込みの外部処理が禁止される業務、更新から検索まで数秒を要求してベクトル生成が間に合わない業務にも、複雑化の負担が上回る可能性があります。

即時停止と縮退の境界

権限外チャンクがいずれかの経路へ一件でも入り、ログや再ランキングへ渡った場合は回答提供を止めます。ACL欠損率が0を超える索引も公開しません。品質面では、融合後nDCG@10が最良単独方式より3ポイント以上低い状態が二回続く、p95がSLAの1.5倍を超える時間帯が週3回ある、片経路のタイムアウトが5%を超える場合に、改善期限を決めて単独経路へ縮退します。

停止中は「検索できない」と明示し、別の経路へ無断で切り替えて同品質を装いません。低リスクFAQではBM25へ縮退し、契約・安全手順では回答を停止するなど、業務別に挙動を分けます。検索統合を外しても、原本閲覧への導線を残せる設計にします。運用担当が融合スコアの説明や経路別ログを確認できない場合は、本番展開を限定し、検索専門担当へ引き継ぎます。

予備比較を始める

最初は検索ログから、型番15問、社内略称10問、言い換え20問、混合10問、条件付き5問の計60問を作ります。同じチャンク集合とACLでBM25、ベクトル、`k=60`のRRFを実行し、各問の順位表とp95を並べてください。混合10問で改善がなく、単独方式の弱点も特定できないなら拡張せず終了します。補完関係が見えたときだけ220問へ増やし、判定集合を固定します。

参考文献・出典

  1. Gordon V. Cormack, Charles L. A. Clarke, Stefan Büttcher「Reciprocal Rank Fusion Outperforms Condorcet and Individual Rank Learning Methods」[原著論文・Google Research掲載] SIGIR 2009、2009年7月(参照日: 2026年7月30日)
  2. Microsoft「Relevance scoring in hybrid search using Reciprocal Rank Fusion (RRF)」[公式ドキュメント] 2026年6月8日更新(参照日: 2026年7月30日)
  3. Elastic「Hybrid search」[公式ドキュメント] 版表記なし(参照日: 2026年7月30日)
  4. OpenSearch Project「Hybrid query」[公式ドキュメント] latest、版表記なし(参照日: 2026年7月30日)
  5. Nandan Thakur et al.「BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models」[原著論文] arXiv:2104.08663、初稿2021年4月17日、NeurIPS Datasets and Benchmarks 2021(参照日: 2026年7月30日)

関連記事

新着記事
  1. 企業のAI活用成熟度を5段階で診断|証拠・評価式・改善順序

  2. AI開発の内製・外注を決める方法|3年要員費と責任分担で判断

  3. ノーコードAIと独自開発の比較|試作・統制・保守負担で決める

TOP

EmMatch AIPをもっと見る

今すぐ購読し、続きを読んで、すべてのアーカイブにアクセスしましょう。

続きを読む