

This article is currently available in Japanese.
We're working on English translations. In the meantime, you can read it via Google Translate, or use your browser's built-in translation feature.
RAGの実装パターンには明確な使い分けがあり、選択を間違えると本番で破綻する
これが、私たちが複数の受託プロジェクトでRAGシステムを構築してきた結論だ。
RAGは2024年のブームから2年が経ち、「とりあえず動くPoC」の時代から「運用環境で精度と信頼性を保つ」フェーズに移行している。LangChain自体も大きく変わった。2024年後半にLegacy Chains(RetrievalQA等)が非推奨となり、LCEL(LangChain Expression Language)とcreate_retrieval_chainベースのパイプラインが標準になった。2025年にはLangGraphが安定版となり、エージェント型RAGのパターンが確立された。
本記事では、LangChainの現行アーキテクチャに基づく3つの実装パターンを比較し、ユースケース別の選定基準とハマりどころを共有する。
なぜLangChainか — LlamaIndexを選ばなかった理由
RAGフレームワークの選定で、私たちはLlamaIndexも検証した。LlamaIndexはRAGに特化しており、インデックス構築やクエリエンジンの抽象度が高い。ドキュメントQAに限定するならLlamaIndexの方が立ち上がりは速い。
しかし、私たちの受託案件では「RAG+既存業務ロジック+外部API連携」が複合的に求められる。LangChainはRAG以外のチェーン構築(ツール呼び出し、マルチステップ推論、ワークフロー制御)との統合が容易で、LangGraphとの連続性がある。RAG単機能の最適さより、プロジェクト全体の拡張性を優先した。
3つの実装パターン
パターン1: Basic RAG(LCEL標準型)
最もシンプルな構成。ドキュメントをチャンクに分割し、ベクトル化して格納。クエリに対して類似度検索でチャンクを取得し、LLMに渡す。2026年現在、create_retrieval_chainがLangChain公式の推奨パターンだ。
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import TiDBVectorStore
from langchain.chains.retrieval import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = TiDBVectorStore(
embedding_function=embeddings,
connection_string=TIDB_CONNECTION,
table_name="documents",
distance_strategy="cosine",
)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
prompt = ChatPromptTemplate.from_messages([
("system", "以下の文脈に基づいて質問に回答せよ。文脈にない情報は「分かりません」と答えよ。\n\n{context}"),
("human", "{input}"),
])
combine_docs_chain = create_stuff_documents_chain(llm, prompt)
chain = create_retrieval_chain(
vectorstore.as_retriever(search_kwargs={"k": 5}),
combine_docs_chain,
)
response = chain.invoke({"input": "直近のチャーンレートの傾向は?"})適するケース: FAQ対応、社内Wiki検索など質問と回答が1対1で対応する領域。ドキュメント数千件程度。PoC・初期プロトタイプ。
適さないケース: 複数ドキュメントの横断推論、曖昧・複合クエリ、回答精度へのビジネス要求が高い場合。
パターン2: Advanced RAG(検索強化型)
Basic RAGの検索精度を改善するパターン。ハイブリッド検索(ベクトル+BM25キーワード検索)にクロスエンコーダーのリランキングを組み合わせる。
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain_community.cross_encoders import HuggingFaceCrossEncoder
from langchain.retrievers.document_compressors import CrossEncoderReranker
from langchain.retrievers import ContextualCompressionRetriever
bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 10
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6],
)
model = HuggingFaceCrossEncoder(model_name="cross-encoder/ms-marco-MiniLM-L-6-v2")
reranker = CrossEncoderReranker(model=model, top_n=5)
compression_retriever = ContextualCompressionRetriever(
base_retriever=ensemble_retriever,
base_compressor=reranker,
)
chain = create_retrieval_chain(compression_retriever, combine_docs_chain)適するケース: 稼働環境で検索精度が求められるシステム。専門用語が多くベクトル検索だけでは取りこぼしが発生する領域。ドキュメント数万件規模。
適さないケース: リランキングのレイテンシー(+200-500ms)が許容できないリアルタイム用途。インフラコストを最小限に抑えたい場合。
パターン3: Agentic RAG(LangGraph型)
LLMがクエリの意図を分析し、検索戦略を自律的に決定するパターン。LangGraphのステートマシンとして実装する。LangGraphが2025年に安定版になったことで、このパターンが実用域に入った。
from langgraph.graph import StateGraph, END
from typing import TypedDict
class RAGState(TypedDict):
query: str
search_strategy: str
retrieved_docs: list
answer: str
needs_refinement: bool
def analyze_query(state: RAGState) -> RAGState:
# LLMにクエリ分類を依頼(省略: gpt-4o-miniでquery→strategy分類)
state["search_strategy"] = classify_query(state["query"])
return state
def retrieve(state: RAGState) -> RAGState:
strategy = state["search_strategy"]
if strategy == "multi_query":
docs = multi_query_retrieve(state["query"], vectorstore)
elif strategy == "decompose":
docs = decomposed_retrieve(state["query"], vectorstore)
else:
docs = vectorstore.similarity_search(state["query"], k=5)
state["retrieved_docs"] = docs
return state
def evaluate_and_answer(state: RAGState) -> RAGState:
# 省略: LLMで検索結果の網羅性を判定
state["answer"] = generate_answer(state)
state["needs_refinement"] = check_sufficiency(state)
return state
graph = StateGraph(RAGState)
graph.add_node("analyze", analyze_query)
graph.add_node("retrieve", retrieve)
graph.add_node("answer", evaluate_and_answer)
graph.set_entry_point("analyze")
graph.add_edge("analyze", "retrieve")
graph.add_edge("retrieve", "answer")
graph.add_conditional_edges(
"answer",
lambda s: "retrieve" if s["needs_refinement"] else END,
)
app = graph.compile()適するケース: 複合的な分析質問。複数データソースの横断検索。検索→評価→再検索の反復が必要な調査型タスク。
適さないケース: シンプルなFAQ(オーバーエンジニアリング)。レイテンシー制約が厳しい場合(LLM呼び出しが3-5回に増える)。LLMコストの管理が厳しい場合。
3パターンの定量比較
私たちがドキュメント数千〜数万件規模の案件で計測した実績値に基づく目安:
指標 | Basic RAG | Advanced RAG | Agentic RAG |
|---|---|---|---|
レイテンシー(P95) | 1-2秒 | 2-4秒 | 5-15秒 |
LLM呼び出し回数/クエリ | 1回 | 1回 | 3-5回 |
Retrieval Precision | 60-70% | 80-90% | 85-95% |
月間コスト目安(1,000クエリ/日) | $50-100 | $80-150 | $200-500 |
実装工数 | 1-2日 | 3-5日 | 1-2週間 |
※Retrieval PrecisionはRAGAS準拠の評価指標で計測。コストはGPT-4o-mini + text-embedding-3-small、2026年4月時点の価格ベース。
選定フローチャート
クエリの複雑さに応じて選択する:
- 単純(FAQ、1対1対応) → Basic RAG
- 中程度(専門用語多い、精度要求あり) → Advanced RAG
- 複合的(横断検索、分析型、反復推論) → Agentic RAG
ただし、私たちのプロジェクトでは最初は必ずBasic RAGから始める。理由は3つ。
- ベースラインの確立。 Basic RAGの精度を測定しないと、Advanced/Agenticの改善幅を定量評価できない
- チャンキングとエンベディングの品質が全てに効く。 検索の土台が悪ければ、上のレイヤーで何をやっても改善幅は限定的だ
- コストとレイテンシーの見積もり。 最もシンプルな構成での数値がないと、稼働環境のインフラ設計ができない
ハマりどころ — 私たちの経験から
1. チャンキング戦略の過小評価
私たちの経験上、RAGの精度に最も影響が大きかったのは、LLMの性能でも検索アルゴリズムでもなく、チャンキングの品質だった。
あるプロジェクトで、一律500トークンで分割した結果、表の途中で切れる、手順の3ステップ目が別チャンクに飛ぶ、前提条件と結論が別チャンクになる問題が頻発した。セマンティックチャンキングに切り替え、メタデータ(ドキュメントタイトル、セクション見出し、作成日)をチャンクに埋め込んだところ、Retrieval Precisionが58%から82%に改善した。
2. 評価パイプラインの不在
「RAGを作った、動いた、デモで見せた、良さそうだ」で終わるプロジェクトが多い。しかし稼働環境で最も重要なのは定量的な評価パイプラインだ。
私たちはRAGAS(Retrieval Augmented Generation Assessment)をベースに、以下の3指標で評価している:
- Retrieval Precision: 取得したチャンクのうち、回答に実際に使われた割合
- Faithfulness: 生成された回答が、取得したチャンクの内容に忠実か
- Answer Relevancy: 生成された回答が、元のクエリに対して的確か
これをCI/CDに組み込み、モデル変更やチャンキング変更のたびに自動実行する。評価パイプラインなしにRAGを稼働環境で運用するのは、テストなしにコードをデプロイするのと変わらない。
3. ベクトルDB選定の罠
私たちはTiDB Serverless(MySQL互換 + ベクトル検索)を採用している。選定理由は、既存のRDBワークロードとベクトル検索を同一インフラで扱えること、そしてサーバーレスのスケーリング特性だ。
選ばなかったものと、その理由:
- Pinecone: マネージドで立ち上がりは速いが、データがベクトルDBに閉じる。RDBとのJOINが必要な業務要件では追加のデータ同期が発生する
- pgvector: PostgreSQLエコシステムとの親和性は高いが、大規模ベクトルインデックスでの運用チューニングにコストがかかる。ただし2025年以降の改善(HNSW indexの高速化)で選択肢としての魅力は上がっている
- Chroma: プロトタイプには最適だが、稼働環境のスケーラビリティと永続性に懸念が残る
ベクトルDB単体の性能で選ぶと、「あのデータと結合したいのにできない」問題に直面する。RDBワークロードとの共存を前提に選定すべきだ。
まだ解決できていないこと
チャンクの鮮度管理。 ドキュメントが更新されたとき、対応するチャンクのベクトルを効率的に更新する仕組みは、まだ力技に頼っている。差分更新の自動化は今後の課題だ。
マルチモーダル対応。 表や図を含むドキュメントのRAGは、テキストのみの場合と比べて精度が大きく落ちる。画像をテキストに変換してからチャンク化するアプローチを試しているが、情報の欠落が避けられない。
評価の自動化精度。 RAGAS自体がLLMに依存しているため、評価の精度にもばらつきがある。人手での抜き取り検証を定期的に行っているが、スケールしない。
まとめ
RAGの実装パターンは、ユースケースの複雑さに応じてBasic → Advanced → Agenticと段階的に選択するのが最も合理的だ。最初からAgenticに飛びつくのではなく、Basic RAGでベースラインを確立してから、必要に応じて検索の強化やエージェント化を検討する。
パターン選定よりも重要なのは、チャンキングの設計と評価パイプラインの整備だ。この2つが欠けたRAGは、デモでは動くが運用環境では壊れる。
RELATED ARTICLES
Related Articles
CONTACT
We support AI and system adoption that drives business growth.
From clarifying "what to build" to design, development, and operations. Practical support grounded in your business, operations, and data.


