RAG構築手順をLangChainとPythonで徹底解説!LCELで効率化
ヨミアゲAI編集部
AI音声・動画制作に関する情報をお届けします
RAG(Retrieval Augmented Generation)は、LLM(大規模言語モデル)の幻覚(hallucination)を抑制し、最新かつ正確な情報を提供するための基盤技術として、2026年5月現在もその重要性を増しています。LangChainはRAGシステム構築のためのデファクトスタンダードとして進化を続けており、特に**LangChain Expression Language (LCEL)**の導入により、複雑なチェインをより直感的かつ効率的に記述できるようになりました。2026年時点では、langchain-core、langchain-community、langchainといったモジュール分離が進み、よりモジュール性とパフォーマンスが向上しています。
LangChainを用いたRAG構築の基本ステップ
ステップ1: 環境構築と依存関係のインストール
RAGシステムを構築するために、まずは必要なライブラリをインストールします。Python 3.10以上が推奨されます。LangChain関連モジュール、OpenAIクライアント、PDFドキュメントローダー(pypdf)、そしてローカルベクトルストア(chromadb)をインストールします。
pip install langchain==0.2.10 langchain-community==0.2.10 langchain-openai==0.2.7 pypdf==4.2.0 chromadb==0.5.3
また、OpenAI APIキーを環境変数に設定しておく必要があります。
export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ステップ2: ドキュメントのロードと分割 (Chunking)
RAGの最初のステップは、外部知識ベースとなるドキュメントをロードし、LLMが処理しやすいように小さなチャンクに分割することです。ここではPDFファイルを例に、LangChainのPyPDFLoaderとRecursiveCharacterTextSplitterを使用します。
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
# ドキュメントのロード
loader = PyPDFLoader("your_document.pdf") # 実際のファイルパスに置き換えてください
documents = loader.load()
# ドキュメントをチャンクに分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # チャンクの最大文字数
chunk_overlap=200, # チャンク間のオーバーラップ文字数
length_function=len,
is_separator_regex=False,
)
chunks = text_splitter.split_documents(documents)
print(f"オリジナルドキュメント数: {len(documents)}")
print(f"分割されたチャンク数: {len(chunks)}")
⚠️ 注意:
chunk_sizeとchunk_overlapはRAGの性能に大きく影響するため、ドキュメントの特性に合わせて調整が必須です。一般的な設定として、chunk_sizeは500〜1500文字、chunk_overlapは100〜300文字が推奨されることが多いです。チャンクが小さすぎると文脈が失われ、大きすぎるとLLMのコンテキストウィンドウを圧迫し、関連性の低い情報も含まれる可能性が高まります。
ステップ3: 埋め込みモデルの選択とベクトルストアへの保存
分割されたテキストチャンクを数値ベクトルに変換する埋め込みモデルを選択し、そのベクトルを保存するためのベクトルストアに格納します。2026年時点では、OpenAIのtext-embedding-3-smallやtext-embedding-3-large、Cohere Embed v4のような高性能な埋め込みモデルが主流です。ここでは、コスト効率と性能のバランスからOpenAI text-embedding-3-small (次元数1536) を利用します。ベクトルストアには、オープンソースで手軽に利用できるChromaDBを使用します。
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
# 埋め込みモデルの初期化
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# ベクトルストアへの保存
# persist_directoryを指定することで、ベクトルストアをディスクに永続化できます
vectorstore = Chroma.from_documents(chunks, embeddings, persist_directory="./chroma_db")
vectorstore.persist() # 明示的に永続化を指示
print("ベクトルストアにチャンクが保存されました。")
💡 ポイント: クラウドベースのベクトルストア(例: Pinecone, Qdrant)を使用する場合、無料枠で最大100,000ベクトルまで利用できるサービスもあります。大規模なRAGシステムでは、スケーラビリティと管理の容易さからクラウドサービスが有利です。
RAGチェインの構築とクエリ実行
ステップ4: Retrieverの準備
ベクトルストアから関連ドキュメントを取得するRetrieverを設定します。ここでは、最も関連性の高い上位k件のドキュメントを取得するように設定します。
# ベクトルストアからRetrieverを作成
# search_kwargs={"k": 3} で、クエリに最も関連性の高い上位3つのドキュメントを取得する
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
print(f"Retrieverが準備されました。取得ドキュメント数: {retriever.search_kwargs['k']}")
ステップ5: LLMの選択とPrompt Templateの定義
質問応答を生成するLLMを選択し、LLMに与えるプロンプトのテンプレートを定義します。2026年5月時点では、コストパフォーマンスに優れたOpenAIのgpt-4o-miniや、Anthropicのclaude-3-5-sonnetが人気です。ここではgpt-4o-miniを使用します。
| モデル名 | 入力 (1Kトークン) | 出力 (1Kトークン) |
|---|---|---|
| GPT-4o-mini | $0.00015 | $0.0006 |
| Claude 3.5 Sonnet | $0.003 | $0.015 |
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# LLMの初期化
llm = ChatOpenAI(model_name="gpt-4o-mini", temperature=0.1)
# プロンプトテンプレートの定義
template = """あなたは与えられたコンテキストに基づいて質問に答えるAIアシスタントです。
もしコンテキストに情報がない場合は、「コンテキストに情報がありません」と答えてください。
回答は簡潔かつ正確に日本語で行ってください。
コンテキスト:
{context}
質問: {question}
"""
prompt = ChatPromptTemplate.from_template(template)
ステップ6: RAGチェインの構築と実行 (LCEL)
LangChain Expression Language (LCEL) を用いて、Retriever、Prompt Template、LLMを連結し、RAGチェインを構築します。LCELはパイプ演算子 (|) を使用して、コンポーネントを繋ぎ合わせることで、データフローを直感的に表現できます。
# 取得したドキュメントを文字列にフォーマットする関数
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# RAGチェインの構築
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# クエリの実行
user_query = "LangChain Expression Language (LCEL) とは何ですか?"
response = rag_chain.invoke(user_query)
print(f"質問: {user_query}")
print(f"回答: {response}")
user_query_no_context = "火星に生命はいますか?"
response_no_context = rag_chain.invoke(user_query_no_context)
print(f"\n質問: {user_query_no_context}")
print(f"回答: {response_no_context}")
⚠️ 注意:
RunnablePassthrough()は、入力値をそのまま次のステップに渡すためのLCELコンポーネントです。ここではユーザーの質問をquestionキーにマッピングしています。これにより、Retrieverがcontextを生成し、ユーザーの質問がquestionとしてLLMに渡されるようになります。
パフォーマンス最適化と将来の展望
RAGシステムの性能をさらに向上させるためには、いくつかの高度な戦略があります。
- チャンク戦略の進化: 2026年においては、単純な固定長チャンクだけでなく、セマンティックチャンキングや、ドキュメントの構造(見出し、段落)を考慮したチャンキング手法がより重要視されています。また、Parent Document RetrieverやMulti-vector Retrieverといった高度なRetrieval手法がLangChainによって容易に実装可能です。
- Rerankingモデルの活用: Retrieverが取得したドキュメントの関連度をさらに高めるために、Cohere RerankやBGE RerankerなどのRerankingモデルを導入することで、RAGの精度を大幅に向上させることができます。これらのモデルは、多くの場合、月額**$50から$500**程度のAPIサービスとして提供されています。Rerankingモデルは、LLMに渡すコンテキストの質を向上させ、応答の精度を高めるために非常に有効です。
- エージェントとの統合: RAGは独立したシステムとしてだけでなく、LangChainのエージェントフレームワークと統合され、複雑なタスクを実行するAIエージェントの一部として機能することが増えています。これにより、LLMが自律的に情報検索を行い、その結果に基づいて意思決定やアクションを実行する高度なシステムが構築可能となります。例えば、エージェントがユーザーの質問を分解し、それぞれに対してRAGツールを用いて情報を検索し、最終的な回答を統合するといったワークフローが一般的になりつつあります。