チャプター 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個——が取得される。

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

3. 生成

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • text-embedding-3-small / -large(OpenAI)— 確かな品質、汎用、多言語対応。始めるときのデフォルト。
  • bge-large / bge-m3(BAAI)— オープンソース、多言語に優秀、2024年のMTEBランキングのトップ。
  • all-MiniLM-L6-v2(Sentence-Transformers)— 小さく、速く、ローカルにデプロイ可能。シンプルなユースケースに対する品質/コスト比が良い。
  • ドメイン特化モデル(コード、生物医学、法律)— 自分のニッチでは常に優れているが、それ以外では弱い。

実用的な目安:自分のデータと自分の質問で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をプライベートまたは最新のデータに接続するための標準的な手法だ。ほぼすべての企業向けチャットボット、ドキュメントアシスタント、モニタリングツールがこれに基づいている。

しかし、これはまだ「受動的な」アーキテクチャだ。モデルは質問を受け取り、チャンクを見つけ、回答する。より複雑なタスク——検索して、結果に基づいて行動し、また検索する——にはさらに一歩踏み込む必要がある。

更新日

RAG とは:LLM にドキュメントを読ませる · Step by Token