本稿は RAG(Retrieval-Augmented Generation / 検索拡張生成) に関する記事です。
| この記事の要点 |
- RAG は「外部知識を検索 → LLM に渡して回答させる」手法
- LLM のハルシネーションを抑え、社内文書・最新情報・固有知識を反映できる
- 基本フロー: 文書分割 → Embedding → ベクトルDB → 検索 → LLM プロンプトに付与 → 回答
- ファインチューニングより低コストで知識更新が容易(DB を差し替えるだけ)
- 主要フレームワーク: LangChain / LlamaIndex / Haystack
- 精度向上の鍵: チャンク分割の質 / Embedding モデル選定 / 再ランク / ハイブリッド検索
- 限界: 検索でヒットしない情報は答えられない。チャンク分割品質に強く依存
|
RAG とは?
RAG (Retrieval-Augmented Generation / 検索拡張生成) は、LLM が回答する際に外部の知識ベースから関連情報を検索し、その内容をプロンプトに含めて回答させる手法です。
LLM 単体は学習時点の知識しか持たず、社内文書や最新情報を知りません。RAG は「LLM の言語能力」と「検索エンジンの正確性」を組み合わせ、社内 FAQ ボット・ナレッジ検索・カスタマーサポート等で実用性を一気に高めました。
なぜ RAG が必要か
| LLM 単体の弱点 | RAG での対処 |
| 学習データのカットオフより新しい情報を知らない | 最新文書を検索して参照 |
| 社内固有情報を知らない | 社内ドキュメントを RAG の対象に |
| ハルシネーション(もっともらしい嘘) | 出典付きの回答で検証可能に |
| 巨大コンテキスト全部投入はコスト高 | 関連箇所だけ抽出して投入 |
| 情報更新にはファインチューニングが必要 | DB を差し替えるだけで反映 |
RAG の基本フロー
| フェーズ | ステップ | 担当技術 |
事前準備 (Indexing) | 1. 文書を集める | PDF / Web クローラ / DB エクスポート |
| 2. テキスト抽出・前処理 | PDF パーサ・OCR・HTML 抽出 |
| 3. チャンク分割 | Text Splitter(再帰的 / 文・段落単位) |
| 4. Embedding 生成・DB 格納 | Embedding モデル + ベクトルDB |
クエリ時 (Retrieval & Generation) | 5. 質問を受信 | ユーザ入力 |
| 6. 質問を Embedding | 同じ Embedding モデル |
| 7. ベクトルDB で類似検索 | 近傍 k 件取得 |
| 8. (任意)再ランク | Cohere Rerank / BGE Reranker |
| 9. LLM プロンプトに連結 → 回答生成 | LLM (GPT / Claude / Gemini) |
主要ライブラリ・サービス
| 分類 | 代表 |
| フレームワーク | LangChain、LlamaIndex、Haystack |
| Embedding | OpenAI text-embedding-3、Cohere Embed、BGE、E5 |
| ベクトルDB | Chroma、Faiss、pgvector、Pinecone、Qdrant、Weaviate |
| 再ランク | Cohere Rerank、BGE Reranker、Jina Reranker |
| ドキュメントパース | LlamaParse、Unstructured、PyMuPDF、pdfplumber |
| LLM プロバイダ | OpenAI、Anthropic、Google、Mistral、ローカル LLM |
| マネージド SaaS | Azure AI Search、Vertex AI Search、AWS Kendra、Pinecone |
最小サンプル(LangChain)
|
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# 1) 文書読込 → チャンク分割
docs = PyPDFLoader("manual.pdf").load()
splits = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=100).split_documents(docs)
# 2) Embedding → ベクトルDB
vs = Chroma.from_documents(splits, OpenAIEmbeddings(), persist_directory="./db")
retriever = vs.as_retriever(search_kwargs={"k": 4})
# 3) RAG チェーン
prompt = ChatPromptTemplate.from_template("以下の参考情報を踏まえて質問に答えてください。\\n\\n参考:\\n{context}\\n\\n質問: {question}")
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
rag = ({"context": retriever, "question": RunnablePassthrough()}
| prompt | llm | StrOutputParser())
print(rag.invoke("返金ポリシーを教えて"))
|
精度を上げる工夫
| 工夫 | 効果 |
| チャンク分割の調整 | サイズ・重複・分割単位(文・段落・見出し)を調整 |
| メタデータフィルタ | 部門・公開日・言語等で事前絞り込み |
| ハイブリッド検索 | 意味検索(ベクトル)+ キーワード検索(BM25)を組合せ |
| 再ランク (Reranker) | 上位 k 件を再評価。Cohere Rerank / BGE Reranker |
| クエリ書き換え | あいまいな質問を LLM で具体化してから検索 |
| HyDE | 仮の回答を LLM に生成させ、それで検索 |
| マルチクエリ | 質問を複数言い換えて広く検索 |
| サブクエリ分解 | 複合質問を細かく分けて個別に検索 |
| 埋め込みモデル切替 | ドメイン特化や多言語モデルへ |
| 引用付き回答 | 「参考: [n]」をプロンプトで強制 |
発展形: GraphRAG / Agentic RAG
| 補足: 標準 RAG の限界を超える |
- GraphRAG: 知識グラフを併用。複数文書をまたいだ関係性質問に強い(Microsoft 提唱)
- Agentic RAG: エージェントが検索 → 評価 → 追加検索を繰り返す。複雑な質問に対応
- マルチモーダル RAG: 画像・表・PDF を含む検索
- セルフルーティング RAG: 質問種別で別の DB / 別の手法に振り分け
|
RAG vs ファインチューニング
| 観点 |
RAG |
ファインチューニング |
| 知識の更新 | DB を差し替えるだけ | 再学習が必要 |
| 導入コスト | 低い | 高い (GPU・データ準備) |
| 出典の提示 | 容易(検索元 URL を返す) | 困難 |
| レイテンシ | 検索分のオーバーヘッド | 追加遅延なし |
| 形式・口調の制御 | プロンプトで指示 | 得意(学習で固定) |
| 専門ドメインの深い知識 | 検索ヒットに依存 | 強い |
| 用途 | FAQ、ナレッジ検索、最新情報 | 特定スタイル、構造化出力 |
多くの業務シナリオでは「RAG で十分」。両方を組合せる選択肢もある。
運用上のヒント
| Tips |
- 評価セットを作る(質問 / 期待回答 / 出典)。ベンチマークが無いと改善できない
- 失敗ログを観察。検索失敗 / 生成失敗 / プロンプト不備のどれか切り分け
- 引用 ID を返させる:
[doc_3] 等を出力に強制 → 検証可能
- 「分からない場合は分からないと言う」をプロンプトで明示
- チャンク分割は意味の単位で切る(見出し・段落で分割)
- 本番では増分インデックスを仕組み化(全件再構築は高コスト)
- 監視: 検索ヒット率・回答時間・LLM トークン消費・ユーザ評価
|
注意点
| よくある落とし穴 |
- 検索ヒットしなければ答えられない。再ランクや HyDE で底上げ
- チャンクが大きすぎる / 小さすぎる — 情報が薄まる / 文脈が切れる
- PII / 機密情報を全部 Embedding: アクセス権付き RAG にする
- Embedding モデルの変更: 既存ベクトルが使えなくなる。再生成が必要
- 同じ用語の表記揺れ: 検索ヒット率が下がる。前処理で正規化
- 古い情報 vs 新しい情報の混在: メタデータ(公開日)でフィルタ
- プロンプトインジェクション: 検索結果に悪意ある指示が紛れる可能性
- 多言語データ: 単言語 Embedding だと精度劣化。多言語モデルを使う
|
関連