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_kwargsk値を増やす(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 に外部知識を与えることで、幻覚を減らし正確な回答を生成
K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →