なぜ単一 Agent では足りないのか:MAS の核心概念
2024〜2025 年に Agent は実験室から本番へ移行しましたが、多くのチームが同じ壁に当たります。すべてのタスクを一つの LLM Agent に任せると、スケール時にシステムが破綻するのです。Google 社内の Agent Bake-Off 実験では、分散型マルチエージェント構成に切り替えた際、処理時間が 1 時間から 10 分へ短縮されました(約 6 倍)。AdaptOrch(2026)も示していますが、オーケストレーションのトポロジ選択は、基盤モデル選択よりも性能への影響が大きい場合があり、SWE-bench などのベンチマークで 12〜23% の改善が報告されています。
コンテキストウィンドウの壁:複雑タスクの中間結果がコンテキストを埋め尽くし、後段の推論品質が急落します。
専門性の希薄化:検索・コーディング・レビューを一人で担うと、どれも中途半端になります(jack-of-all-trades 問題)。
直列実行の非効率:サブタスクを順番に処理すると、総時間は各ステップの合計になり、並列化できません。
単一障害点(SPOF):その Agent が停止すると、ワークフロー全体が止まります。
結論:マルチエージェント協調アーキテクチャ(MAS)はこれらを解くために設計されています。トポロジ > モデル選択、という原則を覚えておいてください。
MAS の定義:Multi-Agent System(MAS)は、複数の独立した AI Agent が通信プロトコルとオーケストレーション機構を通じて協調し、単一 Agent では効率的に処理できない複雑タスクを完了するシステムです。
| 特徴 | 説明 |
|---|---|
| 役割の専門化 | 検索・推論・生成・検証など、明確に定義された一つのサブタスクのみを担当します |
| ツールアクセス | 自身のタスクに必要なツールセットだけを持ちます |
| 状態の分離 | 独自のコンテキストとメモリを保持し、他 Agent を汚染しません |
| 交換可能性 | 独立してアップグレード・差し替えでき、全体への影響を最小化できます |
三つの制御モード:
| モード | 構造 | 利点 | 欠点 |
|---|---|---|---|
| 集中型(Centralized) | Orchestrator が A/B/C を一元制御 | 監査しやすく、制御しやすい | ボトルネックになりやすい |
| 分散型(Decentralized) | Agent 同士が P2P で通信 | 高い弾力性と低遅延 | デバッグが難しく、非決定性が高い |
| 階層型(Hierarchical) | 上位 Orchestrator → Team Lead → Worker | 制御性と拡張性のバランスが良い | 設計の複雑さは中程度 |
LangGraph vs CrewAI vs AutoGen:フレームワーク比較と選定指針
フレームワークを正しく選べば、自前のオーケストレーションと状態管理コードを大幅に削減できます。下表はアーキテクチャパラダイム、言語サポート、学習曲線、状態管理、HITL、可観測性、本番準備度、プロトタイプ速度、Azure 連携、典型シナリオをカバーしています。
| 観点 | LangGraph | CrewAI | AutoGen(Microsoft) |
|---|---|---|---|
| アーキテクチャ | ステートマシングラフ | 役割制チーム | 対話型マルチ Agent |
| 言語 | Python / JS/TS | Python | Python / .NET |
| 学習曲線 | やや急 | 緩やか | 中程度 |
| 状態管理 | ネイティブ対応 | 自前実装が必要 | 限定的 |
| Human-in-the-Loop | ネイティブ interrupt() | 自前実装が必要 | 対応 |
| 可観測性 | LangSmith(商用) | 限定的 | Azure Monitor |
| 本番準備度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 迅速なプロトタイプ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| Azure 連携 | ⭐⭐⭐ | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 向いている場面 | 複雑なステートフルワークフロー | 役割制コンテンツパイプライン | 対話型協調と議論 |
選定の目安:
LangGraph を選ぶ:金融・医療・コンプライアンスなど本番信頼性が必要な場合、複雑な状態永続化、精密な HITL 制御、条件分岐とループの厳密な表現が求められるときです。
CrewAI を選ぶ:1〜2 日でアイデア検証したい、チームが「役割」で直感的に Agent を理解できる、コンテンツ生成や調査レポートなど役割制シナリオのときです。
AutoGen を選ぶ:Microsoft / Azure スタック、Agent 同士の多輪議論と反復推論、研究や対話パターンの実験が中心のときです。
オーケストレーションのトポロジ選択は、基盤モデル選択よりも性能への影響が大きい——まずパターンとフレームワークを決め、次にモデルを選びます。
六大オーケストレーションパターンと MCP+A2A 二層通信
以下の六パターンで、本番マルチエージェントの 95% 以上をカバーできます。パターン一は順次パイプラインです。Agent A の出力を B の入力に渡し、厳密に直列実行します。記事作成やコードレビューなど、固定フローに適しています。
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
class PipelineState(TypedDict):
query: str
retrieved_docs: str
analysis: str
final_report: str
def retrieval_agent(state: PipelineState):
docs = search_knowledge_base(state["query"])
return {"retrieved_docs": docs}
def analysis_agent(state: PipelineState):
result = llm.invoke(f"Analyze the following: {state['retrieved_docs']}")
return {"analysis": result.content}
def writer_agent(state: PipelineState):
report = llm.invoke(f"Write a report based on: {state['analysis']}")
return {"final_report": report.content}
builder = StateGraph(PipelineState)
builder.add_node("retriever", retrieval_agent)
builder.add_node("analyzer", analysis_agent)
builder.add_node("writer", writer_agent)
builder.add_edge(START, "retriever")
builder.add_edge("retriever", "analyzer")
builder.add_edge("analyzer", "writer")
builder.add_edge("writer", END)
pipeline = builder.compile()
パターン二は並列ファンアウト/ファンインです。複数 Agent が独立サブタスクを同時処理し、総時間は max(T1…Tn) になります。LangGraph の Send API と Annotated[list, operator.add] Reducer で並列結果を自動集約できます。
from langgraph.types import Send
from langgraph.graph import StateGraph, START, END
from typing import TypedDict, Annotated
import operator
class ResearchState(TypedDict):
query: str
research_results: Annotated[list, operator.add]
final_synthesis: str
def supervisor(state: ResearchState):
subtasks = [
{"query": state["query"], "source": "academic"},
{"query": state["query"], "source": "industry"},
{"query": state["query"], "source": "news"},
]
return [Send("research_worker", task) for task in subtasks]
def research_worker(state: dict):
result = search_by_source(state["query"], state["source"])
return {"research_results": [result]}
def synthesizer(state: ResearchState):
combined = "\n".join(state["research_results"])
synthesis = llm.invoke(f"Synthesize the following research: {combined}")
return {"final_synthesis": synthesis.content}
builder = StateGraph(ResearchState)
builder.add_node("research_worker", research_worker)
builder.add_node("synthesizer", synthesizer)
builder.add_conditional_edges(START, supervisor, ["research_worker"])
builder.add_edge("research_worker", "synthesizer")
builder.add_edge("synthesizer", END)
graph = builder.compile()
パターン三は階層型 Supervisor-Workerです。Supervisor が意図認識とルーティングを担い、Worker が専門サブタスクを実行します。キーワード高速経路 + LLM 精密ルーティングの二層最適化を推奨します。
KEYWORD_ROUTING = {
"code": "code_agent",
"search": "search_agent",
"query": "search_agent",
"data": "data_agent",
}
def supervisor_with_fast_path(state):
query = state["query"].lower()
for keyword, agent_name in KEYWORD_ROUTING.items():
if keyword in query:
return {"next": agent_name}
routing_prompt = f"""
User request: {state['query']}
Available agents: code_agent, search_agent, data_agent
Return only the most suitable agent name.
"""
decision = llm.invoke(routing_prompt)
return {"next": decision.content.strip()}
パターン四は群集協調(Swarm)です。Agent が P2P でタスクを受け渡し、中央調整者はいません。終了ルールで停止します。本番では慎重に使い、ハードなラウンド上限を必ず設定してください。AutoGen の GroupChat + max_round の例です。
import autogen
reviewer_1 = autogen.AssistantAgent(
name="SecurityReviewer",
system_message="You are a security expert focused on vulnerabilities in code."
)
reviewer_2 = autogen.AssistantAgent(
name="PerformanceReviewer",
system_message="You are a performance expert focused on efficiency and resource usage."
)
human_proxy = autogen.UserProxyAgent(
name="CodeAuthor",
human_input_mode="NEVER",
max_consecutive_auto_reply=2,
is_termination_msg=lambda x: "APPROVED" in x.get("content", "")
)
groupchat = autogen.GroupChat(
agents=[human_proxy, reviewer_1, reviewer_2],
messages=[],
max_round=6
)
manager = autogen.GroupChatManager(groupchat=groupchat)
パターン五はブラックボードアーキテクチャです。全 Agent が構造化ワークスペースを共有し、前提条件を満たしたときに読み書きします。時間単位の非同期タスクや異種サービス連携に向きます。パターン六はハイブリッドです。Intent ルーター + Supervisor + 並列ファンアウト + 品質保証パイプラインを組み合わせ、企業向けコンテンツ生成の典型構成となります。
MCP + A2A 二層プロトコル(Linux Foundation Agentic AI Foundation 管轄):MCP は垂直層で Agent ↔ ツール/DB/API を統一し、A2A は水平層で Agent ↔ Agent のタスク委譲と能力発見を標準化します。
from mcp.server import Server
from mcp.types import Tool, TextContent
app = Server("data-agent-mcp")
@app.list_tools()
async def list_tools():
return [
Tool(
name="query_customer_db",
description="Query customer database by id, name, or email",
inputSchema={
"type": "object",
"properties": {
"field": {"type": "string", "enum": ["id", "name", "email"]},
"value": {"type": "string"}
},
"required": ["field", "value"]
}
)
]
@app.call_tool()
async def call_tool(name: str, arguments: dict):
if name == "query_customer_db":
result = db.query(arguments["field"], arguments["value"])
return [TextContent(type="text", text=str(result))]
A2A の Agent Card(/.well-known/agent.json)で能力を宣言し、Orchestrator が JSON-RPC 2.0 でタスクを委譲します。
import httpx
async def discover_and_delegate(agent_url: str, task: str):
card_response = await httpx.get(f"{agent_url}/.well-known/agent.json")
agent_card = card_response.json()
available_skills = [s["id"] for s in agent_card["skills"]]
if "web_research" not in available_skills:
raise ValueError(f"Agent {agent_card['name']} does not support web_research")
payload = {
"jsonrpc": "2.0",
"method": "message/send",
"id": "task-001",
"params": {
"message": {
"role": "user",
"parts": [{"type": "text", "text": task}]
}
}
}
response = await httpx.post(agent_card["url"], json=payload)
return response.json()
関連記事:ツール接続の詳細はゼロから MCP Server を開発する、プロトコル戦略の全体像はMCP が AI 時代の HTTP となる理由を併読してください。
本番級マルチエージェントシステム:六ステップ Runbook
選定とトポロジ設計:意思決定ツリーでオーケストレーションパターン(パイプライン/ファンアウト/階層/ブラックボード/ハイブリッド)を確定します。Agent 数は 3〜8 個に抑え、過剰設計を避けます。
状態永続化:PostgresSaver でチェックポイントを保存し、プロセス跨ぎの復旧と中断再開を可能にします。各セッションに thread_id を紐づけます。
Human-in-the-Loop:高リスク操作は interrupt() で一時停止し、人手確認を待ちます。コンプライアンス業界では必須です。
サーキットブレーカーとリトライ:外部 Agent 呼び出しを CircuitBreaker で包み、失敗閾値で OPEN 状態に遷移させ、連鎖障害を防ぎます。
Token 予算:TokenBudgetManager を導入し、各 Agent 呼び出し前に残予算を検証して、コスト暴走を防ぎます。
可観測性の接続:OpenTelemetry の traced_agent_call に correlation_id を付与し、MONITORING_METRICS を追跡します。LLM-as-Judge で出力品質をサンプリング評価します。
from langgraph.checkpoint.postgres import PostgresSaver
from langgraph.types import interrupt
with PostgresSaver.from_conn_string("postgresql://user:pass@localhost/agentdb") as checkpointer:
graph = builder.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "user-session-12345"}}
result = graph.invoke({"query": "Analyze Q2 earnings"}, config)
def high_risk_action_agent(state):
proposed_action = plan_action(state)
human_decision = interrupt({
"proposed_action": proposed_action,
"risk_level": "HIGH",
"message": "This operation will modify the production database. Please confirm."
})
if human_decision["approved"]:
return execute_action(proposed_action)
return {"status": "cancelled", "reason": human_decision.get("reason")}
import time
from functools import wraps
class CircuitBreaker:
def __init__(self, failure_threshold=5, recovery_timeout=60):
self.failure_count = 0
self.failure_threshold = failure_threshold
self.recovery_timeout = recovery_timeout
self.state = "CLOSED"
self.last_failure_time = None
def __call__(self, func):
@wraps(func)
async def wrapper(*args, **kwargs):
if self.state == "OPEN":
if time.time() - self.last_failure_time > self.recovery_timeout:
self.state = "HALF_OPEN"
else:
raise Exception("Circuit breaker OPEN - agent temporarily unavailable")
try:
result = await func(*args, **kwargs)
if self.state == "HALF_OPEN":
self.state = "CLOSED"
self.failure_count = 0
return result
except Exception:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
raise
return wrapper
class TokenBudgetManager:
def __init__(self, total_budget: int = 100_000):
self.total_budget = total_budget
self.used_tokens = 0
self.agent_usage = {}
def check_budget(self, agent_name: str, estimated_tokens: int) -> bool:
remaining = self.total_budget - self.used_tokens
if estimated_tokens > remaining:
raise BudgetExceededException(
f"Agent {agent_name} requested {estimated_tokens} tokens, "
f"but only {remaining} tokens remain"
)
return True
def record_usage(self, agent_name: str, actual_tokens: int):
self.used_tokens += actual_tokens
self.agent_usage[agent_name] = self.agent_usage.get(agent_name, 0) + actual_tokens
可観測性の実データ、五大落とし穴と 2026 年の動向
MAST 研究チームが 1,642 件のマルチエージェント実行トレースを分析した結果、故障分布は次のとおりです。
| 故障タイプ | 割合 | 内容 |
|---|---|---|
| システム設計の問題 | 41.77% | ステップ重複、ツール選択ミス、コンテキスト溢れ、終了条件の欠如 |
| Agent 間の不整合 | 36.94% | 引き継ぎ時のコンテキスト喪失、幻覚が次 Agent の「事実」になる |
| タスク検証の失敗 | 21.30% | 早期終了、不完全な検証 |
57% vs 8%:組織の 57% が本番で Agent を稼働させていますが、LLM 可観測性を実装完了したのは 8% のみです。HTTP 200 でエラーが返り、ダッシュボードは緑でも出力は誤っている、という状態が散見されます。
Google Bake-Off 6 倍:分散マルチエージェント構成で処理時間が 1 時間から 10 分へ短縮され、子 Agent を独立アップグレードできます。
AdaptOrch 12〜23%:適切なオーケストレーショントポロジは SWE-bench などで 12〜23% の性能向上をもたらし、モデル差し替え単独より効果が大きい場合があります。
from opentelemetry import trace
import uuid
tracer = trace.get_tracer("multi-agent-system")
def traced_agent_call(agent_name: str, task: dict, correlation_id: str = None):
if not correlation_id:
correlation_id = str(uuid.uuid4())
with tracer.start_as_current_span(f"agent.{agent_name}") as span:
span.set_attribute("agent.name", agent_name)
span.set_attribute("correlation.id", correlation_id)
span.set_attribute("task.type", task.get("type", "unknown"))
try:
result = agent_registry[agent_name].run(task)
span.set_attribute("agent.tokens_used", result.get("tokens", 0))
span.set_attribute("agent.status", "success")
return result
except Exception as e:
span.set_attribute("agent.status", "error")
span.set_attribute("error.message", str(e))
raise
MONITORING_METRICS = {
"task_success_rate": "End-to-end task completion rate (target: >85%)",
"e2e_latency_p95": "P95 end-to-end latency (target: <30s)",
"total_cost_per_task": "Average token cost per task",
"agent_error_rate": "Per-agent error rate (target: <5%)",
"agent_retry_count": "Retry count (high retries = investigate)",
"tool_call_budget_usage": "Tool call count vs budget ratio",
"output_quality_score": "Output quality score",
"goal_alignment_score": "Goal alignment score",
"hallucination_rate": "Hallucination detection rate",
}
def evaluate_agent_output(original_task: str, agent_output: str) -> dict:
evaluation_prompt = f"""
Original task: {original_task}
Agent output: {agent_output}
Score completeness, accuracy, relevance, and hallucination (1-5).
Return JSON: {{"completeness": x, "accuracy": x, "relevance": x,
"hallucination_detected": true/false, "comments": "..."}}
"""
evaluation = llm.invoke(evaluation_prompt)
return json.loads(evaluation.content)
五大落とし穴と対策:
コンテキスト汚染:Agent A の幻覚が B/C に伝播し、誤った前提でワークフロー全体が進みます。対策:各引き継ぎ点で validate_agent_output により Schema と信頼度を検証します。
無限ループ:リトライやツール呼び出しが制御不能になり、Token コストが急増します。対策:MAX_ITERATIONS、MAX_TOOL_CALLS_PER_AGENT、MAX_TOTAL_TOKENS のハード上限を設定します。
過剰設計:二段の処理を八つの Agent に分割してしまいます。対策:まず順次パイプラインから始め、本番の最適数は 3〜8 個です。
デモと本番の断絶:社内デモは綺麗でも、エッジ入力で頻繁に失敗します。対策:ProductionGuardrails で入力長、インジェクション検知、PII フィルタを実装します。
並列同期の欠如:Send API でファンアウト後、遅い分岐が完了する前に Supervisor が再実行されます。対策:LangGraph の defer=True で明示的な同期バリアを作ります。
def validate_agent_output(output: dict, schema: dict) -> bool:
jsonschema.validate(output, schema)
if output.get("confidence_score", 1.0) < 0.7:
raise LowConfidenceError(f"Agent output confidence too low: {output['confidence_score']}")
required_fields = schema.get("required", [])
missing = [f for f in required_fields if not output.get(f)]
if missing:
raise MissingFieldsError(f"Missing required fields: {missing}")
return True
MAX_ITERATIONS = 10
MAX_TOOL_CALLS_PER_AGENT = 20
MAX_TOTAL_TOKENS = 50_000
class ProductionGuardrails:
def validate_input(self, user_input: str) -> str:
if len(user_input) > 10000:
raise InputTooLongError("Input exceeds 10000 character limit")
injection_patterns = ["ignore previous instructions", "forget everything"]
for pattern in injection_patterns:
if pattern.lower() in user_input.lower():
raise PromptInjectionError("Potential prompt injection detected")
return user_input.strip()
def validate_output(self, output: str) -> str:
output = self.pii_filter.redact(output)
if self.content_classifier.is_harmful(output):
raise HarmfulContentError("Output contains harmful content")
return output
builder.add_node("supervisor", supervisor_node, defer=True)
タスクに明確な直列依存ステップがありますか?
├─ はい → サブタスクは並列実行できますか?
│ ├─ いいえ → 【順次パイプライン】
│ └─ はい → 【並列ファンアウト + 順次パイプラインのハイブリッド】
└─ いいえ → 意思決定権を持つ Agent はいますか?
├─ はい → サブチームが必要な規模ですか?
│ ├─ いいえ → 【Supervisor-Worker 階層パターン】
│ └─ はい → 【階層型(Supervisors of Supervisors)】
└─ いいえ → 長時間の非同期タスクですか?
├─ はい → 【ブラックボードアーキテクチャ】
└─ いいえ → Agent 数は 5 以下ですか?
├─ はい → 【Swarm(終了条件を必ず設定)】
└─ いいえ → 【階層パターンへの再分割を検討】
五つの核心要点:① オーケストレーショントポロジ > モデル選択、② 順次パイプラインから始め、根拠があってから Agent を増やす、③ MCP + A2A は新規プロジェクトの標準プロトコル、④ 可観測性はオプションではない(57% vs 8% のギャップ)、⑤ 本番の最適 Agent 数は 3〜8 個です。
2026 年の動向:連邦型オーケストレーション(複数チームのサブオーケストレーターがルーティング戦略を共有)、マルチモーダルマルチエージェント(視覚・音声とテキストの混合協調)、適応型トポロジ選択(AdaptOrch 方向)、EU AI Act による完全な意思決定監査チェーンの義務化が進んでいます。
注意:ノート PC でマルチエージェントを動かすと、スリープによる切断、メモリ不足、ネットワークジッターに直面します。安価な Linux VPS では Xcode や Apple Silicon 最適化推論をネイティブ実行できません。マルチエージェントアーキテクチャは 2026 年 AI エンジニアリングの中核ですが、本番には安定したホストが必要です。
7×24 のマルチエージェント常駐、PostgresSaver チェックポイント永続化、MCP Server と Cursor / Claude Code の協調が必要な本番環境では、MESHLAUNCH の Mac Mini クラウドレンタルが最適解となることが多いです。専有 Apple Silicon、日/週/月の柔軟契約で、LangGraph ワークフローとローカルベクトル検索の安定ホストとして、オーケストレーション資産をベンダー依存からチーム自有过の移植可能な能力へ移行できます。見積もりは料金ページ、デプロイ支援はヘルプセンターをご覧ください。
複雑な状態管理、HITL、コンプライアンス監査が必要なら LangGraph を選びます。1〜2 日でプロトタイプを作り、役割制コンテンツパイプラインを回すなら CrewAI が向きます。Microsoft / Azure スタックで多輪の議論型協調が必要なら AutoGen を検討してください。マルチエージェントの常駐ホストは料金ページをご覧ください。
MCP は垂直層で、Agent がツール・データベース・API にアクセスする経路を統一します(一度実装すればどこでも使えます)。A2A は水平層で、Agent Card と JSON-RPC 2.0 によりエージェント間のタスク委譲と能力発見を標準化します。両者は Linux Foundation Agentic AI Foundation がガバナンスを担っており、新規プロジェクトでは二層アーキテクチャの採用を推奨します。
最低限、PostgreSQL チェックポイント永続化(PostgresSaver)、OpenTelemetry 分散トレース、Token 予算管理、サーキットブレーカー、そしてスリープしない 7×24 ホストが必要です。Mac Mini 裸機では LangGraph ワークフロー、MCP Server、ローカルベクトル検索を同一ノードで稼働できます。デプロイとネットワークの詳細はヘルプセンターをご覧ください。