第3章 アプリケーション開発 / 想定学習時間:30〜40分 / 最終確認:2026年8月

3-5. リランキング(re-ranking)の仕組みと実装

🎯 この節の学習目標

1. なぜリランキングが必要か:ベクトル検索の限界

3-4 の最後で見たとおり、RAG には「候補は広く取りたい(recall)が、プロンプトに入れるのは本当に関連する少数だけにしたい(precision)」というジレンマがあります。ベクトル検索の上位 k 件には、意味的には近いが質問への回答には役立たないチャンクが混ざることがあり、それがノイズとして回答品質を下げます。

この背景には、通常のベクトル検索が採用する bi-encoder(バイエンコーダ)方式の構造的な限界があります。bi-encoder はクエリと文書を別々にベクトル化してから距離を測るため、高速で大規模検索に向く一方、「クエリと文書を突き合わせた細かい関連性」までは捉えきれません。

そこで、速い検索で候補を絞り、遅いが精密なモデルで候補だけを精査するという役割分担、すなわち2段階検索が生まれます。

2段階検索の流れ:

  1. 第1段(retrieval):ベクトル検索で広めに候補を取得する(例:top-20)
  2. 第2段(re-ranking):リランカーが各候補とクエリの関連度を精密にスコアリングし、並べ替えて上位数件(例:top-3)だけを LLM のプロンプトへ渡す

2. リランカーの種類

方式仕組み特徴
クロスエンコーダ(cross-encoder)クエリと文書のペアを1つの入力としてモデルに与え、関連度スコアを直接出力する両者の相互作用を見られるため bi-encoder より高精度。ただしペアごとに推論が必要で高コスト・低速。事前計算(インデックス化)ができない
LLM によるリランキングLLM に「この質問に対して各文書がどれだけ関連するか」を判定・順位付けさせる柔軟な判定基準を指示できる(例:「回答に直接使える情報を含むか」)。LLM 呼び出し分のコストとレイテンシが追加される

bi-encoder と cross-encoder の違いは試験でも問われます。整理すると:

📝 試験のポイント

「クロスエンコーダはなぜ全文書への適用に向かないのか」→ クエリと文書のペアごとに推論が必要で、事前計算・インデックス化ができないため。「まずベクトル検索で候補を絞り、その候補にだけリランカーを適用する」という2段構えの順序が正解の型です。逆順(リランカーで全件→ベクトル検索)の選択肢は誤りです。

3. 利点と欠点:トレードオフを言葉にする

利点説明
検索精度(特に precision)の向上上位に残るチャンクの関連性が高まり、回答品質とグラウンディングが改善する
プロンプトのスリム化によるコスト減LLM へ渡すチャンクを厳選できるため、生成側の入力トークンが減る(3-4 の top-k ジレンマの解消)
欠点説明
追加レイテンシ第2段の推論時間がユーザーの体感応答時間に上乗せされる
追加の計算コストリランカーモデル(またはLLM呼び出し)の推論コストがかかる
全件には適用できない候補を第1段で絞ってあることが前提。候補数(例:20〜100件程度)を超えて適用するとレイテンシが実用範囲を超える

4. 導入判断:どんなときにリランキングを入れるか

リランキングは「入れれば必ず良くなる」ものではなく、症状に対する処方として判断します。

💼 ケース:導入すべき状況・不要な状況

📝 試験のポイント

リランキングが救えるのは「候補の中には正解があるのに、並び順が悪い」ケースです。「候補の中に正解がそもそも無い」ケース(recall 不足)には効きません。シナリオ問題では、症状が precision の問題か recall の問題かを見極めてから選択肢を選びましょう。

5. 実装イメージ

2段階検索の実装は、概念としては次の擬似コードに集約されます。

💡 具体例:2段階検索の擬似コード

# 第1段: ベクトル検索で広めに候補を取る(3-3)
candidates = index.similarity_search(
    query_text=question,
    columns=["chunk_text", "source"],
    num_results=20,          # 広めに取得
)

# 第2段: リランカーで (質問, チャンク) ペアをスコアリング
scored = [
    (reranker.score(question, c["chunk_text"]), c)
    for c in candidates
]
scored.sort(key=lambda pair: pair[0], reverse=True)

# 上位3件だけを LLM のコンテキストへ(3-4)
top_chunks = [c for score, c in scored[:3]]
context = format_docs(top_chunks)

reranker.score() の実体は、クロスエンコーダモデルの推論でも、「次の文書は質問への回答にどの程度関連しますか」と LLM に判定させる呼び出しでも構いません。重要なのは「広く取る → 精密に並べ替える → 少数だけ渡す」という構造です。この構造は 3-4 の RAG チェーンの retriever と format_docs の間に一段挟む形でそのまま組み込めます。

✅ この節のまとめ

練習問題

問1. RAG パイプラインにリランカーを組み込む位置として最も適切なのはどれか。

  1. ベクトル検索の前に、全文書コーパスへ適用して候補を作る
  2. ベクトル検索で広めに取得した候補集合に適用し、上位数件だけを LLM のプロンプトに渡す
  3. LLM の回答生成後に、回答文を並べ替える
  4. インデックス作成時に、チャンクを事前にスコア順で格納しておく
解答と解説を見る

正解:B

リランキングは「検索(第1段)と生成の間」に置き、絞り込まれた候補だけを精査します。Aはクロスエンコーダの推論を全文書に対して行うことになり、計算量的に非現実的です。Cは検索精度の問題を解決しません。Dは不可能です。リランカーのスコアはクエリに依存するため、クエリが来る前に順序を決めておくことはできません。

問2. クロスエンコーダ(cross-encoder)が bi-encoder より高精度な関連度判定ができる理由として正しいものはどれか。

  1. クエリと文書を1つの入力として同時にモデルに与え、両者の相互作用を直接評価できるため
  2. より次元数の大きいベクトルを生成するため
  3. 文書のベクトルを事前計算しておけるため
  4. キーワードの完全一致のみを評価するため
解答と解説を見る

正解:A

クロスエンコーダは(クエリ, 文書)ペアを結合してモデルに入力し、トークンレベルの相互作用まで見た上で関連度スコアを出します。これが独立にベクトル化してから距離を測る bi-encoder との本質的な違いです。Bのような次元数の話ではなく、Cの事前計算はむしろ bi-encoder の特徴です(クロスエンコーダはできない)。Dはキーワード検索の説明で誤りです。

問3. リランキング導入のトレードオフの説明として正しいものはどれか。

  1. 精度が向上し、レイテンシも常に短縮される
  2. 検索の precision が向上し、LLM へ渡すチャンクを減らせる一方、リランカーの推論による追加レイテンシと計算コストが発生する
  3. コストは増えるが、recall の不足(正解チャンクが候補に入らない問題)も解決できる
  4. インデックスの再構築が必要になる
解答と解説を見る

正解:B

リランキングの利点は precision 向上とプロンプトのスリム化(生成側の入力トークン削減)、欠点は追加のレイテンシと計算コストです。Aの「レイテンシ短縮」は誤りで、推論が一段増える分レイテンシは伸びます。Cは誤りで、リランカーは渡された候補を並べ替えるだけなので、候補に正解が無い recall 不足は救えません。Dのインデックス再構築は不要です(リランキングは検索後の後処理であり、インデックスには手を加えません)。

問4. RAG アプリの検索結果を評価したところ、次の2つの問題が見つかった。リランキングの導入が有効なのはどちらのケースか。
ケースX:正解チャンクは top-20 に含まれているが、top-3 にはノイズが混ざり回答品質が低い。
ケースY:正解チャンクが top-20 にもまったく含まれていない。

  1. ケースXのみ。候補内の並べ替えで正解チャンクを上位に押し上げられる
  2. ケースYのみ。リランカーが正解チャンクを新たに発見できる
  3. 両方とも有効
  4. どちらにも効果がない
解答と解説を見る

正解:A

リランカーができるのは「渡された候補の並べ替え」だけです。ケースXは正解が候補内にあるため、精密なスコアリングで上位に押し上げられます(precision の改善)。ケースYは候補自体に正解が無いため、いくら並べ替えても解決しません(recall の問題)。ケースYにはチャンキング戦略・埋め込みモデル・ハイブリッド検索の見直しなど、第1段の検索そのものの改善が必要です。