RAGのリランキングは、一次検索が集めた候補だけを、質問との関連度で並べ直す二段目の処理です。まず広めに候補を回収し、Cross-Encoderでクエリと各チャンクを同時に採点して、生成モデルへ渡す上位だけを選びます。
この記事は一次検索後の候補再順位付けに限定し、Cross-Encoder、候補数上位k件、遅延を測る実装を扱います。ベクトル検索とキーワード検索の統合やRRFの実装は、役割が重ならないようRAGのハイブリッド検索を実装する方法へ分けています。RAG全体の評価データセット設計や生成回答の品質評価も扱いません。
情報確認日:2026年8月17日(日本時間)
結論:回収と精査を二段に分ける
標準的な流れ
- 一次検索:ベクトルまたはキーワード検索で候補を広めに取得する
- 候補準備:一次検索の候補ID・順位・テキストを固定する
- 再採点:Cross-Encoderへ質問と各候補のペアを渡す
- 絞り込み:再採点後の上位だけを生成モデルへ渡す
- 比較:同じ質問セットで、正解チャンクの順位とp95遅延を導入前後で測る
| 項目 | 最初の基準 | 完成条件 |
|---|---|---|
| 一次候補 | 上位 20〜50から検証 | 正解チャンクが候補内へ入る |
| 再順位付け後 | 上位3〜8件を生成へ渡す | 正解チャンクの順位が上がる |
| 品質 | 固定した実質問セット | 導入前より悪化していない |
| 性能 | p50 / p95 遅延 | サービスの応答時間の上限内 |
上の候補数は開始点であり、推奨値の保証ではありません。コーパス、文書長、言語、リランカー、CPU/GPU、同時リクエスト数によって最適値は変わります。
RAGのリランキングは何を改善する?
一次検索は大量のチャンクから候補を素早く回収する役目です。ベクトル検索は言い換えに強い一方で、意味が近いだけの一般説明を上位へ出すことがあります。キーワード検索は製品名や番号に強い一方で、語が一致しない質問を落とすことがあります。
リランカーは候補を増やせません。候補内に正解チャンクがあるとき、その順位を上げ、生成モデルへ渡すコンテキストを選びやすくします。正解が一次候補に入っていない場合は、リランカーではなく一次検索、クエリ、フィルター、チャンク設計へ戻ります。
全corpus
│ fast retrieval: 広く回収
▼
候補 30件
│ optional RRF: 複数順位を統合
│ Cross-Encoder: queryとの一致を再採点
▼
上位 5件
│
└─ generation modelへ渡す
Cross-Encoderとバイエンコーダーの違いは?
バイエンコーダーは質問と文書を別々にベクトル化します。文書ベクトルを事前計算できるため、大きなコーパスから近い候補を高速に探せます。Cross-Encoderは質問と文書を一つのペアとして入力し、トークン同士の関係を見てスコアを返します。
| 比較 | バイエンコーダー / ベクトル検索 | Cross-Encoder |
|---|---|---|
| 入力 | クエリと文書を別々にエンコード | 質問と文書の組を一緒にエンコード |
| 事前計算 | 文書ベクトルを保存できる | クエリごとに各ペアを計算する |
| 役割 | 大規模コーパスから候補回収 | 少数候補の精査・並べ替え |
| コスト | 候補取得が速い | 候補数にほぼ比例して増える |
| 使いどころ | 一次検索 | 二次検索 |
Sentence Transformersの公式ガイドも、バイエンコーダーで候補を取得し、Cross-Encoderで再採点するRetrieve & Re-Rank構成を示しています。Cross-Encoderをコーパス全件へ実行するとペア数が膨らむため、一次候補へ限定します。
RRFはCross-Encoderの前段に置く
RRFはベクトル検索とBM25など、複数の順位表を一つへ統合する処理です。本文を読んでクエリとの一致を再採点するCross-Encoderとは役割が違います。複数検索を使う場合だけ、次のようにRRFを前段へ置けます。
vector top 30 ─┐
├─ RRFで重複排除・順位統合 ─ Cross-Encoder ─ final top 5
BM25 top 30 ───┘
検索方式が一つだけならRRFは不要です。順位統合の式、Python実装、単独検索との比較手順は、ハイブリッド検索の記事で確認してください。ここからは、統合済みの候補をCross-Encoderで精査する工程だけを扱います。
手順1:一次検索の候補を固定する
リランカー導入前に、質問、期待する正解チャンク、一次候補のID・順位・スコアを保存します。質問を変えながら比較すると、改善がリランカーによるものか判断できません。
{
"query_id": "q-014",
"query": "返品の受付期限は?",
"expected_chunk_ids": ["policy:returns:deadline"],
"retriever_version": "embed-v3+index-20260817",
"candidate_limit": 30,
"candidates": [
{"id": "faq:shipping:delay", "rank": 1, "score": 0.83},
{"id": "policy:returns:deadline", "rank": 6, "score": 0.78}
]
}
ここで正解チャンクが上位 30に入らない質問が多ければ、候補数を増やすだけでなく、クエリ表現、メタデータ フィルター、ハイブリッド検索、チャンクの境界を確認します。
手順2:Cross-Encoderで候補を再採点する
PythonのSentence Transformersでは、CrossEncoder.rank()へクエリと文書の一覧を渡し、スコア順の結果を取得できます。次は環境差を減らすためにバージョンを固定した再現用の例です。
python -m venv .venv
source .venv/bin/activate
python -m pip install "sentence-transformers==5.6.1"
python -c "import sentence_transformers; print(sentence_transformers.__version__)"
# 5.6.1
この記事のコードは、再現時の環境差を減らすため5.6.1へ固定しています。2026年8月17日にPyPIで確認した最新版は5.7.0です。導入時はPyPIのリリース履歴と利用中のPython・PyTorchとの互換性を確認し、採用バージョンをロックファイルへ残してください。初回実行時はモデルファイルを取得します。公開環境ではモデルのリビジョンやアーティファクトのハッシュも固定し、取得元、ライセンス、保存先を記録してください。次はAPIと出力形式を確認する最小例です。
from sentence_transformers import CrossEncoder
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L6-v2")
query = "返品の受付期限は?"
candidates = [
{"id": "faq:shipping:delay", "text": "配送遅延が発生した場合は..."},
{"id": "policy:returns:deadline", "text": "返品は商品到着後14日以内に..."},
{"id": "policy:exchange", "text": "交換を希望する場合は..."},
]
ranked = reranker.rank(
query,
[item["text"] for item in candidates],
return_documents=False,
top_k=3,
)
results = [
{
**candidates[row["corpus_id"]],
"rerank_score": float(row["score"]),
}
for row in ranked
]
for rank, item in enumerate(results, start=1):
print(rank, item["id"], item["rerank_score"])
正常なら3行がスコア降順で表示され、各行は順位 chunk_id scoreの形になります。スコアはlibrary・モデル 仕様リビジョン・実行環境で変わるため固定値との一致ではなく、policy:returns:deadlineが1位になることと3件が返ることを確認します。この記事の環境にはSentence Transformersを導入していないため、数値を実測値としては掲載しません。
モデル名は英語検索向けの例です。日本語または多言語コーパスでは、対象言語とドメインに対応するリランカーを選び、自分の質問セットで確認してください。モデルカードのライセンス、学習ドメイン、最大入力長、コード実行要件も確認します。
長いチャンクをそのまま切り捨てない
Cross-Encoderには最大入力長があります。クエリとチャンクの合計が超えると末尾が切られ、結論や例外が消えることがあります。長い候補は見出し・段落単位で分け直すか、titleと該当セクションだけをリランカーへ渡します。
再順位付け用テキストと生成用テキストを分ける場合も、同じchunk_idで結びます。短い検索テキストのスコアを、別の無関係な長文へ付け替えないようにします。
手順3:再順位付け前後の上位k件を比較する
ここで比較するのは、同じ一次候補に対する導入前の順位とCross-Encoder導入後の順位です。複数検索の統合条件まで同時に変えると、改善要因を切り分けられません。ハイブリッド検索を使う場合も、RRFの設定を固定してからリランカーだけを有効・無効にします。
再順位付け前後の上位k件をどう比較する?
次の表は形式を示す架空例です。実測ベンチマークではありません。正解チャンクが候補内にあるか、再順位付け後に何位へ動いたか、誤った上位候補がどう下がったかを一問ずつ確認します。
| 候補 | 一次順位 | 再順位付け順位 | 判定理由 |
|---|---|---|---|
| 配送遅延FAQ | 1 | 4 | 「期限」はあるが返品の答えではない |
| 返品規約:受付期限 | 6 | 1 | 質問と条件が直接一致 |
| 交換規約 | 3 | 3 | 近い手続きだが返品期限ではない |
| 返品規約:送料 | 8 | 2 | 同じ文書だが答えは一部のみ |
順位が改善しても、同じ文書の似たチャンクが上位を占有すると情報量は増えません。生成へ渡す直前に、文書ごとの最大チャンク数や重複テキストの除去を検討します。ただし重要な連続セクションまで落とさないよう、評価例で確認します。
候補数上位k件はどう決める?
一次候補を増やすほど正解を回収できる可能性は上がりますが、Cross-Encoderのペア計算、メモリ、遅延も増えます。最終コンテキストを増やしすぎると、生成モデルへ不要情報が入ります。
| 調整値 | 小さすぎる場合 | 大きすぎる場合 |
|---|---|---|
| 一次候補数 | 正解がリランカーへ届かない | 再順位付けの遅延とコストが増える |
| 再順位付け後件数 | 複数根拠や例外が欠ける | ノイズとコンテキスト量が増える |
| チャンク長 | 答えが分断される | 入力切り捨て、主題の混在 |
- 再順位付けなしで一次上位 10、20、30、50の正解包含率を比べる
- 包含率の伸びが小さくなる範囲を候補にする
- その範囲で再順位付け後上位 3、5、8の正解順位と生成回答を比べる
- 同時リクエストを含むp50、p95 遅延とタイムアウト率を測る
- 品質と応答時間の上限を満たす最小の候補数を採用する
遅延が増えすぎたらどうする?
| 原因 | 確認 | 対策 |
|---|---|---|
| 候補が多い | 候補数別のペア数 | 一次検索を改善し、必要最小数へ減らす |
| バッチが効かない | バッチサイズとデバイス使用率 | メモリ内でバッチを調整する |
| モデルが大きい | モデル別の品質・p95 | 小型モデル、ONNX、OpenVINO等を比較する |
| 長文が多い | トークン長分布・切り詰め | チャンク境界と再順位付け用テキストを見直す |
| 毎回同じクエリ | クエリ正規化後のヒット率 | バージョン付きキャッシュを検討する |
速度のためにリランカーを外す前に、質問の種類で分岐する方法もあります。型番の完全一致はキーワード結果を優先し、曖昧な自然文だけ再順位付けする構成です。分岐を入れたら、どのルートが選ばれたかをログへ残します。
リランキングの失敗をどう切り分ける?
回答に必要なchunkが出ない
├─ 一次候補に正解がある?
│ ├─ No → retriever・query・filter・chunkへ戻る
│ └─ Yes
├─ rerank後に順位が下がった?
│ ├─ Yes → 対象言語・domain・入力切り捨て・modelを確認
│ └─ No
├─ final top-kで落ちた?
│ ├─ Yes → final件数・重複除去条件を確認
│ └─ No
└─ generationが根拠を使わない
└─ prompt・citation・回答評価へ切り替える
再順位付け スコアの絶対値はモデルごとに意味が異なる場合があります。別モデルのスコアを同じしきい値で比較せず、バージョンごとの順位と評価結果を保存します。
導入前チェックリスト
- 実際の質問と正解チャンクを持つ固定テストセットがある
- 一次候補内に正解が入るかを再順位付け前に測っている
- RRFとCross-Encoderの役割を混同していない
- 対象言語・ドメイン・ライセンスに合うモデルを選んだ
- 最大入力長と切り詰めを確認した
- 候補数と最終件数を設定値としてバージョン管理している
- 導入前後の正解順位、p50、p95、エラー率を記録する
- タイムアウト時に一次検索結果へフォールバックできる
よくある質問
RRFだけでもリランキングになりますか?
広い意味では順位の再構成ですが、Cross-Encoderのような本文再採点とは役割が違います。RRFは複数の順位付き一覧を順位で統合し、Cross-Encoderはクエリと候補テキストのペアをスコアします。
Cross-Encoderを全文書へ使ってはいけませんか?
小さなparagraph集合なら可能です。ただしコーパスが増えるとクエリごとのペア数が増えるため、通常は高速な一次検索で候補を絞ります。
再順位付け スコアが低い候補は捨ててよいですか?
しきい値はモデルとドメインで校正が必要です。まず固定件数で評価し、根拠不足時に拒否する設計と合わせてしきい値を決めます。
日本語RAGでも英語のリランカーを使えますか?
英語専用モデルでは品質を保証できません。日本語またはmultilingual対応がモデルカードで確認できる候補を選び、日本語の実質問セットで比較してください。
再順位付けで回答のハルシネーションはなくなりますか?
なくなりません。根拠チャンクの順位は改善できますが、生成時の誤りは別に起きます。根拠限定プロンプト、引用、回答評価、人への引き継ぎを組み合わせます。
まとめ
RAGのリランキングは、大量コーパスを直接精査するのではなく、一次検索が回収した候補へ限定して行います。複数検索の順位付き一覧はRRFで統合でき、クエリと本文の関連度はCross-Encoderで再採点できます。
導入効果は、正解チャンクが一次候補に含まれたか、再順位付け後に何位へ動いたか、p95 遅延が応答時間の上限内かで判断します。候補数を増やすだけで解決せず、検索、再順位付け、生成を別々の観測点として改善してください。