Contents
1. はじめに
LLM(大規模言語モデル)を自社アプリやデータ分析ツールに組み込みたいとき、「Python + LangChain」 が最も手軽です。本稿では以下の流れで解説します。
- 開発環境の構築方法(Python バージョン・仮想環境・パッケージインストール)
- API キーや環境変数の安全な管理方法
- 基本的なチェーン (
LLMChain、ConversationalRetrievalChain) の実装例 - PDF / Markdown / Notion ローダーとベクトルストア (FAISS・Chroma) を組み合わせた RAG パイプライン
- 完全動作するミニプロジェクトのコードとデバッグ手法
この記事を読み終えると、「PDF だけで質問応答できるアプリ」 がローカル環境で動かせます。
2. LangChain の位置付け
| 機能 | 説明 |
|---|---|
| Chain | プロンプト生成 → LLM 呼び出し → 結果加工 を一連のオブジェクトとして定義でき、コードの再利用性が高まります。 |
| Agent | 外部ツール(検索 API、データベース等)を動的に呼び出すロジックをシンプルに記述できます。 |
| エコシステム | PDFLoader・FAISS・Chroma など多数のプラグインが公式で提供されており、RAG(Retrieval‑Augmented Generation)構築が数行で完結します。 |
LangChain は「LLM アプリの土台」を提供し、ビジネスロジックに集中できる実務向けフレームワークです。
3. 開発環境のセットアップ
3.1 Python バージョンと仮想環境
| 推奨 | 理由 |
|---|---|
| Python 3.10 以上(3.11 が最も安定) | LangChain 本体・FAISS・Chroma が公式にサポートしている範囲です。 |
| venv または conda の仮想環境 | プロジェクトごとの依存関係を分離でき、CI/CD でも再現性が保てます。 |
venv の作成例
|
1 2 3 4 5 6 7 8 |
# ディレクトリ作成 & 移動 mkdir langchain-rag && cd langchain-rag # 仮想環境構築 (Python3.11 を使用) python3.11 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # pip の最新版に更新 pip install --upgrade pip ``` |
3.2 必要パッケージとバージョン指定
2024 年時点で推奨されるインストールコマンドは次の通りです。langchain-community はローダー系機能を提供する別パッケージなので明示的に入れます。
基本パッケージ + すべての公式プラグイン
|
1 2 3 4 5 6 |
pip install "langchain[all]==0.2." \ "langchain-community==0.2." \ faiss-cpu==1.8.0 \ chromadb==0.5. \ python-dotenv==1.0. \ loguru==0.7.* |
OpenAI ライブラリは別パッケージとして提供
|
1 |
pip install "langchain-openai==0.2.*" |
ポイント
-requirements.txtに上記行を書き出しておくと、チーム開発や CI が楽になります。
- GPU 環境で高速検索をしたい場合はfaiss-gpuに置き換えてください。
requirements.txt の例
langchain[all]==0.2.*
langchain-community==0.2.*
langchain-openai==0.2.*
faiss-cpu==1.8.0
chromadb==0.5.*
python-dotenv==1.0.*
loguru==0.7.*
3.3 環境変数の管理
API キーはコードに直書きせず .env に保存し、.gitignore で除外します。Notion 用トークン名は NOTION_TOKEN と統一しました。
.env.example
|
1 2 3 4 5 6 7 |
# OpenAI (ChatGPT, GPT‑4 など) OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXXXXXX # Anthropic Claude の場合(任意) ANTHROPIC_API_KEY=claude-xxxxxxxxxxxxxxxxxxxx # Notion Integration Token NOTION_TOKEN=secret_yyyyyyyyyyyyyyyyyyyyyyyy ``` |
.gitignore に追加すべき行
gitignore
.env
__pycache__/
.venv/
Python 側での読み込みは次のようにします。
|
1 2 3 4 5 6 |
from dotenv import load_dotenv import os load_dotenv() # カレントディレクトリの .env を自動ロード OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY") NOTION_TOKEN = os.getenv("NOTION_TOKEN") |
4. 基本的なチェーン構築例
4.1 LLMChain(シンプルな要約)
langchain-openai が提供する OpenAI クラスを使用します。インポートパスは langchain.llms ではなく langchain_openai に変更しました。
|
1 2 3 |
from langchain.prompts import PromptTemplate from langchain.chains import LLMChain from langchain_openai import OpenAI |
プロンプトテンプレート(日本語だけで可読性を保ちます)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
prompt_template = """以下のテキストを要約してください。 {text} ---""" prompt = PromptTemplate( input_variables=["text"], template=prompt_template, ) llm = OpenAI( model_name="gpt-4o-mini", temperature=0.2, api_key=OPENAI_API_KEY, ) summary_chain = LLMChain(llm=llm, prompt=prompt) |
実行例
|
1 2 |
sample_text = "LangChain は、LLM アプリ開発を高速化するフレームワークです。" print(summary_chain.run(text=sample_text)) |
4.2 ConversationalRetrievalChain(対話型検索)
会話履歴とベクトル検索を組み合わせたチェーンです。FAISS インデックスは事前に作成しておく想定です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from langchain.chains import ConversationalRetrievalChain from langchain.memory import ConversationBufferMemory from langchain.vectorstores import FAISS from langchain.embeddings import OpenAIEmbeddings from langchain_openai import OpenAI # 1. 埋め込みモデルとインデックスのロード embeddings = OpenAIEmbeddings(api_key=OPENAI_API_KEY) vectorstore = FAISS.load_local("faiss_index", embeddings) # 2. メモリ(会話履歴)設定 memory = ConversationBufferMemory(memory_key="chat_history") # 3. LLM インスタンス llm = OpenAI( model_name="gpt-4o-mini", temperature=0, api_key=OPENAI_API_KEY, ) # 4. チェーン構築 qa_chain = ConversationalRetrievalChain.from_llm( llm, retriever=vectorstore.as_retriever(search_kwargs={"k": 4}), memory=memory, ) |
実行例
|
1 2 |
response = qa_chain({"question": "LangChain の主な特徴は?"}) print(response["answer"]) |
5. データローダーとベクトルストアで RAG パイプラインを作る
5.1 ローダーの統一インタフェース
| ローダー | 用途 | インポート |
|---|---|---|
| PDFLoader | PDF ファイルからテキスト抽出 | from langchain_community.document_loaders import PDFLoader |
| MarkdownLoader | Markdown ファイルを読み込み | from langchain_community.document_loaders import UnstructuredMarkdownLoader(内部は UnstructuredFileIOLoader) |
| NotionDBLoader | Notion データベースのページ取得 | from langchain_community.document_loaders import NotionDBLoader |
ローダー使用例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import os from dotenv import load_dotenv from langchain_community.document_loaders import PDFLoader, UnstructuredMarkdownLoader, NotionDBLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. PDF pdf_loader = PDFLoader("data/sample.pdf") pdf_docs = pdf_loader.load() # 2. Markdown md_loader = UnstructuredMarkdownLoader("docs/README.md") md_docs = md_loader.load() # 3. Notion(データベース ID とトークンが必要) notion_loader = NotionDBLoader( integration_token=NOTION_TOKEN, database_id="YOUR_NOTION_DATABASE_ID" ) notion_docs = notion_loader.load() # 共通テキスト分割器(1,000 トークン程度に切り出す) splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) def split_documents(docs): """Document オブジェクトのリストをチャンク化して返す""" return splitter.split_documents(docs) pdf_chunks = split_documents(pdf_docs) md_chunks = split_documents(md_docs) notion_chunks = split_documents(notion_docs) |
注意点
-UnstructuredMarkdownLoaderは内部でunstructuredライブラリを利用します。インストールが必要な場合はpip install "unstructured[md]"を追加してください。
5.2 ベクトルインデックスの作成
5.2.1 FAISS(オンプレミス向け高速検索)
|
1 2 3 4 5 6 |
from langchain.vectorstores import FAISS from langchain.embeddings import OpenAIEmbeddings emb = OpenAIEmbeddings(api_key=OPENAI_API_KEY) # PDF と Markdown のチャンクを結合してインデックス化 faiss_index = FAISS.from_documents(pdf_chunks + md_chunks, emb) faiss_index.save_local("faiss_index") |
5.2.2 Chroma(ローカル永続化が簡単)
|
1 2 3 4 5 6 |
from langchain.vectorstores import Chroma chroma_store = Chroma.from_documents( documents=pdf_chunks + md_chunks, embedding=emb, persist_directory="chroma_db" ) |
persist_directory に自動で保存され、次回ロード時は同名ディレクトリを指定すれば OK
5.2.3 パラメータ調整の目安
| パラメータ | 推奨設定 |
|---|---|
k(上位取得件数) | 4〜8 |
score_threshold(FAISS のみ) | 0.7 以上で高信頼性 |
distance_metric | "cosine" が直感的 |
6. 完全動作ミニプロジェクト
以下のファイル一式で PDF → RAG QA が実行できます。コードは日本語コメントのみで統一し、可読性を高めました。
6‑1 requirements.txt
text
langchain[all]==0.2.*
langchain-community==0.2.*
langchain-openai==0.2.*
faiss-cpu==1.8.0
chromadb==0.5.*
python-dotenv==1.0.*
loguru==0.7.*
6‑2 .env.example(先ほどと同様)
dotenv
OPENAI_API_KEY=sk-XXXXXXXXXXXXXXXXXXXXXXXX
ANTHROPIC_API_KEY=claude-xxxxxxxxxxxxxxxxxxxx # 任意
NOTION_TOKEN=secret_yyyyyyyyyyyyyyyyyyyyyyyy # Notion を使う場合のみ
6‑3 run_qa.py
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
!/usr/bin/env python -- coding: utf-8 -- """ PDF ドキュメントを対象にした質問応答システム(RAG)サンプル。 以下の流れで処理を行います。 PDF をロードしテキスト分割 OpenAIEmbeddings でベクトル化 → FAISS インデックス作成・永続化 ConversationalRetrievalChain に LLM とインデックス、メモリを組み合わせる REPL 形式の対話 UI を提供 """ import os from loguru import logger from dotenv import load_dotenv # LangChain 関連 from langchain_community.document_loaders import PDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import FAISS from langchain_openai import OpenAI from langchain.chains import ConversationalRetrievalChain from langchain.memory import ConversationBufferMemory ------------------------------------------------------------ 1. 環境変数読み込み ------------------------------------------------------------ load_dotenv() OPENAI_KEY = os.getenv("OPENAI_API_KEY") if not OPENAI_KEY: logger.error("OPENAI_API_KEY が .env に設定されていません。") raise SystemExit(1) ------------------------------------------------------------ 2. PDF のロード & テキスト分割 ------------------------------------------------------------ def load_and_split(pdf_path: str): """PDF を読み込み、1000 トークン前後のチャンクに分割して返す""" logger.info(f"Loading PDF from {pdf_path}") loader = PDFLoader(pdf_path) docs = loader.load() splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) chunks = splitter.split_documents(docs) logger.info(f"Created {len(chunks)} text chunks") return chunks ------------------------------------------------------------ 3. FAISS インデックス作成(永続化あり) ------------------------------------------------------------ def build_faiss_index(chunks): """OpenAIEmbeddings でベクトル化し、FAISS に保存""" embedder = OpenAIEmbeddings(api_key=OPENAI_KEY) index = FAISS.from_documents(chunks, embedder) index.save_local("faiss_pdf") logger.success("FAISS index saved to ./faiss_pdf") return index ------------------------------------------------------------ 4. QA 用チェーン作成 ---------------------------- def create_qa_chain(index): llm = OpenAI( model_name="gpt-4o-mini", temperature=0, api_key=OPENAI_KEY, ) memory = ConversationBufferMemory(memory_key="chat_history") qa_chain = ConversationalRetrievalChain.from_llm( llm, retriever=index.as_retriever(search_kwargs={"k": 4}), memory=memory, ) logger.success("ConversationalRetrievalChain が作成されました") return qa_chain ------------------------------------------------------------ 5. メイン処理(REPL) ------------------------------------------------------------ chunks = load_and_split(pdf_path) index = build_faiss_index(chunks) qa_chain = create_qa_chain(index) print("\n=== PDF QA Bot (type 'exit' or 'quit' to stop) ===\n") while True: query = input("質問 > ").strip() if query.lower() in {"exit", "quit"}: break try: result = qa_chain({"question": query}) print("\n回答 :", result["answer"]) except Exception as e: logger.error(f"エラーが発生しました: {e}") def main(): pdf_path = "data/your_document.pdf" # ← 任意の PDF に置き換えてください if not os.path.isfile(pdf_path): logger.error(f"{pdf_path} が見つかりません。パスを確認してください。") return chunks = load_and_split(pdf_path) index = build_faiss_index(chunks) qa_chain = creat print("\n=== PDF QA Bot (type 'exit' or 'quit' to stop) ===\n") while True: query = input("質問 > ").strip() if query.lower() in {"exit", "quit"}: break try: result = qa_chain({"question": query}) print("\n回答 :", result["answer"]) except Exception as e: logger.error(f"エラーが発生しました: {e}") if name == "main": main() |
実行手順
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 1. リポジトリクローン(例) git clone https://github.com/yourname/langchain-pdf-qa.git cd langchain-pdf-qa # 2. 仮想環境作成 & アクティベート python3.11 -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # 3. パッケージインストール pip install -r requirements.txt # 4. 環境変数設定(.env を作成し、キーを書き込む) cp .env.example .env # エディタで API キーを入力してください # 5. サンプル PDF を data/ に置くかパスを書き換えて実行 python run_qa.py ``` |
6‑4 デバッグ・ロギングのポイント
| シーン | 推奨ロガーメッセージ |
|---|---|
| ローダーが失敗したとき | logger.error("PDF のロードに失敗しました: {e}") |
| インデックス作成が遅い | logger.info(f"Embedding に要した時間: {elapsed:.2f}s") |
| LLM 呼び出しでタイムアウト | logger.warning("OpenAI API が応答しません。リトライを検討してください") |
loguru の利点はシンプルな設定だけで日時・レベルが自動付与され、開発中の情報取得が容易になることです。
7. 次に挑戦したいテーマ
-
CI/CD での API キー管理
GitHub Actions のsecretsにOPENAI_API_KEY等を登録し、デプロイ時に自動ロードする。 -
マルチソース RAG
NotionLoader と WebScraper(langchain_community.document_loaders.WebBaseLoader)を追加し、検索対象を増やす。 -
エージェント化
Toolインタフェースで SerpAPI や Wikipedia API を組み込み、ユーザーの質問に対して外部情報も取得できるようにする。 -
評価指標とトラッキング
LangSmith(LangChain の公式 MLOps ツール)を導入し、質問ごとの正答率やトークン使用量を可視化する。
8. まとめ
- 環境構築は Python 3.10+ + venv/conda が基本。
langchain[all]とlangchain-community、そしてlangchain-openaiをインストールすれば最新機能が揃います。 - API キーは
.envに保存し、環境変数名は統一(NOTION_TOKEN) して安全に扱いましょう。 - LLMChain と ConversationalRetrievalChain を使えば、シンプルな要約から対話型検索まで幅広く実装できます。インポートパスは最新版に合わせて
langchain_openai系を利用してください。 - PDF・Markdown・Notion のローダーと RecursiveCharacterTextSplitter でテキストを統一フォーマットに変換し、FAISS または Chroma に埋め込むだけで RAG パイプラインが完成します。
- 本稿のミニプロジェクト
run_qa.pyをローカルで走らせれば、すぐに「PDF への質問応答」体験ができます。その先はデータソース拡張やエージェント化、評価可視化です。
さあ、手元の PDF で QA ボットを動かし、LangChain の実力を体感してください。あなたのプロジェクトが次世代 AI アプリケーションへと進化する第一歩です 🚀