RAG解説:LLMアプリに独自データを追加する方法

AI2026-09-24TryQuickToolBox

GPT-4やClaudeのような強力なLLMを使ってチャットボットを構築したものの、一般的な回答しか返さず、社内文書について知らないという経験はありませんか? 自社データに関する質問に答えさせるにはどうすればよいでしょうか? ファインチューニングは高コストで静的です。その解決策がRetrieval-Augmented Generation(RAG)です。

この記事では、RAGを実用的な観点から解説し、段階的な実装方法と、プライベートデータを活用した信頼性の高いLLMアプリケーションを構築するためのベストプラクティスを紹介します。

RAGとは?

RAGは2つの強力な技術を組み合わせたものです:検索(ナレッジベースから関連情報を見つける)と生成(LLMを使って回答を生成する)。LLMの事前学習済み知識だけに頼るのではなく、RAGは関連する文書を動的に取得し、プロンプトに含めることで、モデルの応答をあなたのデータに基づかせます。

AIにとっての「持ち込み可能な試験」のようなものだと考えてください。回答する前に、あなたの文書で答えを調べることができます。

なぜRAGを使うのか?

RAGの仕組み:コアコンポーネント

典型的なRAGシステムには4つの主要部分があります:

  1. 文書の取り込み:文書(PDF、Webページ、データベース)を読み込み、前処理します。
  2. 埋め込みモデル:テキストチャンクを意味を捉えた数値ベクトルに変換します。
  3. ベクトルデータベース:これらのベクトルを保存し、高速な類似検索のためにインデックス化します。
  4. LLM:取得したコンテキストを使って回答を生成します。

ステップバイステップ:RAGパイプラインの構築

1. 文書の準備

データソースを収集します:PDF、Markdownファイル、Confluenceページ、SQLテーブルなど。テキストをクリーニングし(ヘッダー、フッター、定型文を削除)、コンテキストを保持するために200~500語のチャンクに分割し、多少のオーバーラップ(例:50語)を持たせます。

Pythonを使った例:

from langchain.text_splitter import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50
)
chunks = text_splitter.split_text(your_document)

2. 埋め込みの生成

OpenAIのtext-embedding-3-smallやall-MiniLM-L6-v2のようなオープンソースモデルを使用して、各チャンクをベクトルに変換します。

from openai import OpenAI
client = OpenAI()

def get_embedding(text):
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=text
    )
    return response.data[0].embedding

3. ベクトルデータベースへの保存

ベクトルデータベースを選択します:Pinecone、Weaviate、Qdrant、あるいはpgvectorを使ったPostgreSQLなど。各チャンクの埋め込みをメタデータ(ソース、ページ番号)とともに保存します。

import pinecone
pinecone.init(api_key="YOUR_KEY", environment="us-west1-gcp")
index = pinecone.Index("rag-demo")

vectors = [(f"chunk-{i}", get_embedding(chunk), {"text": chunk}) for i, chunk in enumerate(chunks)]
index.upsert(vectors=vectors)

4. 関連チャンクの取得

ユーザーが質問すると、クエリを埋め込み、最も類似した上位k件のチャンクを検索します。

query_embedding = get_embedding(user_question)
results = index.query(vector=query_embedding, top_k=3, include_metadata=True)
context = "\n\n".join([match.metadata["text"] for match in results.matches])

5. 回答の生成

取得したコンテキストを含むプロンプトを構築し、それに基づいて回答するようLLMに指示します。

prompt = f"""Answer the question based only on the following context:
{context}

Question: {user_question}
Answer:"""

response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": prompt}]
)
print(response.choices[0].message.content)

RAG vs ファインチューニング

側面 RAG ファインチューニング
データの鮮度 リアルタイム更新 再トレーニングまで静的
コスト 低(埋め込み+ストレージ) 高(トレーニング計算)
実装 モジュール式で簡単 複雑でMLの専門知識が必要
ユースケース 文書に関するQ&A スタイル適応、ドメイン用語

RAGのベストプラクティス

よくある落とし穴と回避策

FAQ

RAGとファインチューニングの違いは何ですか?

RAGは推論時に関連情報を取得してプロンプトに含めますが、ファインチューニングはモデルの重みを調整して新しいパターンを学習させます。RAGは動的で事実的な知識に適しており、ファインチューニングはスタイルやドメイン適応に適しています。

RAGにベクトルデータベースは必要ですか?

厳密には必要ありません。任意の検索インデックス(例:Elasticsearch)や、小規模データセットならインメモリのコサイン類似度でも可能です。しかし、PineconeやQdrantのようなベクトルデータベースは、高速でスケーラブルな類似検索に最適化されています。

RAGシステムを評価するにはどうすればよいですか?

検索品質(precision@k、recall)と生成品質(回答関連性、忠実性)を測定します。RagasやTruLensなどのツールが評価の自動化に役立ちます。

結論

RAGは、独自データをLLMアプリケーションに追加するための実用的でコスト効率の高い方法です。上記の手順に従い、ベストプラクティスを遵守することで、プライベートなナレッジベースを使って正確に質問に答えるAIアシスタントを構築できます。小さく始め、反復し、常に評価しましょう。

RAGパイプライン用に文書を準備する際、複数のPDFを1つのファイルに結合して取り込みを容易にしたいことがよくあります。無料のPDF Mergerを使って、PDFを迅速かつ安全に結合しましょう。