- Published on
プロダクション RAG パターン — 素朴な RAG が失敗する理由と、実際に効く技法
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- はじめに — 素朴な RAG はなぜ失敗するのか
- チャンキング — 品質を静かに決める選択
- 埋め込みとハイブリッド検索(BM25 + ベクトル)
- メタデータフィルタリング — 検索空間をまず狭める
- リランキング — 上位結果の精度
- コンテキスチュアル・リトリーバル — 孤立チャンク問題を直す
- クエリ書き換え — 検索器を途中で迎えに行く
- 評価 — 検索指標と端から端まで(end-to-end)を分けて、自分のデータで
- 実用的な導入順序
- おわりに
- 参考資料
はじめに — 素朴な RAG はなぜ失敗するのか
RAG(検索拡張生成)の基本アイデアは単純です — 質問に関連する文書を探してプロンプトに貼り付け、モデルにそれを根拠として答えさせる。2020 年の Lewis らの原論文は、これをパラメトリック記憶(モデルの重み)とノンパラメトリック記憶(外部インデックス)の結合として定式化しました。デモは半日で作れます — 文書を分割し、埋め込み、ベクトル DB に入れ、質問に最も近い上位 k 件を貼るだけです。
問題は、その素朴なパイプラインがプロダクションで静かに崩れる点にあります。そして失敗はたいてい生成ではなく 検索 から来ます。
- ベクトル検索は意味はよく捉えますが 正確なトークン一致を取りこぼします — エラーコード、製品 SKU、人名、API 名など。
- 文書から切り出した断片は 自分の文脈を失います。 「売上が 3% 増えた」というチャンクだけでは、どの企業のどの四半期か分かりません。
- 検索器は誤っていても もっともらしい断片を静かに返し、 モデルはそれを自信たっぷりに引用します。幻覚に見えて、実は検索の失敗です。
核心となる直観は一つです — 検索の品質が下流のすべてに上限をかけます。 関連文書がそもそもコンテキストに入らなければ、どんなプロンプト工夫でも取り戻せません。ですから以下の技法の多くは「生成を賢くする」ではなく「正しい断片をコンテキストに入れる」を狙います。
コンテキスト窓を広げて「全部詰め込む」ことで回避したい誘惑もありますが、それは解決策ではありません — 長いコンテキストではモデルは中間に埋もれた情報を取りこぼしやすく(いわゆる lost-in-the-middle)、トークンコストと遅延も一緒に増え、何より正しい文書がそもそもその中になければならないという問題はそのまま残ります。
チャンキング — 品質を静かに決める選択
何か。 文書を、検索・埋め込みの単位に分割することです。よくある失敗は固定長(たとえば文字数)で機械的に切ることで、文の途中、表の真ん中、コードブロックの中央を割ってしまいます。
いつ効くか。 境界を意味の単位に合わせると検索精度が上がります。基本は二つです。 (1) 構造を尊重して切る — 段落・見出し・表・コードの単位。 (2) スライディングウィンドウ / オーバーラップ で隣接チャンクを少し重ね、境界にまたがる文が丸ごと消えないようにする。「Searching for Best Practices in RAG」(2024) は、おおむね 256〜512 トークン 帯の小さめのチャンクが忠実度(faithfulness)で有利だったと報告します。
代償。 チャンクが小さいほど精密ですが文脈は薄くなり、大きいほど文脈は豊かでも一つのチャンクに雑音が混ざって検索信号がぼやけます。最適解はコーパスごとに異なります — だからチャンクサイズは、推測ではなく評価で決める値です。
埋め込みとハイブリッド検索(BM25 + ベクトル)
何か。 埋め込み検索(密・dense)はテキストをベクトルに変え 意味的な類似度 で探します。BM25(疎・sparse)は TF-IDF 系の語彙マッチで 正確な単語一致 を探します。両者は失敗の仕方が違います — ベクトルは同義語・言い換えに強い一方で希少トークンを逃し、BM25 は正確なトークンに強い一方で表現が変わると見つけられません。
いつ効くか。 ほぼ常に。多くのプロダクションのコーパスには二種類の質問が混ざっています — 「返金ポリシーを要約して」(意味)と「ERR_2043 とは何か」(語彙)。だから ハイブリッド 検索(両方を走らせて結果を統合)が強力な既定値になります。統合はしばしば RRF(Reciprocal Rank Fusion) で行い、二つのランキングを順位の逆数で合算して、片方のスコア尺度に引きずられないようにします。
代償。 インデックスを二種類(ベクトルストアと転置索引)運用することになり、統合の重みという調整つまみが一つ増えます。それでも通常は最も ROI の高い第一歩です — 純粋な密検索が構造的に取りこぼす正確一致の質問を取り戻せるからです。
メタデータフィルタリング — 検索空間をまず狭める
何か。 ベクトルや BM25 でスコアを付ける前に(あるいはそれと同時に)、文書の属性で候補を絞り込むことです — テナント ID、作成日、文書種別、そして何より アクセス権限(ACL)。
いつ効くか。 マルチテナントの SaaS や、権限が分かれる社内知識ベースでは、選択肢ではなく必須です。ユーザーが見られない文書を結果に漏らせば、それは品質の問題ではなく セキュリティ事故 です。副次的に、検索空間が狭まって精度と遅延も改善します。
代償。 きれいなメタデータを索引の時点で付けておく必要があり、絞りすぎると必要な文書まで切り落とすことがあります。フィルタはランキングではなくハード制約だ、という点を忘れないでください。
リランキング — 上位結果の精度
何か。 一次検索が候補を多め(たとえば上位 20〜100 件)に引いてきたら、クロスエンコーダ のリランカーが(質問, 文書)の組を一緒に読んで関連度を付け直します。埋め込みは質問と文書を別々にベクトル化(バイエンコーダ)しますが、リランカーは両者を同時に見るので、はるかに精密です。
いつ効くか。 一次検索の再現率(recall)は悪くないのに、上位の精度が物足りないとき。経験則は「広く引いて、狭く残す」 — 20 件を検索して 5 件にリランクし、そのうち 3〜5 件をモデルへ渡します。前述のベストプラクティス論文は、リランキングのモジュールを外すと性能が目に見えて下がったとして、その必要性を強調しています。
代償。 クロスエンコーダは候補ごとに順伝播を一度ずつ走らせるので、遅延とコストが増えます。だから全件ではなく 短い候補リスト にだけ適用します。候補を 100 件、200 件と際限なく増やしてリランクしても、たいてい元は取れません。
コンテキスチュアル・リトリーバル — 孤立チャンク問題を直す
何か。 チャンキングが生む根本的な欠陥が一つあります — 切り出された断片は出典の文脈を失います。Anthropic の コンテキスチュアル・リトリーバル は、埋め込みと BM25 索引の 前に、各チャンクの先頭に、そのチャンクが文書全体のどこに属するかを説明する短い文脈(通常 50〜100 トークン)を LLM で生成して付け足します。「このチャンクは ACME の 2023 年 Q3 決算報告からの抜粋」のように。
いつ効くか。 代名詞や相対参照が多く、文書が長いコーパスで特に。Anthropic が報告した数値は印象的です — recall@20 での失敗率(1 − recall@20)で測ると、コンテキスチュアル埋め込みだけで失敗を 35% 削減(5.7% → 3.7%)、コンテキスチュアル BM25 を加えて 49% 削減(→ 2.9%)、さらにリランキングを重ねて 67% 削減(→ 1.9%)でした。
代償。 チャンクごとに LLM を一度呼ぶ索引時のコストがかかります。Anthropic はプロンプトキャッシュを使って文書 100 万トークンあたり約 $1.02 程度としていますが、文書が更新されるたびに再実行が必要で、パイプラインの複雑さも増します。静的で参照が絡み合った知識ベースほど、その価値を発揮します。
クエリ書き換え — 検索器を途中で迎えに行く
何か。 ユーザーの元の質問を、検索の前に整える段階です。形はいくつかあります — 会話での後続質問の代名詞を解いて独立した質問にする(脱文脈化)、略語・専門用語の展開、複合質問を下位質問に分解する(マルチクエリ)、あるいは HyDE のように仮の答えを先に生成し、それを埋め込んで検索する。
いつ効くか。 マルチターンの会話、短く曖昧な質問、ユーザーの語彙と文書の語彙が食い違う場合。質問がすでによく整っていれば、必要性は下がります。
代償。 LLM 呼び出しが一つ増えて遅延とコストがかさみ、新しい失敗モード が生まれます — 書き換えが外れると、元の質問より悪い結果を招きます。ですから書き換えもまた、有無を評価で比べるべき対象であって、常時オンにしておくスイッチではありません。
評価 — 検索指標と端から端まで(end-to-end)を分けて、自分のデータで
二つの層を分けること。 RAG の評価は必ず二筋に分けて見ます。
- 検索指標 — recall@k、precision@k、MRR、nDCG。「正しいチャンクがコンテキストに入ったか」を測り、質問→関連文書のラベルセットが要ります。
- 端から端まで(生成)の指標 — 忠実度/根拠性(答えが検索された文脈に実際に裏付けられているか=幻覚の有無)、回答の関連性、回答の正確性。しばしば LLM-as-judge で採点します。
なぜ分けるのか — 優れた生成器が、弱い検索を 覆い隠す からです(その逆も)。端から端までのスコアだけを見ると、検索が漏れていても大丈夫に見えることがあります。検索の recall が低ければプロンプトでは救えません — 層を分けてこそ、ボトルネックがどこかが見えます。
プロダクションでは、失敗を二つの箱に分ける習慣が役立ちます — (a) 正しい文書がそもそも検索されなかった、(b) 検索はされたのに生成が無視した、あるいは誤って使った。(a) は検索段(チャンキング・ハイブリッド・リランキング)で直し、(b) はプロンプト・引用の強制・モデルで直します。この仕分けなしに端から端までのスコアだけを眺めると、見当違いの場所をずっと直し続けることになります。
公開ベンチマークはあなたのコーパスを予測しません。 実際の質問から小さなラベルセット(数十件もあれば出発点として十分)を作り、各技法の有無を A/B で比べてください。これはモデルより評価セットが先だという eval-first の原則 の RAG 版です。評価が先にあれば、チャンクサイズ・統合の重み・リランカーの選択は、好みではなく実験になります。
実用的な導入順序
一度に全部入れないでください。たいていこの順序が最も報われます。
- 構造を尊重した チャンキング + オーバーラップから。ここで漏れると後段が全部漏れます。
- ハイブリッド検索(BM25 + ベクトル、RRF で統合) — 正確一致の質問を取り戻す、最も安上がりな大きい改善。
- リランキング — 広く引いて狭く残す。上位の精度を押し上げます。
- 参照が絡んだ長い文書なら コンテキスチュアル・リトリーバル。
- 会話的・曖昧な質問が多ければ クエリ書き換え。
コーパスがマルチテナントであるか権限が分かれているなら、メタデータフィルタリング(とりわけアクセス制御)はこの順序とは別に、最初から既定であるべきです — 後から足す最適化ではなく、初日の要件です。
そしてこれらすべての 前に 小さな評価セットを先に作ってください。そうすれば、各段階が実際に役立ったかを数字で答えられます。
おわりに
RAG は魔法ではありません — LLM に検索を足しても、知識の問題が消えるわけではありません。ただ 検索の問題に変わる だけです。そして検索は数十年来の、今なお難しい分野です。救いは、ここでの最大の改善がたいてい最も地味な場所から来ることです — より大きなモデルではなく、より良いチャンキングとハイブリッド検索、そして多めに引いてリランクする習慣から。
一つだけ持ち帰るなら、これです — 自分のデータで評価せよ。 上の技法は道具箱であって、レシピではありません。あるコーパスではコンテキスチュアル・リトリーバルが勝負を決め、別の場所では BM25 を一つ有効にするだけがすべて、ということもあります。その違いはベンチマークではなく、あなたの質問の中でしか現れません。