このページの目次
RAGと検索強化
モデルに丸ごとデータベースを突っ込んで自分で探させる賭けに出るな。RAGは「無限の知識」を「まず検索し、ヒットした数ページをコンテキストに読み込ませる」に分解する——回答の品質を決めるのはコンテキストの長さではなく、検索の精度である。
概要
コンテキストエンジニアリング は繰り返し強調している:検索は無理やり詰め込むより優れている。この章では、その導入部を実践的なエンジニアリングへと展開する——RAG(Retrieval-Augmented Generation、検索強化生成) である。
モデルのパラメータには学習時に見た知識が圧縮されているが、プライベートなドキュメント、今日のデータ、本プロジェクトの規約については知らない。さらに、ウィンドウは有限であり、注意機構は中間部分が見えにくい(lost in the middle)。二つの行き詰まりがある:知識ベース全体をシステムプロンプトに突っ込む(ウィンドウが溢れ、シグナルが希釈され、高コスト)、あるいはファインチューニングで知識を重みに焼き付ける(遅く、高コスト、更新が困難)。RAG は三つの目の道である:知識をウィンドウ外の検索データベースに置いたまま、毎回、現在の質問に関連する断片だけをコンテキストに読み込ませる。
本質を一言で捉えるなら:RAG は「モデルがどれだけ知っているか」という問題を、「検索システムが正しい断片を前面に出せるか」という問題に変換する。 したがって、回答品質のボトルネックはモデルから検索へと移行する——だからこそ、この章の大部分は「どう生成するか」ではなく、「どう正確に検索するか」について語っているのである。
コアなループ: retrieve → augment → generate
flowchart LR
Q["ユーザーの質問"] --> EMB["クエリのベクトル化"]
EMB --> SEARCH["ベクトルデータベース検索<br/>top-k 類似断片"]
DOCS["知識ベース<br/>(チャンク分割 + 事前embed)"] -.オフラインインデックス構築.-> SEARCH
SEARCH --> RERANK["rerank<br/>(任意、精査ランク付け)"]
RERANK --> AUG["コンテキストに結合<br/>ヒット断片 + 質問"]
AUG --> LLM["モデル生成<br/>(引用付き)"]
LLM --> A["回答"]
二段階に分かれる:オフラインインデックス構築(ドキュメントをチャンクに分割し、ベクトル化してベクトルデータベースに投入するのは一度きり)とオンライン検索生成(リクエストごとにクエリをベクトル化し、検索し、結合し、生成する)。後者は エージェントループ に直接接続できる——検索は一つのツールとなり、モデルがいつ、何を検索するかを自律的に決定する。
embedding とベクトル検索: なぜ「意味」で探せるのか
キーワード検索は文字通りの一致に依存するため、「GPUのVRAMを節約する方法」を聞いても、「KV cache量子化」が書かれたセクションにはヒットしない。embedding はテキストの断片を高次元ベクトルにマッピングし、意味的に類似したテキストのベクトルも近くなるため、検索は「ベクトル空間内で最近傍を探す」ことになり、文字面ではなく意味で探すことができる。
- 類似度: 一般的にコサイン類似度(cos similarity)が用いられる。データベース内の各断片を事前に embed して保存し、クエリ時はクエリを一度だけ embed して、データベースベクトルとの類似度を計算し、top-k を取得する。
- ローカル実行可能: embedding はクラウドに頼る必要はない。llama.cpp は embedding モデル(Qwen3-Embedding、bge-m3 など)を直接実行でき、ローカルベクトルデータベース(sqlite-vec、Qdrant、Chroma)と組み合わせれば、オフラインの RAG 環境が構築できる——ローカルLLMのデプロイ を参照。embedding モデルは小さく、バッチ処理が速く、生成モデルに比べてVRAMの負荷は遥かに小さい。
- 次元と規模: 数千〜数万の断片であれば、ブルートフォースな最近傍探索で十分である。百万単位になれば、ANN インデックス(HNSW、IVF)を用いて近似検索の速度を取る。
embedding モデル ≠ 生成モデル。これらはそれぞれ独自のトークン化を行い、互いに汎用性がない。検索データベース用の embed 用モデルと、クエリ時の embed 用モデルは同一でなければならない——モデルを変えるとデータベース全体を再 embed する必要があり、そうしないとベクトルが異なる空間にあり、検索は完全に破綻する。
チャンク戦略: 分割が悪ければ、検索がどれだけ正確でも意味がない
ドキュメントはまずチャンク(塊)に分割して embed する必要がある。チャンク分割は RAG において最も過小評価されがちだが、結果に最も影響を与えるステップの一つである:
| 次元 | トレードオフ |
|---|---|
| チャンクサイズ | 大きすぎると、一つのチャンクに複数のトピックが混在し、ベクトルが希釈され、ヒットしてもノイズが含まれる。小さすぎると意味が不完全になり、「それ」が何を指すのかが不明になる。一般的に 200〜500 トークン。 |
| チャンクオーバーラップ | 隣接するチャンク間に 10〜20% のオーバーラップを持たせ、一文や一つの論点が途中で切れないようにする。 |
| 分割点 | 意味的な境界(見出し、段落、Markdown セクション)で分割する方が、固定文字数で無理やり切るよりも遥かに優れている。コードは関数やクラス単位で切り、行単位では切らない。 |
| メタデータ | 各チャンクにソース、タイトル、セクションパスを付与する——これによりフィルタリング(特定のドキュメントのみを検索)が可能になり、回答での引用にも使える。 |
経験則:まずドキュメントの自然な構造で分割する(このサイトでは ## セクション単位)、その後、大きすぎるチャンク内で文字数で補完する。 チャンク分割の品質は、ベクトルデータベースの選定よりも遥かに重要である。
ヒット率向上: 検索のハイブリッド化と rerank
純粋なベクトル検索には盲点がある:正確な固有名詞、エラーコード、ID などは、文字通りの一致の方がむしろ信頼できる。精度を高める二つの手法:
- ハイブリッド検索: ベクトル検索(意味を扱う)+ キーワード検索 BM25(文字面を扱う)の二つの結果を融合する(例:RRF、reciprocal rank fusion)。「
model_context_window_exceededは何か」と尋ねた際、BM25 はその文字列を正確にヒットさせるが、ベクトル検索は必ずしもそうではない。 - rerank(精査ランク付け): 検索でまず top-50 を粗く取得し、その後 cross-encoder reranker でクエリと断片のペアごとに精査してスコアリングし、本当に最も関連性の高い top-5 をウィンドウに読み込む。リコールは「漏らさない」ことを管理し、精査ランク付けは「過剰なものを除く」ことを管理する——二段階方式は、単純に k を大きくするよりもウィンドウを節約し、正確である。
k を大きくしてカバーしようとするな。 k が大きくなれば、ウィンドウに読み込まれるノイズが増え、context rot を引き起こし、トークンを無駄にする。むしろ広くリコールし、厳しく精査ランク付けを行い、最後に少数精鋭のページだけを置くべきである。
ウィンドウへの結合: コンテキストエンジニアリングへの復帰
断片を検索した後、それをコンテキストにどう配置するかは、コンテキストエンジニアリング の全結論をそのまま再利用する:
- 位置: ヒットした断片を注意機構が強く働く位置(system プロンプト直後、または質問の直前)に配置し、明確な区切り文字や XML タグで囲み、中間部分に埋め込まないようにする。
- キャッシュ: 安定した指示や、変化しない少量の参照情報をプレフィックスに配置する(prompt caching の恩恵を受ける)。毎回変化する検索断片は後方に配置し、プレフィックスに挿入してキャッシュを破壊しないようにする。
- 引用を付与する: モデルに回答内で、各結論がどの断片に由来するかを明記させる(チャンクメタデータによる)。Claude の citations 機能は、引用された断片に位置情報を付与できるが、構造化出力
output_config.formatと排他関係にあるため、どちらか一方を選ぶ必要がある点に注意。
検索の評価: 「正確さ」を定量化する
RAG の失敗は往々にして生成ではなく検索にある——モデルが間違えるのは、正しい断片がそもそも検索されていないからである。したがって、二段階を別々に評価する必要がある(評価と観測 に接続):
- 検索品質(プログラム可能、最も信頼性が高い): 「正解の場所」が分かっている質問セットに対し、recall@k(正しい断片が top-k に入ったか)、precision@k(top-k にどれくらい真に関連するものがあるか)、MRR を計測する。このステップはコードによるアサートのみで、ゼロコスト、バイアスなし。
- 生成品質(オープンエンド): 回答が検索内容に忠実か、幻覚(hallucination)がないか、引用が正しいか——LLM-as-judge を用い、審査者と被審査者を分離する。
問題の特定にはまず検索を見る:recall が低い場合はチャンク / embedding の変更 / rerank の追加を見直す。recall が高くても間違える場合、初めて生成やプロンプトの問題である。
RAG vs 記憶 vs 長文コンテキスト: 混同するな
「モデルにもっと知識を持たせる」ために混同されがちな三つの手段の境界を明確にする(詳細は 記憶と状態 を参照):
| 解決する問題 | 保存場所 | 寿命 | |
|---|---|---|---|
| 長文コンテキスト | 今回のターンで見るべき全資料 | ウィンドウ内 | 1ターン限り |
| RAG | 膨大な知識から必要に応じて関連断片を取得 | ウィンドウ外の検索データベース | データベースは永続、毎ターン少量をウィンドウに読み込み |
| 記憶 | セッションを跨いで好み/結論/失敗経験を記憶 | セッション外のファイル/データベース | セッションを跨いで永続 |
RAG が取得するのは「客観的な知識の断片」であり、記憶が保存するのは「主観的な経験と結論」である。RAG は毎ターン再検索し、記憶はモデルが能動的に書き込み・読み込みを行う。長期的なエージェントではこれら三つを併用することが多い。
ベストプラクティス
- まず検索を計測し、次に生成を調整する。 「正解の場所」が分かる小さな評価セットを構築し、recall@k を最大化する——検索が不正確であれば、モデルを変えようがプロンプトを調整しようが、的を射たものにはならない。
- 意味的な構造でチャンク分割し、十分なメタデータを付与する。 固定文字数よりも見出し/段落/関数の境界を優先する。各チャンクにソースとセクションを付与し、フィルタリングと引用の両方に備える。
- 広くリコールし、厳しく精査ランク付けし、コンテキストは少なく。 粗検索で top-50 → rerank → top-3〜5 のみ配置。k を大きくして無理やり詰め込まない。
- クエリとデータベースは同一の embedding モデルを使用する。 モデルを変更するとデータベース全体を再 embed する必要がある。embedding のアップグレードはグラデーション方式で行い、その場で置き換えない。
- 固有名詞/エラーコード/ID はハイブリッド検索を用いる。 純粋なベクトル検索は文字通りの正確な項目に盲点があるため、BM25 を重ねる。
- 検索断片はウィンドウの後方に配置する。 プレフィックスキャッシュを保護する。安定した指示を前に、変化しやすいヒットを後に配置する。
- 回答に引用を付与し、追跡可能にする。 チャンクメタデータで出典を明記し、幻覚を減らし、人的検証を容易にする。
トレードオフと失敗パターン
- 丸ごとデータベースを system に突っ込む: ウィンドウ溢れ、シグナルの希釈、高コスト → 検索後はヒットした断片のみを配置。
- チャンクが大きすぎる/小さすぎる: 大きなチャンクはベクトルが希釈され、小さなチャンクは意味が分断される → 意味的な境界で分割し、適度なオーバーラップを持たせ、実測で recall を調整。
- クエリとデータベースの embedding が不一致: ベクトルが異なる空間にあり、検索が破綻する → 同一の embedding モデルを固定し、モデル変更時はインデックスを再構築。
- 純粋なベクトル検索で正確な項目を見逃す: エラーコード/固有名詞の文字面一致に失敗 → ハイブリッド検索で BM25 を重ねる。
- k を大きくしてカバーしようとする: ノイズがウィンドウに入り、context rot を引き起こし、トークンを消費 → 広くリコール + rerank で精査 + 少なく配置。
- 生成のみを見ている: 間違いをモデルのせいにするが、実際は断片がリコールされていない → recall@k を計測してから帰属分析を行う。
- 検索断片をキャッシュプレフィックスに挿入する: 毎ターンヒットが異なるため、プレフィックスキャッシュが無効化される → ヒットは後方に配置し、プレフィックスは安定させる。
参考
- 研究: 「Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks」(Lewis et al., 2020)、「Lost in the Middle」(Liu et al., 2023)
- Anthropic 公式ドキュメント: Citations、Structured Outputs(platform.claude.com、実装前に公式を参照)
- ローカル実戦: ローカルLLMのデプロイ(ローカルで embedding とベクトルデータベースを実行)
- 上位/下位: コンテキストエンジニアリング、記憶と状態、評価と観測
Keywords: RAG, retrieval-augmented generation, embedding, ベクトル検索, cosine similarity, 最近傍, ANN, HNSW, チャンク, チャンク分割, オーバーラップ, チャンク分割, BM25, ハイブリッド検索, RRF, rerank, cross-encoder, recall@k, precision@k, MRR, citations, hallucination, 検索は無理やり詰め込むより優れている, vector database, sqlite-vec, Qdrant