本記事は MARA: Multi-Agent RAG Architecture (arXiv:2504.04603) の解説記事です。
論文概要(Abstract)
MARAは、従来のsingle-agent RAGが抱えるスケーラビリティ・特化不足・並列処理不能の3課題に対し、Triage Agent・RAG Agent・Web Search Agentの3種の専門エージェントをLangGraphのstate machineで協調させるマルチエージェントフレームワークである。著者らは、複雑なクエリを動的にサブタスクに分解し、各エージェントの専門能力を活かした並列処理によって、multi-hopベンチマークで15-22%の精度向上と30%の処理時間削減を報告している。
この記事は Zenn記事: LangGraph×Claude 4.6で推論精度と応答速度を両立するマルチエージェントRAG の深掘りです。
情報源
- arXiv ID: 2504.04603
- URL: https://arxiv.org/abs/2504.04603
- 著者: Aditi Singh, Abul Ehtesham, Saket Kumar, Tala Talaei Khoei
- 発表年: 2025
- 分野: cs.AI, cs.CL, cs.IR
背景と動機(Background & Motivation)
Retrieval-Augmented Generation(RAG)は知識集約型タスクの標準的アプローチとなっているが、著者らは従来のsingle-agent RAGにおける3つの構造的限界を指摘している。
1つ目はスケーラビリティの欠如である。クエリの複雑度が上がるにつれて、1つのエージェントがすべての検索・推論・生成を担当する設計では、処理パイプラインが直列的にボトルネックとなる。2つ目は特化の不足で、FAQ的な単純クエリとmulti-hop推論を要する複雑クエリを同一のプロンプト・ツール構成で処理するため、どちらも最適化できない。3つ目は並列処理の不能で、複数のデータソースからの情報取得が逐次実行となり、レイテンシが加算される。
これらの課題は、Zenn記事で解説しているLangGraph functional APIのfan-outパターン(並列ノード実行)やCommand primitiveによる動的ルーティングが解決を目指す問題と一致する。MARAはこれらの設計パターンの学術的な検証を提供している。
主要な貢献(Key Contributions)
著者らは以下の4点を主要な貢献として挙げている。
- 貢献1: LangGraphのstate machineに基づく、3種の専門エージェント(Triage / RAG / Web Search)によるマルチエージェントRAGアーキテクチャの提案
- 貢献2: クエリの複雑度に応じた動的ルーティングと並列処理を実現するオーケストレーションフレームワーク
- 貢献3: multi-hop QA・open-domain QA・real-time QAの3カテゴリにおけるsingle-agent RAG比での定量的改善の実証
- 貢献4: マルチエージェントシステムの計算コストオーバーヘッドと性能向上のトレードオフ分析
技術的詳細(Technical Details)
アーキテクチャ: 3エージェント協調設計
MARAの中核は、以下3種の専門エージェントとLangGraphによるオーケストレーション層で構成される。
Triage Agent(トリアージエージェント): 入力クエリを分析し、処理戦略を決定する。具体的には、(a)クエリを独立したサブタスクに分解し、(b)各サブタスクを適切な専門エージェントにルーティングし、(c)並列処理が有益か逐次処理が必要かを判断する。Zenn記事のrouter_agentに相当する役割である。
RAG Agent(検索エージェント): 構造化された知識ベースや文書コレクションからの検索を担当する。dense vector search、sparse retrieval、hybrid approachを使い分け、ローカルナレッジベースから関連情報を取得する。
Web Search Agent(Web検索エージェント): ローカル知識ベースでは不十分な場合や最新情報が必要な場合に、検索APIを用いてWebから情報を取得・統合する。
LangGraphによるState Machineオーケストレーション
著者らはLangGraphのstate machine frameworkを用いてエージェント間の協調を実装している。論文で述べられている設計の要点は以下の通りである。
- 共有状態の管理: エージェント間でステートグラフの状態を共有し、各エージェントの出力が後続エージェントの入力として利用可能
- 並列実行の制御: Triage Agentが独立と判定したサブタスクについて、RAG AgentとWeb Search Agentを同時に実行
- エラーリカバリ: 個別エージェントの失敗時のフォールバック戦略を状態遷移として定義
- 同期・非同期実行: 両方の実行パターンをサポートし、ユースケースに応じた使い分けが可能
この設計はZenn記事で紹介しているCommand primitiveのgotoパラメータによる動的遷移と同じ概念をstate machineレベルで実現している。
並列検索のアーキテクチャ
MARAの並列処理は、Zenn記事のfan-out/fan-inパターンと対応する。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from langgraph.func import entrypoint, task
@task
async def rag_retrieval(query: str, knowledge_base) -> list[dict]:
"""ローカル知識ベースからの検索(dense + sparse hybrid)
Args:
query: 検索クエリ
knowledge_base: 検索対象のナレッジベース
Returns:
検索結果のリスト(content, metadata, score)
"""
dense_results = await knowledge_base.dense_search(query, k=5)
sparse_results = await knowledge_base.sparse_search(query, k=5)
return merge_and_deduplicate(dense_results, sparse_results)
@task
async def web_retrieval(query: str) -> list[dict]:
"""Web検索APIを使用したリアルタイム情報取得
Args:
query: 検索クエリ
Returns:
Web検索結果のリスト(title, url, snippet)
"""
results = await web_search_api.search(query, max_results=5)
return [{"content": r.snippet, "url": r.url} for r in results]
@entrypoint(checkpointer=checkpointer)
async def mara_pipeline(query: str) -> str:
"""MARAスタイルの並列検索パイプライン"""
# Triage: クエリ分析とルーティング判定
triage_result = await triage_agent(query)
if triage_result["strategy"] == "parallel":
# fan-out: RAG検索とWeb検索を並列実行
rag_future = rag_retrieval(query, knowledge_base)
web_future = web_retrieval(query)
# fan-in: 全結果を集約
rag_docs = await rag_future
web_docs = await web_future
all_docs = rag_docs + web_docs
elif triage_result["strategy"] == "rag_only":
all_docs = await rag_retrieval(query, knowledge_base)
else:
all_docs = await web_retrieval(query)
# 統合・生成
return await generate_answer(query, all_docs)
このコードは論文のアーキテクチャをLangGraph functional APIで再現したものである。Triage Agentの判定結果に基づき、独立したサブタスクを並列実行し、結果を集約する。
実装のポイント(Implementation)
Triage Agentのルーティング精度が全体性能を決定する
著者らの分析によると、Triage Agentのルーティング精度がシステム全体の性能に最も大きく影響する。適切なエージェントにルーティングできなければ、専門化の恩恵は得られない。
実装上の注意点は以下の通りである。
- ルーティングロジックの設計: 著者らはrule-basedとLLM-basedのルーティングを検討しており、LLM-basedの方が柔軟性は高いがレイテンシが増加する。Zenn記事のeffortルーティング(classify_query関数)と同じトレードオフが存在する
- 並列実行時のレート制限: fan-outで同時リクエスト数がAPIの上限を超過しうるため、
max_concurrencyの設定が必要 - coordination overhead: マルチエージェント間の状態管理・メッセージパッシングにより、一部の単純クエリではsingle-agent RAGよりもレイテンシが増加する可能性がある
JSON-serializable制約
LangGraphのstate machineでは、エージェント間で共有される状態はJSON-serializableである必要がある。LangChainのDocumentオブジェクト等を直接渡すとシリアライズエラーが発生するため、必ずdictに変換して返す設計が求められる。
1
2
3
4
5
6
# NG: Document オブジェクトを直接返す
# return [Document(page_content="...", metadata={...})]
# OK: dict に変換して返す
return [{"content": doc.page_content, "metadata": doc.metadata}
for doc in results]
Production Deployment Guide
AWS実装パターン(コスト最適化重視)
MARAアーキテクチャのAWSデプロイでは、3種のエージェントを個別にスケーリングできる構成が有効である。
トラフィック量別の推奨構成:
| 規模 | 月間リクエスト | 推奨構成 | 月額コスト | 主要サービス |
|---|---|---|---|---|
| Small | ~3,000 (100/日) | Serverless | $50-150 | Lambda + Bedrock + DynamoDB |
| Medium | ~30,000 (1,000/日) | Hybrid | $300-800 | Lambda + ECS Fargate + ElastiCache |
| Large | 300,000+ (10,000/日) | Container | $2,000-5,000 | EKS + Karpenter + EC2 Spot |
Small構成の詳細(月額$50-150):
- Lambda: Triage Agent用(1GB RAM, 30秒タイムアウト, $20/月)
- Bedrock: Claude 3.5 Haiku, Prompt Caching有効($80/月)
- DynamoDB: 検索結果キャッシュ, On-Demand($10/月)
- CloudWatch: 基本監視($5/月)
Medium構成の詳細(月額$300-800):
- Lambda: Triage Agent($50/月)
- ECS Fargate: RAG Agent + Web Search Agent, 0.5 vCPU × 2タスク($120/月)
- Bedrock: Claude 3.5 Sonnet, Batch API活用($400/月)
- ElastiCache Redis: 検索結果キャッシュ, cache.t3.micro($15/月)
コスト削減テクニック:
- Spot Instances使用で最大90%削減(EKS + Karpenter)
- Bedrock Batch API使用で50%削減(非リアルタイム処理)
- Prompt Caching有効化で30-90%削減(Triage Agent用システムプロンプト固定)
コスト試算の注意事項: 上記は2026年2月時点のAWS ap-northeast-1(東京)リージョン料金に基づく概算値です。実際のコストはトラフィックパターン、リージョン、バースト使用量により変動します。最新料金は AWS料金計算ツール で確認してください。
Terraformインフラコード
Small構成(Serverless): Lambda + Bedrock + DynamoDB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# --- IAMロール(最小権限) ---
resource "aws_iam_role" "triage_lambda" {
name = "mara-triage-lambda-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "lambda.amazonaws.com" }
}]
})
}
resource "aws_iam_role_policy" "bedrock_invoke" {
role = aws_iam_role.triage_lambda.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Effect = "Allow"
Action = ["bedrock:InvokeModel", "bedrock:InvokeModelWithResponseStream"]
Resource = "arn:aws:bedrock:ap-northeast-1::foundation-model/anthropic.claude-3-5-haiku*"
}]
})
}
# --- Lambda: Triage Agent ---
resource "aws_lambda_function" "triage_agent" {
filename = "triage_agent.zip"
function_name = "mara-triage-agent"
role = aws_iam_role.triage_lambda.arn
handler = "index.handler"
runtime = "python3.12"
timeout = 30
memory_size = 1024
environment {
variables = {
BEDROCK_MODEL_ID = "anthropic.claude-3-5-haiku-20241022-v1:0"
DYNAMODB_TABLE = aws_dynamodb_table.cache.name
}
}
}
# --- DynamoDB: 検索結果キャッシュ ---
resource "aws_dynamodb_table" "cache" {
name = "mara-retrieval-cache"
billing_mode = "PAY_PER_REQUEST"
hash_key = "query_hash"
attribute {
name = "query_hash"
type = "S"
}
ttl {
attribute_name = "expire_at"
enabled = true
}
}
Large構成(Container): EKS + Karpenter + Spot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 20.0"
cluster_name = "mara-cluster"
cluster_version = "1.31"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
cluster_endpoint_public_access = true
enable_cluster_creator_admin_permissions = true
}
# Karpenter: Spot優先の自動スケーリング
resource "kubectl_manifest" "karpenter_provisioner" {
yaml_body = <<-YAML
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
name: mara-spot
spec:
requirements:
- key: karpenter.sh/capacity-type
operator: In
values: ["spot"]
- key: node.kubernetes.io/instance-type
operator: In
values: ["m6i.xlarge", "m6i.2xlarge"]
limits:
resources:
cpu: "32"
memory: "128Gi"
ttlSecondsAfterEmpty: 30
YAML
}
運用・監視設定
CloudWatch Logs Insights: エージェント別レイテンシ分析
1
2
3
4
5
6
fields @timestamp, agent_name, duration_ms, query_type
| stats avg(duration_ms) as avg_latency,
pct(duration_ms, 95) as p95,
pct(duration_ms, 99) as p99
by agent_name, bin(5m)
| filter agent_name in ["triage", "rag", "web_search"]
CloudWatch アラーム(コスト監視):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import boto3
cloudwatch = boto3.client('cloudwatch')
cloudwatch.put_metric_alarm(
AlarmName='mara-bedrock-token-spike',
ComparisonOperator='GreaterThanThreshold',
EvaluationPeriods=1,
MetricName='TokenUsage',
Namespace='AWS/Bedrock',
Period=3600,
Statistic='Sum',
Threshold=500000,
AlarmDescription='Bedrockトークン使用量異常'
)
コスト最適化チェックリスト
- ~100 req/日 → Lambda + Bedrock (Serverless)
- ~1000 req/日 → ECS Fargate + Bedrock (Hybrid)
- 10000+ req/日 → EKS + Spot (Container)
- EC2: Spot Instances優先(Karpenter自動管理)
- Bedrock Batch API使用で50%削減
- Prompt Caching有効化で30-90%削減
- Lambda: メモリサイズ最適化
- ECS/EKS: アイドル時スケールダウン
- AWS Budgets: 月額予算設定(80%で警告)
- CloudWatch: トークン使用量スパイク検知
- Cost Anomaly Detection: 自動異常検知
- 日次コストレポート: SNS/Slackへ送信
- タグ戦略: 環境別(dev/staging/prod)
- S3ライフサイクル: キャッシュ30日自動削除
- 開発環境: 夜間停止(Auto Start/Stop)
実験結果(Results)
著者らは以下の3カテゴリのベンチマークで評価を行っている。
ベンチマーク結果:
| ベンチマーク | カテゴリ | single-agent RAG | MARA | 改善率 |
|---|---|---|---|---|
| HotpotQA | multi-hop QA | ベースライン | +15-22% | accuracy |
| MuSiQue | multi-hop QA | ベースライン | +15-22% | accuracy |
| Natural Questions | open-domain QA | ベースライン | 改善 | relevance |
| TriviaQA | open-domain QA | ベースライン | 改善 | relevance |
| Real-time QA | real-time | ベースライン | 改善 | accuracy |
(出典: 論文Section Experimental Results)
著者らの報告による主要な知見は以下の通りである。
- 並列処理の効果: 独立したサブタスクの並列実行により、合計処理時間が約30%削減された
- 専門化の効果: ドメイン固有のクエリに対して、専門エージェントが非専門エージェントよりも高い検索品質を達成
- Triage Agentの重要性: ルーティングの正確さがシステム全体の性能に最も大きく寄与する
- LangGraphの有用性: state machineによる明示的な状態遷移定義がデバッグと透明性を向上させる
制約: 著者らは、一部のクエリではcoordination overheadによりsingle-agent RAGよりもレイテンシが増加しうることを述べている。また、評価は英語ベンチマークに限定されており、多言語環境への適用は未検証である。
実運用への応用(Practical Applications)
MARAのアーキテクチャは、Zenn記事で解説しているLangGraph functional APIの@task/@entrypointパターンで実装可能である。
プロダクション視点での適用先:
- 社内ナレッジベース検索: RAG Agentがドキュメントを検索し、Web Search Agentが最新情報を補完するハイブリッド構成
- カスタマーサポート: Triage Agentがクエリを分類し、FAQ(RAG Agent)と問い合わせ(Web Search Agent)を適切に振り分け
- リサーチアシスタント: 論文(RAG Agent)とWeb上の最新情報(Web Search Agent)を並列取得し、統合的な回答を生成
運用上の考慮事項: 著者らの実験では並列実行で30%のレイテンシ削減を報告しているが、coordination overheadが存在するため、クエリの大半が単純な場合はsingle-agent構成の方が効率的である可能性がある。
関連研究(Related Work)
- Agentic RAG Survey (Singh et al., 2025, arXiv:2501.09136): single-agent / multi-agent / hierarchicalの3パターンを分類したサーベイ。MARAはmulti-agentパターンの具体的実装と位置付けられる
- AgenticRAG (Kosba et al., 2025, arXiv:2501.15228): 5種のエージェント(Query Decomposition / Retrieval / Web Search / Synthesis / Verification)を用いたマルチエージェントRAG。MARAは3エージェントで簡潔さを優先した設計
- Adaptive-RAG (Jeong et al., 2024, arXiv:2404.19756): クエリ複雑度に基づく検索戦略の適応的選択。MARAのTriage Agentの設計思想と共通する
まとめと今後の展望
MARAは、LangGraphのstate machineを活用したマルチエージェントRAGの実装と評価を提供する論文である。multi-hop QAで15-22%の精度向上、並列実行で30%の処理時間削減を著者らは報告している。
Zenn記事で解説しているfunctional APIのfan-outパターンやCommand primitiveによる動的ルーティングは、MARAが検証したアーキテクチャパターンをAPIレベルで実現するアプローチと位置付けられる。特にTriage Agentのルーティング精度が全体性能を決定するという知見は、effortルーティングの分類器設計においても重要な示唆である。
ただし、coordination overheadによるレイテンシ増加や英語ベンチマーク限定の評価など、本番環境への適用時には追加検証が必要である。
参考文献
- arXiv: https://arxiv.org/abs/2504.04603
- Related Zenn article: https://zenn.dev/0h_n0/articles/92f41b4fdc7b49
- LangGraph Documentation: https://docs.langchain.com/oss/python/langgraph/
:::message 本記事はAI(Claude Code)により自動生成されました。内容は論文に基づいていますが、実際の利用時は原論文もご確認ください。 :::