更新: 2026年03月 · 10 分で読める · 5,246 文字
RAGをPythonで実装する:LLMに外部知識を統合させる最小限コード
RAG(Retrieval-Augmented Generation)は、大規模言語モデル(LLM)が社内ドキュメントやデータベースなどの外部知識を参照しながら回答を生成する手法です。この記事では、Pythonで実際に動作するRAGシステムを30分で構築できる実装パターンを紹介します。すぐにあなたのプロジェクトに組み込める完全なコード例を用意しました。
RAGとは:LLMの幻覚問題を解決する仕組み
LLMは学習データの知識のみを持ち、最新情報や社内秘密文書には答えられません。RAGはこの問題を解決します。質問を受け取ると、まず関連ドキュメントを検索(Retrieval)し、その情報をLLMへのコンテキストとして与えてから回答生成(Generation)を行うアーキテクチャです。
イメージとしては、LLMが「顧問」で、ドキュメントベースが「参考資料」。質問に対して参考資料を先に渡してから回答させることで、より正確で最新の回答が得られます。
RAGが活躍する場面
- 社内ナレッジベースを対象にしたチャットボット
- 製品マニュアルを基にしたカスタマーサポート
- 医療記録から患者情報を引き出すシステム
- 法務文書の検索と要約
RAGが不要な場面
- 一般的な雑学や知識だけで充分な場合
- リアルタイム検索能力が不要な用途
- 外部データが頻繁に変わらない環境
最小限のRAG実装:LangChainとOpenAI APIを使った構成
以下のコード例では、LangChainライブラリを使ってRAGシステムの最小限の実装を示します。テスト環境はPython 3.10以上、macOS/Linux/Windows対応です。
必要なライブラリのインストール
pip install langchain langchain-openai langchain-community faiss-cpu python-dotenv
各ライブラリの役割:
langchain:LLMの統合フレームワークlangchain-openai:OpenAI API連携モジュールfaiss-cpu:高速ベクトル検索エンジンpython-dotenv:環境変数管理
基本的なRAGパイプラインの実装
import os
from dotenv import load_dotenv
from langchain.text_splitter import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.vectorstores import FAISS
from langchain.chains import RetrievalQA
# 環境変数から API キーを読み込む
load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
# ステップ1: ドキュメントの読み込みと分割
def prepare_documents(document_path):
"""テキストファイルからドキュメントを読み込む"""
with open(document_path, "r", encoding="utf-8") as f:
raw_text = f.read()
# テキストを小分けにする(オーバーラップ付き)
splitter = CharacterTextSplitter(
separator="\n",
chunk_size=1000, # 1チャンク = 1000文字
chunk_overlap=200 # チャンク間で200文字重複させる
)
documents = splitter.split_text(raw_text)
return documents
# ステップ2: ベクトル化とベクトル DB へ格納
def create_vector_store(documents):
"""ドキュメントをベクトル化して FAISS DB に保存"""
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vector_store = FAISS.from_texts(documents, embeddings)
return vector_store
# ステップ3: RAG チェーンの構築
def setup_rag_chain(vector_store):
"""検索と LLM を連携させるチェーンを作成"""
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.7)
# vector_store をベースに検索機を作成
retriever = vector_store.as_retriever(search_kwargs={"k": 3})
# 検索結果と LLM を組み合わせる
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 検索結果を全て context に含める
retriever=retriever,
return_source_documents=True # 参照したドキュメントも返す
)
return qa_chain
# ステップ4: 実際に質問を投げる
def ask_question(qa_chain, question):
"""RAG システムに質問を投げて回答を取得"""
result = qa_chain.invoke({"query": question})
print(f"質問: {question}\n")
print(f"回答: {result['result']}\n")
print(f"参照ドキュメント:")
for doc in result["source_documents"]:
print(f" - {doc.page_content[:100]}...\n")
return result
# 実行例
if __name__ == "__main__":
# サンプルドキュメントを用意(company_guide.txt)
documents = prepare_documents("company_guide.txt")
print(f"✓ {len(documents)} 個のチャンクに分割しました")
# ベクトル DB を作成
vector_store = create_vector_store(documents)
print("✓ ベクトル DB を作成しました")
# RAG チェーンをセットアップ
qa_chain = setup_rag_chain(vector_store)
print("✓ RAG チェーンをセットアップしました\n")
# 質問を投げる
ask_question(qa_chain, "会社の休暇制度について教えてください")
ask_question(qa_chain, "在宅勤務のポリシーは?")
ハマりやすいポイントと解決策
1. APIキーの認証エラー
症状:AuthenticationError: Incorrect API key provided
解決策:.envファイルが正しくセットアップされているか確認してください。
# .env ファイルの例
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
ファイルは Python スクリプトと同じディレクトリに配置し、.gitignoreに追加します。
2. ベクトル検索の精度が低い
症状:関連性の低いドキュメントが返される
解決策:
chunk_sizeを調整する(1000→500または1500)search_kwargsのk値を増やす(3→5)- より強力なエンベディングモデルを使う(
text-embedding-3-large)
3. 遅い検索速度
症状:FAISS が数秒かかる
解決策:大規模データ用に LangChainPinecone または Weaviate への移行を検討してください。ローカル開発では FAISS で十分です。
RAGの精度を高める実装テクニック
メタデータ付きドキュメント管理
ドキュメントのソースや作成日時を保持することで、回答の信頼性が向上します。
from langchain.schema import Document
# メタデータ付きでドキュメントを作成
def prepare_documents_with_metadata(document_path, source_name):
with open(document_path, "r", encoding="utf-8") as f:
raw_text = f.read()
splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_text(raw_text)
# Document オブジェクトにメタデータを付与
documents = [
Document(
page_content=chunk,
metadata={
"source": source_name,
"created_at": "2025-01-15",
"document_type": "company_guide"
}
)
for chunk in chunks
]
return documents
プロンプト最適化による回答品質向上
from langchain.prompts import PromptTemplate
# カスタムプロンプトテンプレートを定義
custom_prompt = PromptTemplate(
template="""以下の情報を基に、質問に日本語で答えてください。
情報の中に答えがない場合は「文書に記載されていません」と答えてください。
情報:
{context}
質問: {question}
答え:""",
input_variables=["context", "question"]
)
# RAG チェーンで使用
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
chain_type_kwargs={"prompt": custom_prompt}
)
類似ツール・代替手段との比較
LangChain 以外の選択肢としては、LlamaIndex(旧 GPT Index)があります。LlamaIndex はドキュメント索引に特化しており、複雑なドキュメント構造に対応しています。一方、LangChain はより柔軟な LLM チェーン構築が可能です。小規模から中規模プロジェクトでは LangChain で十分です。
テスト環境・動作確認
テスト環境:macOS 14.1 / Python 3.11 / LangChain 0.1.14 / OpenAI API(2025年1月時点)
動作確認済みのサンプルファイルを用意しました。company_guide.txtとして以下の内容を保存してから上記コードを実行してください:
【会社ハンドブック】
休暇制度
- 有給休暇:年間20日
- 特別休暇:結婚、出産、慶弔時に付与
- リフレッシュ休暇:3年勤続後に5日間
在宅勤務ポリシー
- 週3日まで申請可能
- 会議は原則対面出席
- コアタイムは10:00-15:00
給与・賞与
- 基本給は経験と実績で決定
- 年2回(6月、12月)の賞与支給
- 昇給は年1回4月に実施
よくある質問
はい、対応できます。CharacterTextSplitterは日本語の分割にも対応しています。ただし、より精密に分割したい場合はRecursiveCharacterTextSplitterを使用するか、MeCab などの形態素解析器を組み合わせることをお勧めします。
可能です。LangChain は PostgreSQL(pgvector 拡張)、Pinecone、Weaviate などをサポートしています。本番環境では永続化とスケーラビリティの観点から、これらのベクトル DB の使用を推奨します。
以下の施策を優先順に試してください:(1) ドキュメントの前処理を改善(重複排除、ノイズ除去)、(2) chunk_size と chunk_overlap を調整、(3) より高度なエンベディングモデルを使用、(4) プロンプト工学で指示を明確化。それでも駄目な場合は、chain_type="map_reduce"に変更して複数ドキュメントの要約を試してください。
まとめ
- RAG の本質:LLM に外部知識を与えることで、幻覚を減らし正確な回答を生成
おすすめAIリソース
- Anthropic Claude API Docs Official Claude API reference. Essential for implementation.
- OpenAI Platform Official GPT series API documentation with pricing details.
- Hugging Face Open-source model hub with many free models to try.