LangChain vs LangGraph:完全比較ガイド
2つの人気のあるLLMアプリケーションフレームワークの包括的な比較で、開発者がプロジェクトについて情報に基づいた意思決定を行えるよう支援します。
概要
LangChainとは?
LangChainは、大規模言語モデル(LLM)を活用したアプリケーションの開発を簡素化するために設計されたオープンソースフレームワークです。 チェーンの標準化されたインターフェース、様々なツールとの統合、一般的なユースケースのためのエンドツーエンドのチェーンを提供します。
LangGraphとは?
LangGraphはLangChainの上に構築されたライブラリで、ステートフルなマルチアクターLLMアプリケーションの作成を可能にします。 グラフベースのワークフローでLangChainを拡張し、複雑な循環アプリケーションの構築を容易にします。
なぜ比較するのか?
両方のフレームワークはLLMアプリケーション開発のために設計されていますが、異なるニーズを満たし、異なるシナリオで優れています。 それらの違いを理解することで、特定のユースケースに適したツールを選択するのに役立ちます。
コア概念と設計哲学
LangChain:チェーンベースの思考
LangChainは、操作が順番に実行される線形のチェーンベースのアプローチに従っています。 複雑なワークフローを作成するために一緒にリンクできる操作のシーケンスである「チェーン」の概念を中心に構築されています。
コア概念:
- Chains(チェーン):一緒にリンクされた順次操作
- Prompts(プロンプト):テンプレートベースの入力フォーマット
- LLMs:言語モデル統合
- Memory(メモリ):呼び出し間の状態永続化
- Agents(エージェント):動的な意思決定コンポーネント
LangGraph:グラフベースの状態思考
LangGraphは、ノードがアクションを表し、エッジがそれらの間のフローを定義するグラフベースのアプローチを導入しています。 これにより、組み込みの状態管理でより複雑な循環ワークフローが可能になります。
コア概念:
- Nodes(ノード):個々のアクションまたは関数
- Edges(エッジ):ノード間の接続
- State(状態):グラフ全体の共有データ
- Conditional Edges(条件付きエッジ):条件に基づく動的ルーティング
- Persistence(永続性):組み込み状態チェックポイント
比較テーブル
| 機能 | LangChain | LangGraph |
|---|---|---|
| 学習曲線 | 初心者にやさしい | 急だが一貫性あり |
| 状態管理 | メモリクラス | 組み込み状態スキーマ |
| ワークフロー複雑度 | 線形チェーン向き | 複雑なグラフに最適 |
| サイクルとループ | 限定的サポート | 完全サポート |
| 統合 | 100+ 統合 | LangChain統合 |
| デバッグ | コールバック + LangSmith | 状態スナップショット + LangSmith |
| 本番デプロイ | LangServe | LangServe互換 |
| マルチエージェント | 可能だが複雑 | ネイティブサポート |
| ドキュメント | 充実 | 拡大中 |
学習曲線
LangChain学習パス
LangChainは初心者にとって比較的緩やかな学習曲線を持っています。チェーンベースの概念は直感的で、 ドキュメントは多くの例を提供しています。ただし、アプリケーションがより複雑になると、 状態とフローの管理が困難になる可能性があります。
初級:2-3日 - 基本的なチェーンとプロンプト
中級:1-2週間 - エージェント、ツール、メモリ
上級:2-4週間 - カスタムチェーンと複雑な統合
LangGraph学習パス
LangGraphはグラフベースのパラダイムのため、初期の学習曲線が急です。ただし、一度理解すると、 複雑なアプリケーションにより一貫したメンタルモデルを提供します。
初級:3-5日 - 基本的なグラフと状態
中級:1-2週間 - 条件付きルーティングと永続性
上級:2-3週間 - 複雑なマルチエージェントシステム
コード比較
Level 1:シンプルなQ&Aチェーン
最もシンプルなユースケースから始めましょう:基本的な質問応答チェーンです。
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# プロンプトテンプレートを定義
prompt = ChatPromptTemplate.from_template(
"以下の質問に答えてください:{question}"
)
# モデルを作成
model = ChatOpenAI(model="gpt-4")
# チェーンを構築
chain = prompt | model | StrOutputParser()
# チェーンを呼び出し
result = chain.invoke({"question": "LangChainとは何ですか?"})
print(result) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict
# 状態を定義
class State(TypedDict):
question: str
answer: str
# ノードを定義
def generate_answer(state: State) -> State:
model = ChatOpenAI(model="gpt-4")
response = model.invoke(state["question"])
return {"answer": response.content}
# グラフを構築
graph = StateGraph(State)
graph.add_node("generate", generate_answer)
graph.set_entry_point("generate")
graph.add_edge("generate", END)
# コンパイルして実行
app = graph.compile()
result = app.invoke({"question": "LangGraphとは何ですか?"})
print(result["answer"]) Level 2:マルチターン会話
複数のターンにわたってコンテキストを維持するために会話履歴を追加します。
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessage, AIMessage
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
# モデルとメモリを作成
model = ChatOpenAI(model="gpt-4")
memory = ConversationBufferMemory(return_messages=True)
# 会話チェーンを作成
chain = ConversationChain(llm=model, memory=memory)
# マルチターン会話
response1 = chain.predict(input="こんにちは、私は田中です")
response2 = chain.predict(input="私の名前は何ですか?")
print(response2) # "田中"を覚えている from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage
from typing import TypedDict, List
class State(TypedDict):
messages: List
def chat(state: State) -> State:
model = ChatOpenAI(model="gpt-4")
response = model.invoke(state["messages"])
return {"messages": state["messages"] + [response]}
# グラフを構築
graph = StateGraph(State)
graph.add_node("chat", chat)
graph.set_entry_point("chat")
graph.add_edge("chat", END)
app = graph.compile()
# 状態を持つマルチターン会話
result1 = app.invoke({
"messages": [HumanMessage("こんにちは、私は田中です")]
})
result2 = app.invoke({
"messages": result1["messages"] + [HumanMessage("私の名前は何ですか?")]
}) Level 3:RAG(検索拡張生成)
ドキュメント検索と生成を含むRAGパイプラインを実装します。
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_community.document_loaders import TextLoader
# ドキュメントをロードして分割
loader = TextLoader("docs.txt")
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000)
texts = text_splitter.split_documents(documents)
# ベクターストアを作成
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(texts, embeddings)
# RAGチェーンを作成
llm = ChatOpenAI(model="gpt-4")
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectorstore.as_retriever()
)
# クエリ
result = qa_chain.invoke({"query": "ドキュメントは何についてですか?"}) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from typing import TypedDict, List
class RAGState(TypedDict):
query: str
documents: List[str]
answer: str
def retrieve(state: RAGState) -> RAGState:
embeddings = OpenAIEmbeddings()
vectorstore = Chroma(persist_directory="./chroma", embedding_function=embeddings)
docs = vectorstore.similarity_search(state["query"], k=3)
return {"documents": [d.page_content for d in docs]}
def generate(state: RAGState) -> RAGState:
llm = ChatOpenAI(model="gpt-4")
context = "\n\n".join(state["documents"])
prompt = f"コンテキスト: {context}\n\n質問: {state['query']}"
response = llm.invoke(prompt)
return {"answer": response.content}
# グラフを構築
graph = StateGraph(RAGState)
graph.add_node("retrieve", retrieve)
graph.add_node("generate", generate)
graph.set_entry_point("retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", END)
app = graph.compile()
result = app.invoke({"query": "ドキュメントは何についてですか?"}) Level 4:ツール呼び出しと関数バインディング
LLMが外部ツールと関数を使用できるようにします。
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
# カスタムツールを定義
@tool
def get_weather(city: str) -> str:
"""都市の天気を取得"""
return f"{city}の天気:晴れ、22°C"
@tool
def calculate(expression: str) -> str:
"""数式を計算"""
return str(eval(expression))
# ツールリストを作成
tools = [get_weather, calculate]
# エージェントを作成
llm = ChatOpenAI(model="gpt-4")
prompt = ChatPromptTemplate.from_messages([
("system", "あなたは役立つアシスタントです。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])
agent = create_tool_calling_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools)
# 実行
result = agent_executor.invoke({"input": "東京の天気はどうですか?"}) from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from typing import TypedDict, List, Any
# ツールを定義
@tool
def get_weather(city: str) -> str:
"""都市の天気を取得"""
return f"{city}の天気:晴れ、22°C"
@tool
def calculate(expression: str) -> str:
"""数式を計算"""
return str(eval(expression))
tools = [get_weather, calculate]
# 状態を定義
class AgentState(TypedDict):
messages: List[Any]
# ツールがバインドされたモデルを作成
llm = ChatOpenAI(model="gpt-4")
llm_with_tools = llm.bind_tools(tools)
# エージェントノードを定義
def agent(state: AgentState) -> AgentState:
response = llm_with_tools.invoke(state["messages"])
return {"messages": state["messages"] + [response]}
# グラフを構築
graph = StateGraph(AgentState)
graph.add_node("agent", agent)
graph.add_node("tools", ToolNode(tools))
graph.set_entry_point("agent")
# 条件付きルーティング
def should_continue(state):
if state["messages"][-1].tool_calls:
return "tools"
return END
graph.add_conditional_edges("agent", should_continue)
graph.add_edge("tools", "agent")
app = graph.compile()
result = app.invoke({"messages": [{"role": "user", "content": "東京の天気はどうですか?"}]}) Level 5:マルチエージェント協調
複数のエージェントが協力して複雑な問題を解決するシステムを構築します。
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_functions_agent
from langchain.tools import Tool
from langchain_core.prompts import ChatPromptTemplate
# 専門エージェントを作成
def create_agent(role: str, expertise: str):
llm = ChatOpenAI(model="gpt-4")
prompt = ChatPromptTemplate.from_messages([
("system", f"あなたは{role}です。{expertise}"),
("human", "{input}"),
])
return prompt | llm
# エージェントを作成
researcher = create_agent("リサーチャー", "情報を収集します。")
analyst = create_agent("アナリスト", "データを分析します。")
writer = create_agent("ライター", "レポートを作成します。")
# 順次実行(簡略化)
topic = "2024年のAIトレンド"
research = researcher.invoke({"input": f"{topic}を調査"})
analysis = analyst.invoke({"input": f"分析: {research.content}"})
report = writer.invoke({"input": f"分析に基づいてレポートを作成: {analysis.content}"}) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, List, Optional
class MultiAgentState(TypedDict):
topic: str
research: Optional[str]
analysis: Optional[str]
report: Optional[str]
feedback: Optional[str]
llm = ChatOpenAI(model="gpt-4")
def researcher_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"このトピックを徹底的に調査してください: {state['topic']}"
response = llm.invoke(prompt)
return {"research": response.content}
def analyst_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"この調査を分析してください: {state['research']}"
response = llm.invoke(prompt)
return {"analysis": response.content}
def writer_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"この分析に基づいてレポートを作成してください: {state['analysis']}"
response = llm.invoke(prompt)
return {"report": response.content}
def reviewer_node(state: MultiAgentState) -> MultiAgentState:
prompt = f"このレポートをレビューしてフィードバックを提供してください: {state['report']}"
response = llm.invoke(prompt)
return {"feedback": response.content}
# 条件:修正が必要かどうか
def should_revise(state):
if "修正" in state["feedback"]:
return "writer"
return END
# グラフを構築
graph = StateGraph(MultiAgentState)
graph.add_node("researcher", researcher_node)
graph.add_node("analyst", analyst_node)
graph.add_node("writer", writer_node)
graph.add_node("reviewer", reviewer_node)
graph.set_entry_point("researcher")
graph.add_edge("researcher", "analyst")
graph.add_edge("analyst", "writer")
graph.add_edge("writer", "reviewer")
graph.add_conditional_edges("reviewer", should_revise)
app = graph.compile()
result = app.invoke({"topic": "2024年のAIトレンド"}) Level 6:ストリーミング出力
より良いユーザー体験のためにリアルタイムストリーミングを実装します。
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
# ストリーミング可能なモデルを作成
model = ChatOpenAI(model="gpt-4", streaming=True)
prompt = ChatPromptTemplate.from_template("{topic}について物語を話して")
chain = prompt | model
# ストリーミング出力
for chunk in chain.stream({"topic": "勇敢な騎士"}):
print(chunk.content, end="", flush=True) from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict
class StreamState(TypedDict):
topic: str
story: str
async def generate_story(state: StreamState) -> StreamState:
model = ChatOpenAI(model="gpt-4")
prompt = f"{state['topic']}について物語を話して"
story_parts = []
async for chunk in model.astream(prompt):
story_parts.append(chunk.content)
print(chunk.content, end="", flush=True)
return {"story": "".join(story_parts)}
# グラフを構築
graph = StateGraph(StreamState)
graph.add_node("generate", generate_story)
graph.set_entry_point("generate")
graph.add_edge("generate", END)
app = graph.compile()
# ストリーミング実行
import asyncio
asyncio.run(app.ainvoke({"topic": "勇敢な騎士"})) Level 7:エラー処理と再試行ロジック
自動再試行による堅牢なエラー処理を実装します。
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from tenacity import retry, stop_after_attempt, wait_exponential
# 再試行デコレータでラップ
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=2, max=10))
def call_llm_with_retry(prompt: str) -> str:
model = ChatOpenAI(model="gpt-4")
try:
response = model.invoke(prompt)
return response.content
except Exception as e:
print(f"エラー: {e}, 再試行中...")
raise
# 使用
try:
result = call_llm_with_retry("LangChainとは何ですか?")
except Exception as e:
print(f"再試行後に失敗: {e}") from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from typing import TypedDict, Optional
class RetryState(TypedDict):
prompt: str
response: Optional[str]
error: Optional[str]
attempts: int
MAX_RETRIES = 3
def call_llm(state: RetryState) -> RetryState:
model = ChatOpenAI(model="gpt-4")
try:
response = model.invoke(state["prompt"])
return {"response": response.content, "error": None}
except Exception as e:
return {"error": str(e), "attempts": state.get("attempts", 0) + 1}
def handle_error(state: RetryState) -> RetryState:
print(f"{state['attempts']}回目の試行が失敗: {state['error']}")
return state
def should_retry(state: RetryState):
if state.get("response"):
return END
if state.get("attempts", 0) >= MAX_RETRIES:
return END
return "call_llm"
# グラフを構築
graph = StateGraph(RetryState)
graph.add_node("call_llm", call_llm)
graph.add_node("handle_error", handle_error)
graph.set_entry_point("call_llm")
graph.add_conditional_edges("call_llm", should_retry)
graph.add_edge("handle_error", "call_llm")
app = graph.compile()
result = app.invoke({"prompt": "LangGraphとは何ですか?", "attempts": 0}) アーキテクチャ深掘り
LangChainアーキテクチャ
LangChainは3つの主要なレイヤーを持つ階層型アーキテクチャに従っています:
- 統合レイヤー:外部サービスとの接続(LLM、ベクターストア、ツール)
- コアレイヤー:基本的な抽象化(プロンプト、チェーン、メモリ)
- アプリケーションレイヤー:一般的なユースケースのための事前構築ソリューション
チェーンベースの設計は構成を促進します - より小さなチェーンをより大きなチェーンに組み合わせることができます。 ただし、これによりデバッグと保守が困難な深くネストされた構造が生じる可能性があります。
LangGraphアーキテクチャ
LangGraphは以下のコンポーネントを持つグラフベースのアーキテクチャを導入しています:
- 状態スキーマ:共有状態構造を定義するTypedDict
- ノード:状態を受け取り更新する関数
- エッジ:ノード間のフローを定義(静的または条件付き)
- チェックポインター:状態スナップショットのための永続性レイヤー
グラフ構造はフロー制御についてより明示的であり、複雑なアプリケーションをより理解しやすくデバッグしやすくします。 状態管理はアドオン機能ではなく、フレームワークに組み込まれています。
パフォーマンスベンチマーク
コミュニティベンチマークと内部テストに基づく、一般的なパフォーマンス特性:
| 指標 | LangChain | LangGraph |
|---|---|---|
| シンプルチェーンオーバーヘッド | ~5ms | ~15ms |
| 複雑なワークフロー(5+ステップ) | ~50ms オーバーヘッド | ~30ms オーバーヘッド |
| 状態シリアライゼーション | 手動 | 自動(~10ms) |
| メモリ使用量 | シンプルなケースで低い | ベースラインがわずかに高い |
| ストリーミングレイテンシ | ~50ms 最初のトークン | ~55ms 最初のトークン |
注:これらは近似値であり、特定のユースケース、LLMプロバイダー、インフラストラクチャによって異なります。
ユースケース分析
LangChainに最適なユースケース
1. シンプルなチャットボット
基本的な会話履歴を持つシンプルなチャットボットを構築する場合、 LangChainの会話チェーンは迅速に実装でき、ほとんどのニーズを満たします。
2. ドキュメントQ&Aシステム
ドキュメントを一度ロードして繰り返しクエリするRAGアプリケーションの場合、 LangChainの検索チェーンはうまく機能し、セットアップが簡単です。
3. プロトタイピングと実験
アイデアを素早くテストしたりMVPを構築したりする必要がある場合、LangChainの豊富な テンプレートと例により迅速な開発が可能です。
LangGraphに最適なユースケース
1. マルチエージェントシステム
複数のエージェントが協力し、情報を渡し、お互いの作業を反復する必要がある場合、 LangGraphのグラフ構造が必要な制御フローを提供します。
2. 分岐のある複雑なワークフロー
中間結果に基づいて動的ルーティングが必要なアプリケーションは、 LangGraphの条件付きエッジと明示的な状態管理の恩恵を受けます。
3. 長時間実行プロセス
数時間または数日にわたるワークフローには永続性と再開機能が必要であり、 LangGraphはチェックポインターシステムを通じてこれを提供します。
4. 人間参加型システム
特定のポイントで人間の承認や介入が必要な場合、LangGraphの インタラプトと再開機能により実装が簡単になります。
マイグレーションガイド
LangChainからLangGraphへのマイグレーション
LangChainからLangGraphへの移行を検討している場合、一般的なアプローチは以下の通りです:
- 状態を特定:ステップ間で永続化する必要があるデータを定義
- チェーンをノードにマップ:各チェーンがグラフのノードになります
- エッジを定義:ワークフローロジックに基づいてノードを接続
- メモリを処理:メモリクラスを状態スキーマに置き換え
- 段階的にテスト:一度に1つのコンポーネントずつマイグレーション
両方を一緒に使用する
LangGraphはLangChainの上に構築されているため、LangGraphノード内で LangChainコンポーネント(LLM、ツール、リトリーバー)を引き続き使用できます。 これにより段階的な移行と両方の長所を生かしたソリューションが可能になります。
柔軟性と拡張性
LangChainの柔軟性
- 広範な統合エコシステム(100+ 統合)
- コンポーネントの交換が容易(LLM、ベクターストアなど)
- チェーンの構成で複雑なワークフローを構築可能
- 基本クラスの拡張でカスタムチェーンを作成可能
LangGraphの柔軟性
- グラフ構造で任意のワークフロートポロジーが可能
- 条件付きエッジで動的ルーティングをサポート
- 組み込みの永続性と状態管理
- サイクルとループのより良いサポート
状態管理とメモリ
LangChainの状態管理
LangChainはメモリクラスを使用して呼び出し間の状態を永続化します。 さまざまなメモリタイプ(ConversationBufferMemory、ConversationSummaryMemoryなど)がありますが、 設定が複雑で、すべてのシナリオをうまく処理できない場合があります。
LangGraphの状態管理
LangGraphはTypedDictベースの状態スキーマを通じて組み込みの状態管理を提供します。 状態はノード間で渡され、各ステップで変更できます。 チェックポインターシステムにより、ワークフローの永続化と再開が可能です。
エラー処理とデバッグ
LangChainのエラー処理
- チェーン呼び出し周辺のtry-catchブロック
- ロギングとモニタリングのためのコールバック
- トレーシングのためのLangSmith統合
LangGraphのエラー処理
- 失敗を処理するエラーノード
- グラフ実行に組み込まれた再試行ロジック
- 実行フローのより良い可視性
- デバッグのための状態スナップショット
パフォーマンスと効率性
両方のフレームワークは基本的な操作で同様のパフォーマンス特性を持っています。主な違い:
- LangChain:シンプルなチェーンではオーバーヘッドが低い; 状態管理が複雑になる可能性がある
- LangGraph:初期オーバーヘッドがわずかに高い; 複雑でステートフルなワークフローでより良いパフォーマンス
コミュニティとドキュメント
LangChainエコシステム
- より大きなコミュニティとより多くのリソース
- 豊富なドキュメントと例
- より多くのサードパーティ統合
- 観測可能性のためのLangSmith
LangGraphエコシステム
- 成長中のコミュニティ
- よく文書化されたコア概念
- LangChainエコシステムとのより緊密な統合
- トレーシングのためのLangSmith統合
プロダクション対応
プロダクションでのLangChain
- 多くのプロダクションアプリケーションで使用されている
- デプロイのためのLangServe
- よりシンプルで線形なワークフローに適している
- カスタム状態管理が必要になる場合がある
プロダクションでのLangGraph
- 複雑でステートフルなアプリケーションのために構築されている
- 組み込みの永続性と再開
- マルチエージェントシステムに適している
- 明示的なエラー処理と再試行ロジック
結論と推奨事項
LangChainを選ぶべき時
- シンプルで線形なワークフロー
- 迅速なプロトタイピングと実験
- 広範なサードパーティ統合が必要
- チームがLLM開発に不慣れ
- 標準的なユースケース(チャットボット、Q&A、RAG)
LangGraphを選ぶべき時
- 複雑でマルチステップなワークフロー
- サイクルやループが必要なアプリケーション
- マルチエージェントシステム
- 明示的な状態管理が必要
- 長時間実行、再開可能なワークフロー
ハイブリッドアプローチ
LangGraphはLangChainの上に構築されているため、両方を一緒に使用できます。 多くのアプリケーションは基本操作(LLM呼び出し、リトリーバー)にLangChainを使用しながら、 オーケストレーションと状態管理にLangGraphを使用しています。
将来のトレンド
両方のフレームワークは活発に開発され進化しています。LangGraphはLangChainエコシステム内で 複雑なアプリケーションのための推奨アプローチになりつつあります。 将来はより多くの収束とより良いツールサポートが期待されます。