RAG入門 — 検索拡張生成の基礎知識を実験結果とともに学ぶ
RAGとは何か — 検索で回答精度を上げるアーキテクチャ
RAG(Retrieval-Augmented Generation: 検索拡張生成)は、外部データを検索してLLMに渡すことで回答の正確性を高めるアーキテクチャである。2020年にMeta AIのLewisらが提唱した(要出典: Lewis et al., 2020)。
LLM単体には以下の課題がある。
- ハルシネーション: 学習データにない情報を「もっともらしく」生成してしまう
- 知識のカットオフ: 学習時点以降の情報を持たない
- ドメイン知識の欠如: 社内規定や自社製品情報など、非公開データに対応できない
RAGはLLMモデル自体を再学習させるのではなく、回答生成の直前に関連情報を検索して渡すというアプローチでこれらを解決する。社内ドキュメント検索、FAQ自動応答、カスタマーサポートなど「特定のデータから正確な情報を引き出したい」場面で採用されている。
RAGの仕組み — 格納フェーズと検索フェーズ
RAGの処理は「格納フェーズ」と「検索・回答フェーズ」の2つに分かれる。
格納フェーズ(データの準備)
テキストデータをVectorDBに格納するまでの流れは以下の通り。
テキストファイル(社内規定、製品マニュアル等)
↓ ① チャンク分割(適切な単位に分割)
チャンクのリスト
↓ ② Embeddingモデルでベクトル化(数値の配列に変換)
ベクトルのリスト
↓ ③ VectorDBに格納(ベクトル + 元テキスト + メタ情報)
VectorDB
テキストをそのまま格納するのではなく、チャンク分割 → ベクトル化という前処理を経る。この前処理の質が、検索精度を左右する。
検索・回答フェーズ(質問への応答)
ユーザーが質問を投げてから回答が返るまでの流れは以下の通り。
ユーザーの質問
↓ ① 同じEmbeddingモデルでベクトル化
質問ベクトル
↓ ② VectorDBで類似検索(質問に近いチャンクをk件取得)
関連チャンク(上位k件)
↓ ③ LLMに「質問 + 関連チャンク」を渡す
回答を生成 → ユーザーに返却
格納時と検索時で同じEmbeddingモデルを使う必要がある。同じモデルで変換するからこそ同じ「意味の空間」に配置され、ベクトル間の距離比較が成り立つ。
RAGの3つのコンポーネント
RAGの検索精度を決めるのは、VectorDB・Embedding・チャンク分割の3つである。
VectorDB — 意味で検索するデータベース
VectorDB(ベクトルデータベース)は、ベクトルを大量に保存し、「このベクトルに近いものをk件返す」という類似度検索を高速に行うデータベースである。
RDB(PostgreSQL等)との違いは以下の通り。
| RDB(PostgreSQL等) | VectorDB(Qdrant等) | |
|---|---|---|
| 検索方法 | WHERE句で完全一致・範囲指定 | ベクトル間の距離(類似度)で検索 |
| インデックス | B-Tree, Hash | HNSW, IVF等の近似最近傍インデックス |
| 得意なこと | 「部署 = 営業部」のような正確な絞り込み | 「この質問に意味的に近い文書」を探す |
| 苦手なこと | 曖昧な意味的検索 | 完全一致検索、数値の大小比較 |
VectorDBに格納されるデータは、検索用ベクトル・LLMに渡す元テキスト・出典表示用のメタ情報で構成される。
{
"vector": [0.12, -0.45, 0.78, ...],
"payload": {
"page_content": "【経費精算】書籍購入は年間30,000円まで...",
"metadata": { "source": "company-policy.txt" }
}
}
検索時は質問ベクトルと全ベクトルの距離を計算し、近い順に返す。全件計算は遅いため、HNSW(Hierarchical Navigable Small World)というインデックスで効率的に絞り込む。RDBにおけるB-Treeインデックスに相当する仕組みである。
代表的なVectorDBにはQdrant、Pinecone、Chroma、pgvector(PostgreSQL拡張)がある。検証・学習目的ならローカル実行可能なQdrantやChroma、本番運用ならPineconeやWeaviateが選択肢に入る。
Embedding — テキストを数値に変換する
Embedding(埋め込み)は、テキストを数値の配列(ベクトル)に変換する処理である。Embeddingモデルが大量のテキストデータから学習した「意味の法則」に基づき、意味が近い文は近いベクトルに変換される。
「経費精算」 → [0.12, -0.45, 0.78, ..., 0.33] (2560次元)
「書籍購入費」 → [0.11, -0.42, 0.81, ..., 0.35] (2560次元) ← 近い
「今日の天気」 → [-0.55, 0.22, 0.01, ..., -0.18] (2560次元) ← 遠い
人間がルールを定義したのではなく、モデルが大量のテキストから統計的に「意味の近さ」を学んだ結果である。そのためモデルによって精度が異なり、日本語データで多く学習したモデルの方が日本語の意味を正確に捉えられる。
Embeddingモデル選定で考慮すべき3つの観点を以下に示す。
| 観点 | 説明 |
|---|---|
| 対応言語 | 日本語データなら多言語対応モデルが必須 |
| 次元数 | 高次元ほど表現力が高いがストレージ・計算コスト増 |
| モデルサイズ | 大きいほど精度が高い傾向だがメモリ消費増 |
実験で確認: Embeddingモデル変更の効果
筆者のRAG検証環境(14ファイル・539行の社内ドキュメント、検索パラメータk=3)で、Embeddingモデルの変更が検索精度にどう影響するかを検証した。
| 構成 | 5問中の正答数(k=3に正解が入った数) |
|---|---|
| 文字数分割 + nomic-embed-text | 0/5 |
| 見出し分割 + nomic-embed-text | 1/5 |
| 見出し分割 + qwen3-embedding:4b | 4/5 |
nomic-embed-text(英語中心)からqwen3-embedding:4b(多言語対応、2560次元)への変更で正答率が劇的に改善した。日本語データを扱う場合、多言語対応のEmbeddingモデルの選定がRAG精度に直結する。
チャンク分割 — 検索精度を左右するデータの切り方
チャンク分割は、テキストデータを検索に適した単位に分割する処理である。以下の3つの理由で必要になる。
- LLMに渡せるコンテキストには長さの制限がある
- ドキュメント全体をベクトル化すると意味が希釈される
- 関連する部分だけを取得してLLMに渡す必要がある
分割戦略はデータの性質ごとに設計が必要である。万能な分割戦略はない。
| データ種別 | 有効な分割単位 |
|---|---|
| 社内規定・マニュアル | 見出し・セクション単位 |
| FAQ | Q&Aペア単位 |
| チャットログ・議事録 | 時系列・スレッド単位 |
| 契約書・法律文書 | 条項単位 |
| ソースコード | 関数・クラス単位 |
1チャンクに複数トピックが混在するとベクトルの意味が曖昧になり、検索精度が下がる。
悪い例: 【勤務時間】+【有給休暇】+【経費精算】→ 1チャンク
→ ベクトルが「勤務」「休暇」「経費」の中間的な意味になり、
どの質問にも中途半端にしかマッチしない
良い例: 【経費精算】→ 1チャンク
→ ベクトルが「経費」に集中し、経費関連の質問に的確にマッチする
1チャンク=1トピックを意識した分割が、検索精度改善の基本である。
検索精度に影響する要因 — 実験で確認したこと
筆者のRAG検証では、Embeddingモデル・チャンク分割戦略・検索パラメータkの3つが検索精度に影響することを確認した。
Embeddingモデルの選定が最もインパクトが大きい
3構成のクエリ別結果を以下に示す。順位の数値はVectorDB内での検索ランクである。
| クエリ | 文字数+nomic | 見出し+nomic | 見出し+qwen3 |
|---|---|---|---|
| 書籍の経費上限は? | 圏外 | 4位 | 2位+3位 |
| DataBridge vs DataLink料金? | DataLink圏外 | DataLink 4位 | 1位+2位 |
| リモートワーク週何日? | 3位(旧情報) | 1位 | 1位+2位 |
| 資格の報奨金は? | 1位(旧情報) | 2位 | 1位+2位 |
| サポート連絡先は? | 圏外 | 圏外 | 1位+2位 |
Embeddingモデルの変更はチャンク分割の改善よりインパクトが大きい。日本語データを扱うRAGでは、多言語対応Embeddingモデルの選定を最優先にすべきである。
チャンク戦略4種の比較
別の検証で、4種類のチャンク分割戦略を同一条件で比較した。
| 戦略 | チャンク数 | Hit Rate | MRR | Source Hit Rate |
|---|---|---|---|---|
| standard_1000 | 30 | 90.0% | 0.850 | 80.0% |
| large_2000 | 18 | 80.0% | 0.800 | 80.0% |
| parent_child | 60 | 80.0% | 0.800 | 80.0% |
| hypothetical_questions | 30 | 100.0% | 0.783 | 90.0% |
各指標の意味は以下の通り。
- Hit Rate: 正解チャンクが上位k件に含まれた質問の割合。取りこぼしの少なさを示す
- MRR(Mean Reciprocal Rank): 正解チャンクの順位の逆数の平均(1位=1.0、2位=0.5、3位=0.333、圏外=0)。1位ヒット率を示す
- Source Hit Rate: 期待するソースファイルが上位k件に含まれた割合
1位精度重視ならstandard_1000(MRR 0.850)、取りこぼし最小化ならhypothetical_questions(Hit Rate 100%)が有効という結果が得られた。
kを増やしても根本解決にならない
VectorDB検索のパラメータk(取得件数)を増やせば取りこぼしは減るが、ランキング自体は変わらない。
- kが小さい → 関連チャンクを取りこぼすリスク
- kが大きい → ノイズが増え、LLMのコンテキストが長くなり処理が遅くなる
正解チャンクのスコアが低い場合、k=3をk=8に増やしても正解は上位に来ない。根本的な改善にはEmbeddingモデルの変更やチャンク分割の見直しが必要であり、kの調整は補助的な手段である。
RAGの精度をさらに上げる手法
基本的なRAGの精度が確保できたら、以下の手法で改善を重ねられる。
Hybrid Search(ベクトル検索 + キーワード検索)
ベクトル検索は意味的な類似度に強いが、金額・日付・固有名詞などの具体的な値の検索には弱い。BM25などのキーワード検索と組み合わせるHybrid Searchで、意味検索とキーワードマッチの両方をカバーできる。
Re-ranking(検索結果の再評価)
ベクトル検索で広めに取得(k=20程度)した後、クロスエンコーダモデルで再スコアリングして上位N件に絞り込む手法。ベクトル検索の取りこぼしをカバーし、精度の高い結果をLLMに渡せる。
History-Aware Retriever(会話文脈の活用)
マルチターンの会話で代名詞を含む質問が来た場合、会話履歴を踏まえてクエリを書き換えてから検索する手法。
筆者の検証結果を以下に示す。
| ターン | 質問 | 回答 | 判定 |
|---|---|---|---|
| 1 | リモートワークは週何日まで? | 週3日まで | OK |
| 2 | それは最近変更されましたか? | 2025年10月1日から週4日に変更 | OK |
2ターン目で「それ」が「リモートワーク制度」として正しく解決され、変更情報を含むチャンクが検索された。履歴なしで同じ質問を送ると無関係なチャンクが返るため、クエリ書き換えが機能していることを確認できた。
まとめ — RAGは「検索の質」が全てを決める
RAGは「検索で関連情報を取得し、LLMに渡して回答を生成する」アーキテクチャである。回答生成モデルがいくら優秀でも、検索で正解チャンクが取れなければ正しい回答は生成できない。
筆者の検証で得られた知見は以下の3点に集約される。
- Embeddingモデルの選定が最もインパクトが大きい — モデル変更だけで正答率0/5→4/5
- チャンク分割は「1チャンク=1トピック」を意識する — データの性質に応じた分割戦略が必要
- kを増やすだけでは根本解決にならない — ランキング自体の改善が必要
RAG構築時に確認すべき3つのポイント
- [ ] 対象データの言語に適したEmbeddingモデルを選定したか?
- [ ] チャンク分割は「1チャンク=1トピック」になっているか?
- [ ] Hit RateやMRRなどの定量指標で検索精度を計測しているか?
RAGの精度改善は「検索の質を上げる」ことに尽きる。まずは手元のデータで小規模な検証を始め、Embeddingモデルとチャンク分割を調整しながら精度を計測・改善していくのが実践的なアプローチである。
Comments