Skip to content
Published on

LLM Agentフレームワーク比較:AutoGen vs CrewAI vs LangGraph 実践選択ガイド

Authors
  • Name
    Twitter
LLM Agentフレームワーク比較

はじめに

LLMベースのエージェントシステムは、単純なチャットボットを超えて、ツールを使用し計画を立てマルチステップタスクを自律的に実行するAIアプリケーションの中核アーキテクチャとして定着しました。2024〜2025年にかけてエージェントフレームワークのエコシステムは爆発的に成長し、2026年現在、プロダクションレベルの安定性を備えた3つのフレームワークが市場の主流として確立されています。

AutoGen(Microsoft)は会話ベースのマルチエージェントインタラクションに特化しており、エージェント間の自然言語対話を通じて複雑なタスクを解決するパラダイムを提示しました。CrewAIは役割ベースのエージェントチームを直感的なAPIで構成し、迅速なプロトタイピングとビジネスワークフロー自動化に強みを持ちます。LangGraph(LangChain)は有向グラフ(directed graph)でエージェントワークフローをモデリングし、複雑な状態管理と条件分岐が必要なプロダクションシステムに最適化されています。

各フレームワークはそれぞれ異なる設計思想と強みを持っており、プロジェクトの要件に応じて最適な選択が異なります。本記事では3つのフレームワークのアーキテクチャを深く比較し、実践的なコード例を通じてそれぞれの使い方を学び、プロダクション展開で直面する障害事例とリカバリ戦略まで包括的に解説します。

エージェントフレームワークが必要な理由

単一LLM呼び出しの限界

単純なLLM API呼び出しには以下のような限界があります。

  1. 単発的な応答: 1回の呼び出しで複雑なマルチステップタスクを完了することはできません。調査、分析、実行、検証の連鎖的なタスクが必要な場合、複数回の呼び出しを調整する必要があります。
  2. ツール使用の制約: LLMが外部API、データベース、ファイルシステムなどとインタラクションするには、Function Callingの結果をLLMにフィードバックするループが必要です。
  3. 状態管理の欠如: 会話履歴、タスクの進行状況、中間成果物を体系的に管理するメカニズムがありません。
  4. エラーリカバリの欠如: API呼び出しの失敗、ハルシネーション(hallucination)、無限ループなどに対するシステムレベルの対応ができません。

エージェントフレームワークの役割

エージェントフレームワークはこれらの問題を以下のように解決します。

問題フレームワークの解決方法
マルチステップ実行計画(planning)-> 実行(execution)-> 観察(observation)ループの自動管理
ツール統合ツールレジストリ、入出力スキーマの自動生成、結果パース
状態管理会話メモリ、タスク状態、中間成果物の永続化
エラーリカバリリトライポリシー、タイムアウト、フォールバック戦略、Human-in-the-loop
可観測性実行トレーシング、トークン使用量追跡、デバッグツール

アーキテクチャ比較

AutoGen:会話中心のマルチエージェント

AutoGenの核心的な設計思想は「エージェント間の対話による問題解決」です。すべてのインタラクションがメッセージベースであり、エージェントは会話パターン(conversation pattern)に従って発言の順番と内容が決定されます。

主要コンポーネント:

  • AssistantAgent: LLMベースのエージェントで、システムプロンプトとツールを持ちます
  • UserProxyAgent: ユーザーを代理するエージェントで、コード実行やユーザー入力の転送を担当します(v0.4ではUserProxy単体ではなくAssistantAgentとチームで構成)
  • GroupChat / Teams: 複数のエージェントが参加するグループ会話またはチーム構造
  • ConversationPattern: RoundRobin、Selector、Swarmなどの会話フローパターン

アーキテクチャの特徴:

  • すべてのエージェントインタラクションがメッセージ交換で行われます
  • エージェント間の関係が会話パターンで定義されます
  • コード実行環境(Docker、ローカル)を組み込みで提供します
  • v0.4(AgentChat)で非同期(async)優先設計を採用しました

CrewAI:役割ベースのエージェントチーム

CrewAIは「各エージェントに明確な役割(role)と目標(goal)を付与し、タスク(task)を割り当ててチームとして運営する」という直感的なメタファーを使用します。実際の組織のチーム運営と類似したモデルです。

主要コンポーネント:

  • Agent: 役割(role)、目標(goal)、背景(backstory)、ツール(tools)を持つエージェント
  • Task: エージェントに割り当てられる具体的な作業単位
  • Crew: エージェントとタスクを組み合わせた実行単位
  • Process: Sequential(順次)、Hierarchical(階層)、Parallel(並列)の実行方式

アーキテクチャの特徴:

  • LangChainに依存しない独立した実装(独自エンジン)
  • YAMLベースのエージェント/タスク設定ファイル分離をサポート
  • タスク間の結果委譲(delegation)メカニズムを内蔵
  • 迅速なプロトタイピングに最適化された簡潔なAPI

LangGraph:グラフベースのステートマシン

LangGraphはエージェントワークフローを**有向グラフ(directed graph)**でモデリングします。各ノードは実行関数であり、エッジは状態に基づく遷移(transition)を定義します。これは本質的にステートマシン(state machine)の構造と同一です。

主要コンポーネント:

  • StateGraph: 状態を管理する有向グラフ
  • State (TypedDict): グラフ全体で共有される型安全な状態
  • Node: 状態を入力として受け取り更新を返す関数
  • Edge: ノード間の遷移を定義(条件付きエッジを含む)
  • Checkpointer: 状態を永続化して再開可能な実行をサポート

アーキテクチャの特徴:

  • 明示的な制御フローによる予測可能な実行
  • 型安全な状態管理(TypedDictまたはPydantic)
  • 組み込みのHuman-in-the-loopサポート
  • LangSmithとの統合による優れた可観測性
  • v1.0到達(2025年末)によりAPIが安定化

マルチエージェントパターン比較表

比較項目AutoGenCrewAILangGraph
設計思想会話中心の協業役割ベースのチームグラフベースのステートマシン
エージェント定義AssistantAgent + システムプロンプトAgent(role, goal, backstory)Node関数 + State
オーケストレーションConversationPattern (RoundRobin, Selector, Swarm)Process (Sequential, Hierarchical, Parallel)条件付きエッジ + 状態遷移
状態管理会話履歴ベースタスク間コンテキスト伝達TypedDict/Pydantic状態オブジェクト
ツール統合Function基盤のツール登録@toolデコレータ / 組み込みツールToolNode + tools_condition
Human-in-the-loopUserProxyパターンタスクレベルの承認Interrupt + Checkpointer
コード実行Docker/ローカル組み込み別途ツールで実装別途ツールで実装
メモリ会話ベースの短期/長期短期 + 選択的長期Checkpointerベースの永続化
可観測性AutoGen Studio UIロギングベースLangSmith統合(追跡/再生)
学習コスト
プロダクション成熟度中(MS Agent Framework移行中)中上高(v1.0)
ライセンスMITMITMIT
Pythonバージョン3.10+3.10~3.133.9+

各フレームワーク実践コード例

AutoGen:リサーチエージェントチームの構成

AutoGen v0.4 AgentChat APIを使用したマルチエージェントリサーチシステムの例です。

import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat, SelectorGroupChat
from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
from autogen_ext.models.openai import OpenAIChatCompletionClient

# 1. LLMクライアント設定
model_client = OpenAIChatCompletionClient(
    model="gpt-4o",
    api_key="your-api-key",
)

# 2. ツール定義
async def search_web(query: str) -> str:
    """ウェブから情報を検索します。"""
    # 実際にはTavily、SerpAPIなどを使用
    return f"'{query}'に対する検索結果: [検索結果の要約]"

async def analyze_data(data: str) -> str:
    """データを分析しインサイトを抽出します。"""
    return f"分析結果: {data[:100]}に対する深層分析完了"

# 3. エージェント定義
researcher = AssistantAgent(
    name="Researcher",
    model_client=model_client,
    system_message="""あなたは専門リサーチャーです。
    与えられたテーマについてウェブ検索を行い、関連情報を収集します。
    収集した情報を構造化してAnalystに渡します。""",
    tools=[search_web],
)

analyst = AssistantAgent(
    name="Analyst",
    model_client=model_client,
    system_message="""あなたはデータアナリストです。
    Researcherが収集した情報を分析し、核心的なインサイトを抽出します。
    分析結果をWriterに渡します。""",
    tools=[analyze_data],
)

writer = AssistantAgent(
    name="Writer",
    model_client=model_client,
    system_message="""あなたはレポート作成者です。
    Analystの分析結果をもとに最終レポートを作成します。
    レポート作成が完了したら'TERMINATE'を出力します。""",
)

# 4. チーム構成と実行
termination = MaxMessageTermination(max_messages=15) | TextMentionTermination("TERMINATE")

# SelectorGroupChat: LLMが次の発言者を選択
team = SelectorGroupChat(
    participants=[researcher, analyst, writer],
    model_client=model_client,
    termination_condition=termination,
    selector_prompt="""次の発言者を選択してください。
    情報収集が必要ならResearcher、分析が必要ならAnalyst、
    最終レポート作成が必要ならWriterを選択します。""",
)

async def main():
    result = await team.run(
        task="2026年のAIエージェントフレームワーク市場動向を調査し、レポートを作成してください。"
    )
    for message in result.messages:
        print(f"[{message.source}]: {message.content[:200]}")
    print(f"\n総メッセージ数: {len(result.messages)}")

asyncio.run(main())

CrewAI:コンテンツ制作パイプライン

CrewAIを使用したブログ記事自動生成パイプラインの例です。

from crewai import Agent, Task, Crew, Process
from crewai.tools import tool
from langchain_openai import ChatOpenAI

# 1. LLM設定
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)

# 2. カスタムツール定義
@tool("Search Tool")
def search_tool(query: str) -> str:
    """指定されたクエリでウェブ検索を実行します。"""
    # 実際にはTavily、SerpAPIなどを使用
    return f"'{query}' 検索結果: [関連情報の要約]"

@tool("SEO Analyzer")
def seo_analyzer(content: str) -> str:
    """コンテンツのSEOスコアを分析します。"""
    word_count = len(content.split())
    return f"単語数: {word_count}, SEOスコア: {'良好' if word_count > 500 else '不足'}"

# 3. エージェント定義
research_agent = Agent(
    role="技術リサーチャー",
    goal="最新の技術トレンドを調査し、正確な情報を収集する",
    backstory="""10年のキャリアを持つ技術ジャーナリストで、AIとソフトウェアエンジニアリング
    分野の最新動向を常に把握しています。""",
    tools=[search_tool],
    llm=llm,
    verbose=True,
    allow_delegation=True,  # 他のエージェントへのタスク委譲が可能
)

writer_agent = Agent(
    role="技術ブログライター",
    goal="読者が容易に理解できる深みのある技術ブログを作成する",
    backstory="""技術ブログ専門のライターで、複雑な技術概念を
    明確かつ実用的な文章に落とし込むことに長けています。""",
    llm=llm,
    verbose=True,
)

editor_agent = Agent(
    role="編集長",
    goal="コンテンツの品質、正確性、SEO最適化を検査する",
    backstory="""技術メディアの編集長で、コンテンツの技術的正確性と
    可読性、SEO最適化を同時に追求しています。""",
    tools=[seo_analyzer],
    llm=llm,
    verbose=True,
)

# 4. タスク定義
research_task = Task(
    description="""'{topic}'に関する総合リサーチを実施します。
    - 最新動向を3つ以上収集
    - 核心的な技術概念の整理
    - 実際の活用事例を2件以上含める""",
    expected_output="構造化されたリサーチレポート(マークダウン形式)",
    agent=research_agent,
)

writing_task = Task(
    description="""リサーチ結果をもとに技術ブログ記事を作成します。
    - 2000字以上の本文
    - コード例を2つ以上含める
    - 実務者視点のインサイトを含める""",
    expected_output="完成したブログ記事(マークダウン形式)",
    agent=writer_agent,
    context=[research_task],  # research_taskの結果をコンテキストとして受け取る
)

editing_task = Task(
    description="""作成されたブログ記事を検査します。
    - 技術的正確性の確認
    - 可読性の改善
    - SEO分析の実施
    - 最終修正版の出力""",
    expected_output="検査完了した最終ブログ記事",
    agent=editor_agent,
    context=[writing_task],
)

# 5. Crew構成と実行
crew = Crew(
    agents=[research_agent, writer_agent, editor_agent],
    tasks=[research_task, writing_task, editing_task],
    process=Process.sequential,  # 順次実行
    verbose=True,
    memory=True,                 # 短期メモリを有効化
    max_rpm=10,                  # API呼び出しレート制限
)

# 実行
result = crew.kickoff(inputs={"topic": "LLMエージェントフレームワーク比較"})
print(f"最終結果:\n{result}")
print(f"トークン使用量: {crew.usage_metrics}")

LangGraph:条件分岐を持つカスタマーサポートエージェント

LangGraphを使用した状態ベースのカスタマーサポートエージェントの例です。条件分岐、Human-in-the-loop、エラーリカバリを含みます。

from typing import Annotated, Literal, TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, SystemMessage

# 1. 状態定義
class SupportState(TypedDict):
    messages: Annotated[list, add_messages]
    category: str           # 問い合わせカテゴリ (billing, technical, general)
    sentiment: str          # 顧客感情 (positive, neutral, negative)
    escalated: bool         # 上位担当者へのエスカレーション有無
    resolution: str         # 解決状態

# 2. ツール定義
@tool
def lookup_order(order_id: str) -> str:
    """注文情報を照会します。"""
    orders = {
        "ORD-001": {"status": "配送中", "eta": "2026-03-11"},
        "ORD-002": {"status": "配送完了", "delivered": "2026-03-07"},
    }
    if order_id in orders:
        return f"注文 {order_id}: {orders[order_id]}"
    return f"注文 {order_id}が見つかりません。"

@tool
def create_ticket(summary: str, priority: str) -> str:
    """カスタマーサポートチケットを作成します。"""
    return f"チケット作成完了 - 概要: {summary}, 優先度: {priority}, チケットID: TKT-{hash(summary) % 10000}"

@tool
def refund_order(order_id: str, reason: str) -> str:
    """注文の返金を処理します。(承認が必要)"""
    return f"返金リクエスト受付: {order_id}, 理由: {reason}。管理者承認待ち。"

tools = [lookup_order, create_ticket, refund_order]
llm = ChatOpenAI(model="gpt-4o", temperature=0).bind_tools(tools)

# 3. ノード関数定義
def classify_intent(state: SupportState) -> dict:
    """顧客の問い合わせを分類します。"""
    classifier_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
    last_message = state["messages"][-1].content

    response = classifier_llm.invoke([
        SystemMessage(content="""顧客の問い合わせを分類してください。
        カテゴリ: billing, technical, general
        感情: positive, neutral, negative
        JSON形式で応答: {"category": "...", "sentiment": "..."}"""),
        HumanMessage(content=last_message)
    ])

    import json
    try:
        result = json.loads(response.content)
    except json.JSONDecodeError:
        result = {"category": "general", "sentiment": "neutral"}

    return {
        "category": result.get("category", "general"),
        "sentiment": result.get("sentiment", "neutral"),
    }

def agent_respond(state: SupportState) -> dict:
    """エージェントが応答します。"""
    system_prompt = f"""あなたはカスタマーサポートエージェントです。
    現在の問い合わせカテゴリ: {state.get('category', 'unknown')}
    顧客感情: {state.get('sentiment', 'unknown')}
    エスカレーション有無: {state.get('escalated', False)}

    顧客に親切かつ正確に応答してください。
    必要に応じてツールを使用して情報を照会したりチケットを作成してください。"""

    messages = [SystemMessage(content=system_prompt)] + state["messages"]
    response = llm.invoke(messages)
    return {"messages": [response]}

def check_escalation(state: SupportState) -> dict:
    """エスカレーションの必要性を判断します。"""
    if state.get("sentiment") == "negative" and state.get("category") == "billing":
        return {"escalated": True}
    return {"escalated": False}

def human_escalation(state: SupportState) -> dict:
    """上位担当者にエスカレーションします。"""
    return {
        "messages": [SystemMessage(content="[システム] この問い合わせは上位担当者に転送されました。しばらくお待ちください。")],
        "resolution": "escalated_to_human",
    }

# 4. ルーティング関数
def route_after_classification(state: SupportState) -> Literal["check_escalation", "agent_respond"]:
    """分類結果に基づいてルーティングします。"""
    if state.get("sentiment") == "negative":
        return "check_escalation"
    return "agent_respond"

def route_after_escalation_check(state: SupportState) -> Literal["human_escalation", "agent_respond"]:
    """エスカレーション判断結果に基づいてルーティングします。"""
    if state.get("escalated"):
        return "human_escalation"
    return "agent_respond"

# 5. グラフ構成
workflow = StateGraph(SupportState)

# ノード追加
workflow.add_node("classify_intent", classify_intent)
workflow.add_node("check_escalation", check_escalation)
workflow.add_node("agent_respond", agent_respond)
workflow.add_node("tools", ToolNode(tools))
workflow.add_node("human_escalation", human_escalation)

# エッジ定義
workflow.add_edge(START, "classify_intent")
workflow.add_conditional_edges("classify_intent", route_after_classification)
workflow.add_conditional_edges("check_escalation", route_after_escalation_check)
workflow.add_conditional_edges("agent_respond", tools_condition, {"tools": "tools", END: END})
workflow.add_edge("tools", "agent_respond")
workflow.add_edge("human_escalation", END)

# チェックポインター(状態永続化)
checkpointer = MemorySaver()
app = workflow.compile(checkpointer=checkpointer)

# 6. 実行
config = {"configurable": {"thread_id": "customer-123"}}
result = app.invoke(
    {"messages": [HumanMessage(content="注文ORD-001の配送が遅すぎます。返金してください。")]},
    config=config,
)

for msg in result["messages"]:
    print(f"[{msg.__class__.__name__}]: {msg.content[:200]}")
print(f"カテゴリ: {result['category']}, 感情: {result['sentiment']}")

ツール統合の比較

各フレームワークにおける外部ツール統合方式を比較します。

ツール定義パターンの比較

項目AutoGenCrewAILangGraph
定義方式Python関数 + type hint@toolデコレータまたはBaseTool継承@toolデコレータ(LangChain)
スキーマ自動生成関数シグネチャベースdocstring + type hintdocstring + type hint
非同期サポートasync関数ネイティブ限定的asyncサポート
エラー処理try/except直接実装ToolExceptionクラスToolException + フォールバック
組み込みツールコード実行、ウェブ検索SerperDev、ファイル読み書き、コードインタープリタウェブ検索、Retriever、Python REPL

主な違い

AutoGenはPython関数を直接ツールとして登録する最も簡潔な方式を使用します。関数のtype hintとdocstringから自動的にJSONスキーマが生成され、async関数をネイティブにサポートします。コード実行環境をデフォルトで提供し、エージェントが生成したコードをDockerコンテナまたはローカル環境で即座に実行できる点が独自の強みです。

CrewAI@toolデコレータでツールを定義するか、BaseToolクラスを継承してより複雑なツールを実装できます。組み込みツールライブラリが豊富であり、エージェントが他のエージェントにタスクを委譲すること自体も一種のツールとして動作します。

LangGraphはLangChainのツールエコシステムをそのまま活用します。ToolNodetools_conditionを使用してツール呼び出しと結果処理をグラフのノードとして明示的にモデリングします。これにより、ツール呼び出しの前後にカスタムロジック(ロギング、検証、変換)を挿入しやすくなります。

メモリと状態管理

短期メモリ(会話コンテキスト)

フレームワーク短期メモリ方式ウィンドウ管理トークン制限の処理
AutoGenメッセージリストの自動管理手動またはsummarizeパターン会話要約エージェントの活用
CrewAIタスク間コンテキストの自動伝達自動(設定可能)自動要約
LangGraphStateのmessagesフィールドadd_messages reducerTrimmer/Summarizerノード

長期メモリ(セッション間の永続化)

AutoGenは会話履歴をファイルやデータベースに保存するカスタム実装が必要です。AutoGen Studio UIを通じてセッション管理機能を提供していますが、プログラミングレベルの長期メモリAPIは限定的です。

CrewAImemory=True設定で短期メモリを有効化し、オプションでChromaDBなどのベクトルストアを使用した長期メモリを設定できます。エージェントが過去のタスクで学習したパターンを自動的に活用するメカニズムが内蔵されています。

LangGraphCheckpointerインターフェースを通じて最も体系的な状態永続化を提供します。MemorySaver(インメモリ)、SqliteSaverPostgresSaverなどさまざまなバックエンドをサポートし、thread_idベースで会話を再開したり、特定の時点に巻き戻したりすることが可能です。これはプロダクション環境で非常に重要な機能です。

プロダクション展開の考慮事項

展開アーキテクチャの比較

項目AutoGenCrewAILangGraph
サービング方式FastAPI直接実装FastAPI/Flask直接実装LangServe / LangGraph Cloud
スケーリング手動実装手動実装LangGraph Platform(マネージド)
状態永続化カスタム実装組み込み(限定的)Checkpointer(PostgreSQLなど)
並行性asyncioベーススレッド/プロセスベースasyncio + Checkpointerベース
モニタリングカスタムロギングカスタムロギングLangSmith(トレーシング、評価)
コスト追跡コールバックベースusage_metrics組み込みLangSmithトークン追跡

プロダクションチェックリスト

  1. レートリミット管理: 各エージェントのLLM呼び出し頻度を制御します。CrewAIのmax_rpm、LangGraphのカスタムミドルウェア、AutoGenのコールバックを活用します。
  2. タイムアウト設定: エージェントが無限ループに陥ることを防止します。最大反復回数と全体実行時間の制限を必ず設定します。
  3. コスト上限: トークン使用量をリアルタイムで監視し、予算超過時に実行を停止する安全装置を実装します。
  4. エラー隔離: 1つのエージェントの障害がシステム全体を停止させないよう、サーキットブレーカーパターンを適用します。
  5. 監査ログ: エージェントのすべての意思決定とツール呼び出しをトレース可能な形式で記録します。

障害事例とリカバリ戦略

事例1:エージェントの無限ループ

症状: エージェントが同じツールを繰り返し呼び出したり、2つのエージェントが果てしなく会話を続けたりします。

原因: 終了条件が曖昧であるか、エージェントのプロンプトが明確な完了基準を提示していない場合に発生します。

フレームワーク別リカバリ:

  • AutoGen: MaxMessageTermination(max_messages=20)で最大メッセージ数を制限。TextMentionTermination("TERMINATE")と組み合わせて二重の安全装置を構成。
  • CrewAI: max_iterパラメータでエージェントの最大反復回数を制限。Crew(max_rpm=10)でAPI呼び出し速度も制限。
  • LangGraph: recursion_limitパラメータでグラフ走査の最大回数を制限。状態にstep_countを追加し条件付きエッジで確認。

事例2:ツール呼び出しの失敗とハルシネーション

症状: エージェントが存在しないツールを呼び出したり、ツールに誤った引数を渡したりします。

原因: LLMがツールのスキーマを正確に理解できていないか、コンテキストウィンドウが不足してツール定義を「忘れて」しまう場合に発生します。

リカバリ戦略:

  • ツールのdocstringとパラメータ説明をできる限り明確かつ詳細に記述します。
  • ツール呼び出し結果に対する検証レイヤーを追加します。想定される形式と異なる結果が返された場合はリトライするかフォールバックロジックを実行します。
  • LangGraphではツールノードの後に検証ノードを追加して結果の妥当性を確認できます。

事例3:コンテキストウィンドウの超過

症状: 長い会話や大量のツール結果によりトークン制限に達し、エージェントが重要な情報を失います。

原因: マルチエージェントシステムではメッセージが指数関数的に蓄積され、コンテキストウィンドウを急速に消費します。

リカバリ戦略:

  • 会話履歴を定期的に要約して圧縮します。
  • ツールの結果を要約するか、核心的な情報のみを抽出してコンテキストに含めます。
  • LangGraphではtrim_messagesユーティリティを使用して直近N件のメッセージのみを保持します。

事例4:エージェント間のタスク委譲失敗

症状: CrewAIでエージェントが他のエージェントにタスクを委譲したが、委譲先のエージェントがコンテキストを理解できず見当違いの結果を返します。

原因: 委譲時に十分なコンテキストが伝達されなかったか、委譲先エージェントの役割に合わないタスクが渡された場合です。

リカバリ戦略:

  • エージェントのallow_delegation=Falseで不要な委譲を防止します。
  • タスクの説明に具体的な入出力形式を明示します。
  • contextパラメータを通じて先行タスクの結果を明示的に伝達します。

事例5:API障害時のシステム全体停止

症状: LLM APIの障害またはツールAPIの障害により、エージェントシステム全体が停止します。

原因: 単一障害点に対するフォールバックメカニズムがない場合です。

リカバリ戦略:

# LangGraphでのエラーリカバリパターン例
from tenacity import retry, stop_after_attempt, wait_exponential
from langchain_core.messages import AIMessage

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, max=10))
def resilient_agent_node(state: SupportState) -> dict:
    """リトライロジックを含むエージェントノード。"""
    try:
        response = llm.invoke(state["messages"])
        return {"messages": [response]}
    except Exception as e:
        # フォールバック: 事前定義された応答を返す
        fallback_response = AIMessage(
            content="申し訳ございません。一時的なシステムエラーが発生しました。"
                    "しばらくしてから再度お試しください。お急ぎの場合は"
                    "カスタマーセンター 0120-000-000 までご連絡ください。"
        )
        return {
            "messages": [fallback_response],
            "resolution": f"error_fallback: {str(e)}",
        }

選択ガイド:意思決定ツリー

プロジェクトの要件に基づくフレームワーク選択基準を整理します。

CrewAIを選択すべき場合

  • 迅速なプロトタイピングが最も重要な場合。数時間以内にマルチエージェントシステムを構築しデモを行う必要がある場合。
  • 役割ベースのワークフローが自然な場合。「リサーチャー -> アナリスト -> ライター」のような線形パイプライン。
  • 非技術系のステークホルダーにエージェントの定義を見せる必要がある場合。role/goal/backstoryの形式が最も直感的です。
  • プロダクションレベルの状態管理や複雑な条件ロジックがそれほど必要でない場合。

AutoGenを選択すべき場合

  • エージェント間の自由な対話が核心となる場合。討論、合意形成、ブレインストーミングのような会話パターン。
  • コード生成と実行が主要機能である場合。組み込みのコード実行環境が大きなメリット。
  • Microsoftエコシステムとの統合が必要な場合。Azure OpenAI、Microsoft Agent Frameworkとの互換性。
  • .NET環境でもエージェントを実行する必要がある場合。AutoGenは.NETバージョンも提供しています。

LangGraphを選択すべき場合

  • 複雑な条件ロジックと分岐が必要な場合。状態に応じて実行パスが動的に変化するワークフロー。
  • プロダクションレベルの安定性が必須の場合。状態の永続化、再開可能な実行、監査ログ。
  • Human-in-the-loopがコア要件の場合。特定のステップで人間の承認を得てから進行。
  • **可観測性(Observability)**が重要な場合。LangSmithを通じた詳細なトレーシングとデバッグ。
  • 長時間実行ワークフローをサポートする必要がある場合。数時間から数日にわたる非同期タスクの管理。

意思決定の要約表

優先考慮事項第1推奨第2推奨備考
プロトタイピング速度CrewAIAutoGenLangGraphは初期セットアップコストが高い
プロダクション安定性LangGraphCrewAIAutoGenはMS Agent Framework移行の過渡期
対話型エージェントAutoGenLangGraphCrewAIは対話よりタスク中心
複雑なワークフローLangGraphAutoGen条件分岐、並列実行、ステートマシン
コード実行AutoGenLangGraphAutoGenのコード実行環境が最も成熟
可観測性/デバッグLangGraphAutoGenLangSmith統合が圧倒的な強み
学習コストCrewAIAutoGenLangGraphはグラフ概念の理解が必要
コミュニティ/エコシステムLangGraphCrewAILangChainエコシステムの規模が最も大きい

運用時の注意事項

コスト管理

マルチエージェントシステムは単一LLM呼び出しと比較してトークン消費が5〜20倍増加する可能性があります。エージェント数が増えて会話が長くなるほど、コストは指数関数的に増大します。必ずエージェント別、タスク別のトークン使用量を追跡し、予算上限を設定する必要があります。可能であれば、分類器のような単純なタスクにはGPT-4o-miniのような小型モデルを、核心的な推論にのみ大型モデルを使用するモデルルーティング戦略を適用します。

セキュリティの考慮事項

エージェントがツールを通じて外部システムにアクセスする際には以下に注意します。

  • 最小権限の原則: エージェントには必要最小限のAPI権限のみを付与します。読み取り専用の作業に書き込み権限を与えないようにします。
  • 入力検証: エージェントがツールに渡す引数を検証します。SQLインジェクションやコマンドインジェクションの攻撃ベクトルになり得ます。
  • 出力フィルタリング: エージェントの応答に機密情報(PII、資格情報)が含まれないようフィルタリングレイヤーを追加します。
  • サンドボックス化: コード実行ツールは必ず隔離された環境(Dockerコンテナ)で実行します。

テスト戦略

エージェントシステムのテストは従来のソフトウェアテストより複雑です。LLMの非決定的な出力のため、同一の入力に対して異なる結果が返される可能性があります。

  • 単体テスト: 各ツール関数を独立してテストします。エージェントなしでツールの入出力を検証します。
  • 統合テスト: モック(mock)LLM応答を使用してエージェントのツール呼び出しパターンを検証します。
  • 評価(Eval): LLM-as-Judgeパターンを使用してエージェントの最終出力品質を自動評価します。LangSmithの評価機能や独自の評価パイプラインを構築します。
  • 負荷テスト: 同時ユーザー数とリクエスト頻度に応じたシステム性能を測定します。特にLLM APIのレートリミットに注意します。

バージョン管理とマイグレーション

3つのフレームワークすべてが急速に発展しておりAPI変更が頻繁です。特にAutoGenはv0.2からv0.4(AgentChat)への大規模なアーキテクチャ変更があり、今後Microsoft Agent Frameworkへの移行が予告されています。フレームワークのバージョンを固定(pinning)し、メジャーアップデート時には別ブランチでマイグレーションテストを実施するのが安全です。

おわりに

LLMエージェントフレームワークの選択に銀の弾丸はありません。CrewAIは「素早く作って見せる」シナリオで真価を発揮し、AutoGenはエージェント間の豊かな対話インタラクションが核心となる場合に適しており、LangGraphはプロダクションレベルの安定性と制御が求められる複雑なワークフローに最適です。

実務で推奨するアプローチは以下の通りです。まずCrewAIで迅速にプロトタイプを構築し、エージェントシステムの価値を検証します。検証が完了したら、プロダクション要件(状態管理、再開可能性、可観測性、Human-in-the-loop)を基準にLangGraphでの再実装を検討します。対話中心のエージェントが核心であればAutoGenを選択しつつ、Microsoft Agent Frameworkへの移行計画も併せて策定します。

どのフレームワークを選択しても、エージェントシステムの成功はフレームワーク自体よりも、プロンプトエンジニアリングの品質、ツール設計の堅牢性、そして障害モードに対する徹底的な備えにかかっているという点を忘れてはなりません。

参考資料

クイズ

Q1: 「LLM Agentフレームワーク比較:AutoGen vs CrewAI vs LangGraph 実践選択ガイド」の主なトピックは何ですか?

LLMエージェントフレームワーク3種(AutoGen、CrewAI、LangGraph)の総合比較ガイドです。アーキテクチャ設計思想、マルチエージェントオーケストレーションパターン、ツール統合、メモリ管理、プロダクション展開戦略、そして実践的な選択基準までコード例を交えて解説します。

Q2: エージェントフレームワークが必要な理由とは何ですか? 単一LLM呼び出しの限界 単純なLLM API呼び出しには以下のような限界があります。 単発的な応答: 1回の呼び出しで複雑なマルチステップタスクを完了することはできません。調査、分析、実行、検証の連鎖的なタスクが必要な場合、複数回の呼び出しを調整する必要があります。 ツール使用の制約: LLMが外部API、データベース、ファイルシステムなどとインタラクションするには、Function Callingの結果をLLMにフィードバックするループが必要です。 状態管理の欠如: 会話履歴、タスクの進行状況、中間成果物を体系的に管理するメカニズムがありません。

Q3: アーキテクチャ比較について説明してください。 AutoGen:会話中心のマルチエージェント AutoGenの核心的な設計思想は「エージェント間の対話による問題解決」です。すべてのインタラクションがメッセージベースであり、エージェントは会話パターン(conversation pattern)に従って発言の順番と内容が決定されます。

Q4: 各フレームワーク実践コード例の主な特徴は何ですか? AutoGen:リサーチエージェントチームの構成 AutoGen v0.4 AgentChat APIを使用したマルチエージェントリサーチシステムの例です。 CrewAI:コンテンツ制作パイプライン CrewAIを使用したブログ記事自動生成パイプラインの例です。 LangGraph:条件分岐を持つカスタマーサポートエージェント LangGraphを使用した状態ベースのカスタマーサポートエージェントの例です。条件分岐、Human-in-the-loop、エラーリカバリを含みます。

Q5: ツール統合の比較はどのように機能しますか? 各フレームワークにおける外部ツール統合方式を比較します。 ツール定義パターンの比較 主な違い AutoGenはPython関数を直接ツールとして登録する最も簡潔な方式を使用します。関数のtype hintとdocstringから自動的にJSONスキーマが生成され、async関数をネイティブにサポートします。コード実行環境をデフォルトで提供し、エージェントが生成したコードをDockerコンテナまたはローカル環境で即座に実行できる点が独自の強みです。