【第2回】バックエンド編:PythonとLangChainで構築するRAG API実践入門
はじめに:アイデアを形にする「心臓部」を作る
第1回では、2025年を見据えたLLMアプリケーションの全体像とモダンな技術スタックについて解説しました。
シリーズ第2回となる今回は、いよいよコーディングの実践です。アプリケーションの「心臓部」となるバックエンドAPIを、PythonのフレームワークであるFastAPIと、LLM開発の強力な武器であるLangChainを使って構築していきます。
本記事のゴールは、「アップロードされたPDFの内容について、質問応答ができるRAG API」をゼロから作り上げることです。ドキュメントの読み込み、分割、ベクトル化、そしてAPIとして公開するまでの一連の流れを、コピー&ペーストで動かせるコードと共に、ハンズオン形式で徹底解説します。
1. 技術スタックの確認
今回使用する主要なライブラリです。
- FastAPI: モダンで高性能なPythonのWebフレームワーク。API開発に最適です。
- LangChain: LLMアプリ開発を効率化するフレームワーク。複雑な処理を数行のコードで実現します。
- PyPDF: PDFファイルからテキストを抽出するために使用します。
- FAISS: Facebook AIが開発した、高速なベクトル検索ライブラリ。今回はローカルのベクトルストアとして利用します。
- OpenAI: テキストのベクトル化(Embeddings)と、最終的な回答生成(LLM)に利用します。
2. ステップ・バイ・ステップ:RAG API構築
Step 1: プロジェクトのセットアップ
まずは、作業ディレクトリを作成し、必要なライブラリをインストールします。
# 1. プロジェクトフォルダ作成
mkdir rag-api
cd rag-api
# 2. 仮想環境の作成と有効化
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
# 3. 必要なライブラリをインストール
pip install fastapi uvicorn[standard] langchain langchain-openai pypdf faiss-cpu sentence-transformers python-dotenv
次に、プロジェクトのルートに.env
ファイルを作成し、あなたのOpenAI APIキーを設定します。
OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Step 2: RAGパイプラインのコアロジックを実装
rag_pipeline.py
というファイルを作成し、PDFを処理してQAチェーンを構築するロジックを記述します。
# rag_pipeline.py
import os
from dotenv import load_dotenv
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.chains import RetrievalQA
load_dotenv()
FAISS_INDEX_PATH = "faiss_index"
def create_vector_store(pdf_path: str):
"""PDFを読み込み、ベクトルストアを作成・保存する"""
print("Loading PDF...")
loader = PyPDFLoader(pdf_path)
documents = loader.load()
print("Splitting text into chunks...")
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
chunks = text_splitter.split_documents(documents)
print("Creating embeddings and vector store...")
embeddings = OpenAIEmbeddings()
vector_store = FAISS.from_documents(chunks, embeddings)
vector_store.save_local(FAISS_INDEX_PATH)
print(f"Vector store created and saved at {FAISS_INDEX_PATH}")
def create_qa_chain():
"""ベクトルストアを読み込み、QAチェーンを作成する"""
embeddings = OpenAIEmbeddings()
if not os.path.exists(FAISS_INDEX_PATH):
raise FileNotFoundError("Vector store not found.")
vector_store = FAISS.load_local(
FAISS_INDEX_PATH,
embeddings,
allow_dangerous_deserialization=True # FAISSのローカル読み込みに必要
)
llm = ChatOpenAI(model_name="gpt-4o")
# LangChain Expression Language (LCEL) を使った書き方
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vector_store.as_retriever()
)
return qa_chain
Step 3: FastAPIでAPIエンドポイントを作成
main.py
ファイルを作成し、PDFアップロードと質問応答のエンドポイントを定義します。
# main.py
import os
import shutil
from fastapi import FastAPI, UploadFile, File, HTTPException, Form
from fastapi.responses import JSONResponse
from rag_pipeline import create_vector_store, create_qa_chain
app = FastAPI(title="PDF RAG API")
UPLOAD_DIR = "uploaded_pdfs"
os.makedirs(UPLOAD_DIR, exist_ok=True)
# グローバル変数としてQAチェーンを保持
qa_chain = None
@app.post("/upload-pdf/")
async def upload_pdf(file: UploadFile = File(...)):
"""PDFをアップロードし、ベクトルストアを作成するエンドポイント"""
global qa_chain
if not file.filename.endswith(".pdf"):
raise HTTPException(status_code=400, detail="Only PDF files are allowed.")
file_path = os.path.join(UPLOAD_DIR, file.filename)
with open(file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
try:
create_vector_store(file_path)
qa_chain = create_qa_chain() # チェーンを更新
return JSONResponse(status_code=200, content={"message": f"'{file.filename}' processed successfully."})
except Exception as e:
raise HTTPException(status_code=500, detail=f"Failed to process PDF: {str(e)}")
@app.post("/ask/")
async def ask_question(question: str = Form(...)):
"""アップロードされたPDFについて質問するエンドポイント"""
if qa_chain is None:
raise HTTPException(status_code=400, detail="Please upload a PDF first.")
try:
response = qa_chain.invoke({"query": question})
return JSONResponse(status_code=200, content={"answer": response.get("result")})
except Exception as e:
raise HTTPException(status_code=500, detail=f"Error: {str(e)}")
Step 4: APIサーバーの起動とテスト
ターミナルで以下のコマンドを実行し、APIサーバーを起動します。
uvicorn main:app --reload
ブラウザで http://127.0.0.1:8000/docs
にアクセスすると、APIのドキュメント(Swagger UI)が表示されます。ここで、/upload-pdf/
エンドポイントを使ってPDFファイルをアップロードし、その後 /ask/
エンドポイントでその内容について質問してみてください。AIがPDFの内容に基づいて回答を生成すれば成功です!
まとめ:RAG APIという強力な「部品」を手に入れた
今回は、LLMアプリケーションの中核となるRAG APIを構築しました。このAPIは、今後のフロントエンド開発や機能拡張の基礎となる、非常に強力な「部品」です。
- PDFを読み込み、検索可能な知識ベースに変換した
- FastAPIを使い、外部から利用可能なAPIとして公開した
- LangChainを使い、複雑なRAGの処理をシンプルに実装した
このバックエンドがあることで、私たちは次回のフロントエンド開発に集中できます。
次回予告:【第3回】フロントエンド編:Next.jsとVercel AI SDKで作るモダンなチャットUI
いよいよユーザーが直接触れるインターフェースを構築していきます。お楽しみに!
関連記事
– 【第1回】LLMアプリ開発の全体像:2025年の技術スタックと収益化モデル徹底解説
– Pythonとpandasで始めるビジネス分析入門:売上データ分析編
コメント