RAG(検索拡張生成)[1]は、チュートリアル通りに組めば1日で動きます。問題はそこからで、「動くRAG」と「使えるRAG」の間には長い道があります。そして精度が出ないとき、多くの人が最初にやりがちなのがLLMやプロンプトをいじること——実は、これはたいてい遠回りです。
この記事は、RAGの精度改善を疑うべき順序——切り分け→検索→チャンク→文脈→生成→評価——のガイドとしてまとめたものです。LLM製品の設計論は医療LLMの安全設計でも扱いましたが、今回はドメインを問わないRAG本体の話です。
まず「どこで壊れているか」を切り分ける
RAGの失敗は、大きく2種類しかありません。
- 検索ミス(Retrieval miss) — 答えの根拠になる文書が、そもそも検索結果に入っていない
- 生成ミス(Grounding miss) — 根拠は検索できているのに、LLMがそれを使えていない・無視している
切り分けの方法は泥臭いですが単純です。失敗した質問を20〜50件集め、それぞれについて「正解の根拠チャンクがtop-kに入っていたか」を人手で確認する。これで失敗が2群に分かれます。
経験則として、精度に不満のあるRAGの故障の大半は検索ミス側です。LLMは「良い根拠を渡せば良い答えを返す」ところまで既に賢く、渡す根拠を作る検索側がボトルネックになりやすい——だから改善は検索から始めます。
検索を疑う(最初に、最も疑う)
ベクトル検索だけで戦わない — ハイブリッド検索
埋め込みベクトルの類似検索は意味の近さに強い一方、固有名詞・型番・エラーコード・関数名のような「一字一句が大事な語」に弱いという明確な弱点があります。社内文書やテクニカルドキュメントのRAGでは、この弱点が直撃します。
定石は、キーワード検索(BM25)とベクトル検索を併走させ、結果を統合するハイブリッド検索です。統合には、順位の逆数を足し合わせるシンプルなRRF(Reciprocal Rank Fusion)[2]が標準的に使われます。実装は数十行で、検索ミス群に型番系・固有名詞系の失敗が多いなら、まず効きます。
クエリと文書の「語彙ギャップ」を埋める
ユーザーの質問文と文書の書き方が違いすぎて検索が外れるパターンです(質問:「ログインできない」/文書:「認証エラー E4012 のトラブルシューティング」)。対策は2方向あります。
- クエリ書き換え: LLMで質問を検索向きに言い換える・複数のクエリに展開する
- HyDE[3]: 質問に対する「仮の回答」をLLMに生成させ、その仮回答をクエリとして検索する。質問文よりも文書に近い語彙が得られる
リランキングで仕上げる
第一段の検索(高速・粗い)でtop-50を取り、クロスエンコーダ型のリランカー(クエリと文書をペアで評価する精密なモデル)でtop-5に絞り込む2段構成は、コスト対効果の高い改善です。後述のAnthropicの実験でも、リランキング併用が最良の結果を出しています[4]。
チャンクを疑う
「検索する単位」と「読ませる単位」を分ける
チャンク分割の最頻出の誤解は、検索単位と生成に渡す単位を同じにしてしまうことです。小さく索引して、大きく渡す(small-to-big、親子チャンク)が定石です——検索は文単位・段落単位の精度で行い、ヒットしたらその親セクション全体をLLMに渡す。検索精度と文脈の完全性を両立できます。
構造を壊す分割をしていないか
固定長で機械的に切ると、表の途中・コードブロックの途中・箇条書きの途中で文書が真っ二つになります。見出し・表・コードといった文書構造を尊重した分割に変えるだけで、検索ミスも生成ミスも減ります。Markdown化してから見出し単位で切る、が実務では扱いやすい形です。
チャンク単体で意味が通るか — 文脈の付与
チャンクだけ読むと「この製品は」「同機能では」の指示語が解決できない、という文脈の欠落問題があります。Anthropicが提案したContextual Retrieval[4]は、索引化の前に「このチャンクは文書全体のどの文脈にあるか」の短い説明をLLMで生成して各チャンクに付与する手法で、top-20検索の失敗率を49%(リランキング併用で67%)削減したと報告しています。索引時に一度だけコストを払う設計なのも実務向きです。
文脈の渡し方を疑う
検索が当たり始めたら、次はLLMへの渡し方です。
- 詰め込みすぎない。「とりあえずtop-20全部渡す」は逆効果になり得ます。長いコンテキストの中央に置かれた情報は使われにくいという位置バイアス(Lost in the Middle)[5]が知られており、無関係チャンクはノイズとしても働きます。リランキングで絞ったtop-3〜5を、重要なものから順に置くのが基本です
- 出典を構造化して渡す。チャンクにIDを付けて渡し、回答には引用IDを必須にする。引用IDが実在するかは機械検証できます(この検証パターンは医療LLMの安全設計で詳述しました)
- 「資料にない」と言える設計にする。根拠が見つからないときに推測で埋めない指示と、その場合のUI(検索結果ゼロの明示)まで含めて設計します
生成を疑う(最後でいい)
ここまでやって残る失敗が、ようやくプロンプトとモデルの問題です。
- プロンプトには「回答方針(要約か抜粋か)」「引用形式」「拒否条件」を明示する
- モデル変更が効くのは、長文コンテキストの扱いと指示追従が弱いケース。検索が壊れている状態でモデルを替えても、高いモデルが同じ間違いをするだけです
評価がないと、改善は運任せになる
ここまでの改善は、すべて「変更の前後で数字を比べられる」ことが前提です。
- 評価セットを作る: 実際の質問50〜100件に、正解(または正解根拠のチャンクID)を付ける。これが一番面倒で、一番価値のある資産です
- 検索単体を先に測る: recall@k(正解チャンクがtop-kに入る率)やMRRは決定的に計算でき、テストとしてCIに入れられます。生成を通さず検索だけを測れるのがRAGの利点です
- エンドツーエンドはRAGAS系で: 忠実性(根拠にない内容を言っていないか)・回答の関連性・コンテキスト精度などを自動採点するフレームワークとしてRAGAS[6]が代表的です。LLMによる自動採点は万能ではないので、定期的な人手レビューと組み合わせます
評価がCIに入ると、RAGの改善は「変更→自動評価→採否」のループになります。この構造はループエンジニアリングで書いた「検証信号の質がループの質を決める」の実例そのものです。
改善の優先順位 — チェックリスト
| 順序 | 見る場所 | 代表的な打ち手 | 期待効果 |
|---|---|---|---|
| 0 | 切り分け | 失敗50件で検索ミス/生成ミスを分類 | 以降の全判断の根拠 |
| 1 | 検索 | ハイブリッド検索(BM25+ベクトル+RRF)、リランキング | 大 |
| 2 | クエリ | クエリ書き換え・HyDE | 中〜大 |
| 3 | チャンク | 構造を尊重した分割、small-to-big、文脈付与 | 中〜大 |
| 4 | 文脈 | top-kを絞る・並べ替え、引用の構造化 | 中 |
| 5 | 生成 | プロンプト整理、モデル選定 | 小〜中 |
| 常時 | 評価 | 評価セット+recall@k+RAGAS をCIへ | 改善速度そのもの |
まとめ
- RAGの故障は検索ミスか生成ミスかの2種類。まず50件の失敗を人手で切り分ける
- 改善の期待値は概ね「検索 > チャンク > 文脈の渡し方 > 生成」の順。LLMのせいにするのは最後
- ハイブリッド検索+リランキング、構造を尊重したチャンク、文脈付与(Contextual Retrieval)が三種の神器
- 詰め込みすぎはLost in the Middleを招く。絞って、順序をつけて渡す
- 評価セットとrecall@kをCIに入れた時点で、RAGの改善は「勘」から「エンジニアリング」になる
RAGは「LLMアプリ」というより、検索システムの上に薄い生成を載せたものです。検索エンジニアリングの古典(BM25もRRFも2009年以前の技術です)が、生成AI時代の最前線でそのまま効いている——そのこと自体が、この分野の面白さだと思います。
参考文献・一次情報
- [1]
- [2]PAPERReciprocal Rank Fusion outperforms Condorcet and Individual Rank Learning Methods
ハイブリッド検索の融合で標準的に使われるRRFの原論文
- [3]PAPERPrecise Zero-Shot Dense Retrieval without Relevance Labels (HyDE)
仮回答を生成してから検索する、クエリとチャンクの語彙ギャップ対策
- [4]
- [5]PAPERLost in the Middle: How Language Models Use Long Contexts
長いコンテキストの中央に置かれた情報は使われにくい、という位置バイアスの報告
- [6]