mcpField
L
langchain-rag

langchain-rag

github.com/langchain-ai開発1.3k17.7k 回インストール3 日前に更新
リポジトリ

langchain-rag について

Python と TypeScript で LangChain の検索拡張生成パイプラインを構築するガイドです。ドキュメントの取り込み、ベクトルインデックス、検索、回答生成に加え、設定と安全なインデックス読み込みも説明します。

主な機能

  1. 01ドキュメントの読み込みと分割
  2. 02埋め込みとベクトルストア
  3. 03類似度・MMR 検索
  4. 04メタデータによる検索フィルター
  5. 05Python と TypeScript の例
  6. 06インデックスの永続化と安全性

活用シーン

  1. 01ドキュメントを使った質問応答の構築
  2. 02PDF、ウェブ、テキストのインデックス作成
  3. 03関連度スコア付きの検索
  4. 04ベクトルインデックスの永続化と再読み込み

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/langchain-ai/langchain-skills --skill langchain-rag

インストール後、Claude Code で/langchain-ragを入力して読み込まれたか確認します。

詳細

検索拡張生成(RAG)は、外部のナレッジソースから関連するコンテキストを取得することで、LLM の応答を強化します。

パイプライン:

  1. インデックス作成:読み込み → 分割 → 埋め込み → 保存
  2. 検索:クエリ → 埋め込み → 検索 → ドキュメントを返す
  3. 生成:ドキュメント + クエリ → LLM → 応答

主要コンポーネント:

  • ドキュメントローダー:ファイル、ウェブ、データベースからデータを取り込みます
  • テキストスプリッター:ドキュメントをチャンクに分割します
  • 埋め込み:テキストをベクトルに変換します
  • ベクトルストア:埋め込みを保存し、検索します
ベクトルストア用途永続化
InMemoryテストメモリのみ
FAISSローカル、高パフォーマンスディスク
Chroma開発ディスク
Pinecone本番環境、マネージドクラウド

RAG パイプライン全体

ドキュメントの読み込み、チャンクへの分割、埋め込み、保存、検索を行い、応答を生成する、エンドツーエンドの RAG パイプラインです。
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import InMemoryVectorStore
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document

# 1. Load documents
docs = [
    Document(page_content="LangChain is a framework for LLM apps.", metadata={}),
    Document(page_content="RAG = Retrieval Augmented Generation.", metadata={}),
]

# 2. Split documents
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
splits = splitter.split_documents(docs)

# 3. Create embeddings and store
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = InMemoryVectorStore.from_documents(splits, embeddings)

# 4. Create retriever
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})

# 5. Use in RAG
model = ChatOpenAI(model="gpt-4.1")
query = "What is RAG?"
relevant_docs = retriever.invoke(query)

context = "\n\n".join([doc.page_content for doc in relevant_docs])
response = model.invoke([
    {"role": "system", "content": f"Use this context:\n\n{context}"},
    {"role": "user", "content": query},
])
ドキュメントの読み込み、チャンクへの分割、埋め込み、保存、検索を行い、応答を生成する、エンドツーエンドの RAG パイプラインです。
import { ChatOpenAI, OpenAIEmbeddings } from "@langchain/openai";
import { MemoryVectorStore } from "@langchain/classic/vectorstores/memory";
import { RecursiveCharacterTextSplitter } from "@langchain/textsplitters";
import { Document } from "@langchain/core/documents";

// 1. Load documents
const docs = [
  new Document({ pageContent: "LangChain is a framework for LLM apps.", metadata: {} }),
  new Document({ pageContent: "RAG = Retrieval Augmented Generation.", metadata: {} }),
];

// 2. Split documents
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 500, chunkOverlap: 50 });
const splits = await splitter.splitDocuments(docs);

// 3. Create embeddings and store
const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
const vectorstore = await MemoryVectorStore.fromDocuments(splits, embeddings);

// 4. Create retriever
const retriever = vectorstore.asRetriever({ k: 4 });

// 5. Use in RAG
const model = new ChatOpenAI({ model: "gpt-4.1" });
const query = "What is RAG?";
const relevantDocs = await retriever.invoke(query);

const context = relevantDocs.map(doc => doc.pageContent).join("\n\n");
const response = await model.invoke([
  { role: "system", content: `Use this context:\n\n${context}` },
  { role: "user", content: query },
]);

ドキュメントローダー

PDF ファイルを読み込み、各ページを個別のドキュメントとして抽出します。
from langchain_community.document_loaders import PyPDFLoader

loader = PyPDFLoader("./document.pdf")
docs = loader.load()
print(f"Loaded {len(docs)} pages")
PDF ファイルを読み込み、各ページを個別のドキュメントとして抽出します。
import { PDFLoader } from "@langchain/community/document_loaders/fs/pdf";

const loader = new PDFLoader("./document.pdf");
const docs = await loader.load();
console.log(`Loaded ${docs.length} pages`);
ウェブ URL からコンテンツを取得して解析し、ドキュメントにします。
from langchain_community.document_loaders import WebBaseLoader

loader = WebBaseLoader("https://docs.langchain.com")
docs = loader.load()
Cheerio を使用してウェブ URL からコンテンツを取得して解析し、ドキュメントにします。
import { CheerioWebBaseLoader } from "@langchain/community/document_loaders/web/cheerio";

const loader = new CheerioWebBaseLoader("https://docs.langchain.com");
const docs = await loader.load();
glob パターンを使用して、ディレクトリ内のすべてのテキストファイルを読み込みます。
from langchain_community.document_loaders import DirectoryLoader, TextLoader

# Load all text files from directory
loader = DirectoryLoader(
    "path/to/documents",
    glob="**/*.txt",  # Pattern for files to load
    loader_cls=TextLoader
)
docs = loader.load()

テキスト分割

サイズとオーバーラップを設定可能な RecursiveCharacterTextSplitter を使用して、ドキュメントをチャンクに分割します。
from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1000,        # Characters per chunk
    chunk_overlap=200,      # Overlap for context continuity
    separators=["\n\n", "\n", " ", ""],  # Split hierarchy
)

splits = splitter.split_documents(docs)

ベクトルストア

永続化する Chroma ベクトルストアを作成し、ディスクから再読み込みします。
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings

vectorstore = Chroma.from_documents(
    documents=splits,
    embedding=OpenAIEmbeddings(),
    persist_directory="./chroma_db",
    collection_name="my-collection",
)

# Load existing
vectorstore = Chroma(
    persist_directory="./chroma_db",
    embedding_function=OpenAIEmbeddings(),
    collection_name="my-collection",
)
稼働中の Chroma サーバーに接続する Chroma ベクトルストアを作成します。
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { OpenAIEmbeddings } from "@langchain/openai";

const vectorstore = await Chroma.fromDocuments(
  splits,
  new OpenAIEmbeddings(),
  { collectionName: "my-collection", url: "http://localhost:8000" }
);
FAISS ベクトルストアを作成してディスクに保存し、再読み込みします。
from langchain_community.vectorstores import FAISS

vectorstore = FAISS.from_documents(splits, embeddings)
vectorstore.save_local("./faiss_index")

# Only load FAISS indexes that you created and fully control.
# The Python FAISS loader uses pickle-backed metadata, so never load
# downloaded, shared, or otherwise untrusted index directories.
loaded = FAISS.load_local(
    "./faiss_index",
    embeddings,
    allow_dangerous_deserialization=True,
)
FAISS ベクトルストアを作成してディスクに保存し、再読み込みします。
import { FaissStore } from "@langchain/community/vectorstores/faiss";

const vectorstore = await FaissStore.fromDocuments(splits, embeddings);
await vectorstore.save("./faiss_index");

const loaded = await FaissStore.load("./faiss_index", embeddings);

検索

類似性検索を実行し、関連度スコアとともに結果を取得します。
# Basic search
results = vectorstore.similarity_search(query, k=5)

# With scores
results_with_score = vectorstore.similarity_search_with_score(query, k=5)
for doc, score in results_with_score:
    print(f"Score: {score}, Content: {doc.page_content}")
類似性検索を実行し、関連度スコアとともに結果を取得します。
// Basic search
const results = await vectorstore.similaritySearch(query, 5);

// With scores
const resultsWithScore = await vectorstore.similaritySearchWithScore(query, 5);
for (const [doc, score] of resultsWithScore) {
  console.log(`Score: ${score}, Content: ${doc.pageContent}`);
}
MMR(最大限界関連性)を使用して、検索結果の関連性と多様性のバランスを取ります。
# MMR balances relevance and diversity
retriever = vectorstore.as_retriever(
    search_type="mmr",
    search_kwargs={"fetch_k": 20, "lambda_mult": 0.5, "k": 5},
)
ドキュメントにメタデータを追加し、メタデータのプロパティで検索結果をフィルタリングします。
# Add metadata when creating documents
docs = [
    Document(
        page_content="Python programming guide",
        metadata={"language": "python", "topic": "programming"}
    ),
]

# Search with filter
results = vectorstore.similarity_search(
    "programming",
    k=5,
    filter={"language": "python"}  # Only Python docs
)
質問に回答するためのツールとして RAG を使用するエージェントを作成します。
from langchain.agents import create_agent
from langchain.tools import tool

@tool
def search_docs(query: str) -> str:
    """Search documentation for relevant information."""
    docs = retriever.invoke(query)
    return "\n\n".join([d.page_content for d in docs])

agent = create_agent(
    model="gpt-4.1",
    tools=[search_docs],
)

result = agent.invoke({
    "messages": [{"role": "user", "content": "How do I create an agent?"}]
})
質問に回答するためのツールとして RAG を使用するエージェントを作成します。
import { createAgent } from "langchain";
import { tool } from "@langchain/core/tools";
import { z } from "zod";

const searchDocs = tool(
  async (input) => {
    const docs = await retriever.invoke(input.query);
    return docs.map(d => d.pageContent).join("\n\n");
  },
  {
    name: "search_docs",
    description: "Search documentation for relevant information.",
    schema: z.object({ query: z.string() }),
  }
);

const agent = createAgent({
  model: "gpt-4.1",
  tools: [searchDocs],
});

const result = await agent.invoke({
  messages: [{ role: "user", content: "How do I create an agent?" }],
});
### 設定できる項目
  • チャンクサイズ/オーバーラップ
  • 埋め込みモデル
  • 結果数(k)
  • メタデータフィルター
  • 検索アルゴリズム:類似性、MMR
設定できない項目
  • モデルごとの埋め込み次元数
  • 同じストア内で異なるモデルの埋め込みを混在させること
チャンクサイズは通常、500~1500が適切です。
# WRONG: Too small (loses context) or too large (hits limits)
splitter = RecursiveCharacterTextSplitter(chunk_size=50)
splitter = RecursiveCharacterTextSplitter(chunk_size=10000)

# CORRECT
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
チャンクサイズは通常、500~1500が適切です。
// WRONG: Too small or too large
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 50 });

// CORRECT
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000, chunkOverlap: 200 });
境界でコンテキストを維持するため、オーバーラップ(チャンクサイズの10~20%)を使用してください。
# WRONG: No overlap - context breaks at boundaries
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=0)

# CORRECT: 10-20% overlap
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
データ損失を防ぐため、インメモリではなく永続的なベクトルストアを使用してください。
# WRONG: InMemory - lost on restart
vectorstore = InMemoryVectorStore.from_documents(docs, embeddings)

# CORRECT
vectorstore = Chroma.from_documents(docs, embeddings, persist_directory="./chroma_db")
データ損失を防ぐため、インメモリではなく永続的なベクトルストアを使用してください。
// WRONG: Memory - lost on restart
const vectorstore = await MemoryVectorStore.fromDocuments(docs, embeddings);

// CORRECT
const vectorstore = await Chroma.fromDocuments(docs, embeddings, { collectionName: "my-collection" });
インデックス作成とクエリには、同じ埋め込みモデルを使用してください。
# WRONG: Different embeddings for index and query - incompatible!
vectorstore = Chroma.from_documents(docs, OpenAIEmbeddings(model="text-embedding-3-small"))
retriever = vectorstore.as_retriever(embeddings=OpenAIEmbeddings(model="text-embedding-3-large"))

# CORRECT: Same model
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever()  # Uses same embeddings
インデックス作成とクエリには、同じ埋め込みモデルを使用してください。
const embeddings = new OpenAIEmbeddings({ model: "text-embedding-3-small" });
const vectorstore = await Chroma.fromDocuments(docs, embeddings);
const retriever = vectorstore.asRetriever();  // Uses same embeddings
信頼できるローカルインデックスに限り、FAISS のデシリアライズを有効にしてください。Python の FAISS インデックスには pickle ベースのメタデータが含まれており、信頼できない pickle ファイルを読み込むと、任意のコードが実行される可能性があります。
# WRONG: Loading a downloaded, shared, cloud-hosted, or third-party-controlled
# FAISS index with dangerous deserialization enabled.
loaded_store = FAISS.load_local(
    "./untrusted_faiss_index",
    embeddings,
    allow_dangerous_deserialization=True,
)

# CORRECT: Only opt in when the index directory was created by you and has
# remained under your control.
loaded_store = FAISS.load_local(
    "./faiss_index",
    embeddings,
    allow_dangerous_deserialization=True,
)

永続化されたインデックスの出所を保証できない場合は、allow_dangerous_deserialization=True を指定して読み込まないでください。信頼できるソースドキュメントからインデックスを再構築するか、信頼できないファイルの pickle デシリアライズを必要としないベクトルストア/バックエンドを使用してください。

埋め込みの次元数がベクトルストアのインデックスの次元数と一致していることを確認してください。
# WRONG: Index has 1536 dimensions but using 512-dim embeddings
pc.create_index(name="idx", dimension=1536, metric="cosine")
vectorstore = PineconeVectorStore.from_documents(
    docs, OpenAIEmbeddings(model="text-embedding-3-small", dimensions=512), index=pc.Index("idx")
)  # Error: dimension mismatch!

# CORRECT: Match dimensions
embeddings = OpenAIEmbeddings()  # Default 1536

コメント