チャプター 10 · RAG · 9 min

あなたのドキュメントを読む

LLMが記憶せずに何千ページにもアクセスする方法。埋め込み、セマンティック検索、注入されたコンテキスト。

コンテキストが解決できない限界

会社の社内ドキュメント——5万ページ、毎週更新される——に基づいて質問に答えられるアシスタントを構築したいとする。

100万トークンのコンテキストウィンドウがあっても、すべてを詰め込むことはできない。仮にできたとしても、コストが膨大になり、応答の品質が低下する。非常に長いコンテキストから正確な情報を抽出するのは、モデルにとって難しいことだ。

別のアプローチが必要だ。それが RAG——検索拡張生成(Retrieval-Augmented Generation)だ。

根本的なアイデア

ドキュメント全体をモデルに渡すのではなく、質問に関連する段落だけを渡す。そのために必要なものが2つある:

  1. インデックス:すべてのドキュメントをあらかじめベクトル化して保存したもの。
  2. セマンティック検索エンジン:質問が届いたとき、意味的に最も近い段落を見つけるもの。

取得した段落は質問とともにLLMのコンテキストに注入される。モデルはその抜粋を元に回答を生成する。

これは第3章で学んだ埋め込みによるセマンティック検索——それを実際のドキュメントに応用したものだ。

パイプラインの3ステップ

1. インデックス作成(一度だけ)

ドキュメントをチャンク——数百トークンの断片(意味が途切れないよう、少し重複させる)——に分割する。

各チャンクは埋め込みモデルによってベクトルに変換される。これらのベクトルはベクトルデータベース(Pinecone、Chroma、pgvectorなど)に保存される。

この処理は一度だけ行うか、ドキュメントが更新されたときに行う。

2. 検索(質問のたびに)

質問が届くと、同じ埋め込みモデルでベクトルに変換される。

次に、この質問ベクトルとデータベース内のチャンクベクトルとのコサイン類似度を計算する。最も近い k 個のチャンク——通常3〜5個——が取得される。(実のところ、すべてのチャンクと比較しているわけではない。数万ベクトルを超えたあたりから、ベクトルデータベースは近似インデックス——HNSW、IVF——を使い、全件を走査せずに最近傍を見つける。むしろそれがベクトルデータベースの存在理由だ。)

これがセマンティック検索だ。「近い」とは「同じ単語を含む」ではなく、「似た意味を表現している」ということを意味する。

3. 生成

取得したチャンクをプロンプトに組み立てる:

ドキュメントの抜粋:
[抜粋1 — ...]
[抜粋2 — ...]
[抜粋3 — ...]

質問:{ユーザーの質問}

このプロンプトがLLMに送られ、抜粋を元に回答が生成される。モデルはドキュメントを記憶しているのではなく、質問が届いた瞬間に読み込んでいる。

パイプラインを見てみよう

質問を 1 つ選ぶ:それが embedded され、コーパスのチャンクと比較され、最も関連性の高いものが生成前にプロンプトへ挿入される。この迂回こそが、学習時に見たことのない文書について LLM に答えさせる仕組みである。

この操作から持ち帰るべきことが2つある。ひとつめ。取得されるチャンクは、質問の単語を含むものではない。「最も大きい惑星は?」と尋ねればガス惑星に関する抜粋が返ってくるが、「大きい」という語はそれらの抜粋のどこにも現れていない。決めているのはベクトル空間内での近さであって、語彙ではない。ふたつめ。モデルはコーパス全体を一度も見ていない。選ばれたひと握りの抜粋しか受け取らないのだ。つまりRAGの品質はすべて、生成ので決まる。正しいチャンクが取得されなければ、どれほど巧みなプロンプトでも取り返せない。この章の残りが、分割と検索の話ばかりになるのはそのためだ。

チャンクサイズ:重要な設計の選択

チャンクへの分割は単純ではない。RAGシステムで問題が生じる主な原因だ。

チャンクが小さすぎる場合:各チャンクの情報量が少なすぎる。文脈から切り離されたフラグメントが取得され、モデルが正しく回答できない。

チャンクが大きすぎる場合:埋め込みの精度が下がる。2,000トークンを表すベクトルはぼやけた平均値だ——特定の質問には取得されるかもしれないが、関連するコンテンツがテキストの中に埋もれてしまう。

実用的な目安:200〜500トークンのチャンク、10〜15%の重複。最適なパラメータはドキュメントの構造に依存する。

どの埋め込みモデルを使うべきか?

すべての質問が同じ埋め込みで適切なチャンクにたどり着くわけではない。汎用日本語で訓練されたモデルはPythonコードをうまく検索できない;コードで訓練されたモデルは医療のやり取りをうまく検索できない。

実用上よく登場する選択肢:

  • text-embedding-3-small / -large(OpenAI)— 確かな品質、汎用、多言語対応。長らく始めるときのデフォルトだった。今ではベンチマーク上は追い抜かれているが、実用上はまったく問題なく使える。
  • bge-large / bge-m3(BAAI)— オープンソース、多言語に優秀。2024年のMTEBランキングを制していた。
  • Gemini Embedding(Google)— 英語MTEBのトップ。
  • Qwen3-Embedding(Alibaba)— オープンウェイトのモデルでは最良で、自前でホストできる。
  • Voyage 3.x、Cohere embed-v4、Jina v4 — 現時点の商用リファレンス。最後のものはマルチモーダルで、テキストと画像を同じ空間に置く。
  • all-MiniLM-L6-v2(Sentence-Transformers)— 小さく、速く、ローカルにデプロイ可能。シンプルなユースケースに対する品質/コスト比が良い。
  • ドメイン特化モデル(コード、生物医学、法律)— 自分のニッチでは常に優れているが、それ以外では弱い。

このランキングは半年ごとに入れ替わる。そして読むときの注意点がひとつ。MTEB v2のスコアはv1のスコアと比較できない。両者の間でタスクが変わっている。

実用的な目安:自分のデータと自分の質問で2〜3個のモデルをテストすること。MTEBのランキングは、あなた特有のユースケースについてはほとんど何も語らない。

リランカー:第二のパス

ベクトル検索には欠点がある:速いが粗い。数百次元の埋め込みはテキストのぼやけた平均だ。「だいたい関連がある」チャンクが多く浮上し、本当に良いチャンクが埋もれてしまうこともある。

そこで、本格的なRAGシステムでは標準となった2段階目のステップ:リランカー

アイデアは2段階:

  1. 第一パス(ベクトル検索)— 最も近い50〜100個のチャンクを取得する。速い。
  2. 第二パス(リランキング)— 小さなモデル(多くの場合クロスエンコーダ)が (質問, チャンク) のペアを一緒に評価し、精密なスコアを与えて、上位5〜10件だけを残す。チャンクごとに遅いが、第一パスで絞られた50候補だけに走らせる。

リランカーは質問チャンクを同時に見ることができる——埋め込みにはできないことだ。ベクトル検索では見逃される意味の機微を捉える。Cohere、Jina AI、BAAIが既製のリランカーを提供している。

リランカーがないと、RAGの品質は頭打ちになる。導入すれば、パイプラインの他を変えなくても品質が一段上がる。

なぜ全文検索では不十分なのか

当然の疑問:なぜ古典的な検索エンジンのようなキーワード検索ではいけないのか?

ベクトル検索はその代替ではなく、補完だ。表現が異なっていても意味的に近い段落を見つけられる。「最も遅く自転する惑星はどれ?」という質問に対して、「公転周期」について書かれたチャンクを見つけられる——質問にその言葉が含まれていなくても。

実際、最良のシステムは両方を組み合わせる:ハイブリッド検索——ベクトル類似度とキーワードマッチング(BM25)の組み合わせスコア。

RAGの限界

RAGは万能ではない。よくある問題を挙げる:

不適切な分割によるチャンク。回答が2つのチャンクにまたがっており、分割位置が悪い場合、どちらのチャンクも単独では不十分になる。

「中間で迷子になる」問題。チャンクが長すぎると、重要な情報が中央に位置し、モデルに無視される可能性がある。

曖昧な質問。質問が漠然としていると、質問の埋め込みが関連するチャンクから離れてしまう。解決策:質問を言い換えるか、複数の変形を生成する。

抜粋に基づくハルシネーションLLMはチャンクの内容を超えて推測することがある。RAGはハルシネーションを減らすが、完全には排除しない。

RAGが実践で変えること

RAGは今日、LLMをプライベートまたは最新のデータに接続するための標準的な手法だ。ほぼすべての企業向けチャットボット、ドキュメントアシスタント、モニタリングツールがこれに基づいている。

しかし、古典的なパイプライン——embed、retrieve、generate——は「受動的な」アーキテクチャだ。モデルは質問を受け取り、チャンクを見つけ、回答する。2025年以降、これは多くのユースケースでエージェント的検索に取って代わられつつある。モデルに検索ツールを与え、必要なものが揃うまでループしながら検索させ、言い換えさせ、掘り下げさせるやり方だ。より複雑なタスク——検索して、結果に基づいて行動し、また検索する——にはさらに一歩踏み込む必要がある。

更新日