Elasticsearch で A2A プロトコルと MCP を使用して LLM エージェント ニュースルームを作成する: パート II
エージェントのコラボレーションに A2A プロトコルを使用し、Elasticsearch でのツール アクセスに MCP を使用して、特殊なハイブリッド LLM エージェント ニュースルームを構築する方法を説明します。
Agent Builder is available now GA. Get started with an Elastic Cloud Trial, and check out the documentation for Agent Builder here.
A2AとMCP:コードの動作
これは、記事「Elasticsearch で A2A プロトコルと MCP を使用して LLM エージェント ニュースルームを作成する」の補足記事です。この記事では、同じエージェント内に A2A と MCP の両方のアーキテクチャを実装して、両方のフレームワークの独自のメリットを最大限に活用するメリットについて説明しました。自分でデモを実行したい場合、リポジトリが利用可能です。
ニュースルームのエージェントが A2A と MCP の両方を使用して協力し、ニュース記事を作成する方法を見ていきましょう。エージェントの動作を確認するための付属リポジトリは、ここにあります。
ステップ1:ストーリーの割り当て
ニュースチーフ(クライアントとして行動)がストーリーを割り当てます。
{
"message_type": "task_request",
"sender": "news_chief",
"receiver": "reporter_agent",
"payload": {
"task_id": "story_renewable_energy_2024",
"assignment": {
"topic": "Renewable Energy Adoption in Europe",
"angle": "Policy changes driving solar and wind expansion",
"target_length": 1200,
"deadline": "2025-09-30T18:00:00Z"
}
}
}ステップ2: 記者が調査を依頼する
レポーター エージェントは背景情報が必要であることを認識し、A2A を介してリサーチャー エージェントに委任します。
{
"message_type": "task_request",
"sender": "reporter_agent",
"receiver": "researcher_agent",
"payload": {
"task_id": "research_eu_renewable_2024",
"parent_task_id": "story_renewable_energy_2024",
"capability": "fact_gathering",
"parameters": {
"queries": [
"EU renewable energy capacity 2024",
"Solar installations growth Europe",
"Wind energy policy changes 2024"
],
"depth": "comprehensive"
}
}
}ステップ3: 報告者がアーカイブエージェントに歴史的背景をリクエストする
レポーターエージェントは、歴史的背景が記事の内容を強めることを認識しています。A2A 経由でアーカイブエージェント( Elastic の A2A エージェントを搭載) に委任し、ニュースルームの Elasticsearch 搭載記事アーカイブを検索します。
{
"message_type": "task_request",
"sender": "reporter_agent",
"receiver": "archive_agent",
"payload": {
"task_id": "archive_search_renewable_2024",
"parent_task_id": "story_renewable_energy_2024",
"capability": "search_archive",
"parameters": {
"query": "European renewable energy policy changes and adoption trends over past 5 years",
"focus_areas": ["solar", "wind", "policy", "Germany", "France"],
"time_range": "2019-2024",
"result_count": 10
}
}
}ステップ4: アーカイブエージェントはMCPでElastic A2Aエージェントを使用する
アーカイブ エージェントはElastic の A2A エージェントを使用し、A2A エージェントは MCP を使用して Elasticsearch ツールにアクセスします。これは、A2A がエージェントのコラボレーションを可能にし、MCP がツール アクセスを提供するハイブリッド アーキテクチャを示しています。
# Archive Agent using Elastic A2A Agent
async def search_historical_articles(self, query_params):
# The Archive Agent sends a request to Elastic's A2A Agent
elastic_response = await self.a2a_client.send_request(
agent="elastic_agent",
capability="search_and_analyze",
parameters={
"natural_language_query": query_params["query"],
"index_pattern": "newsroom-articles-*",
"filters": {
"topics": query_params["focus_areas"],
"date_range": query_params["time_range"]
},
"analysis_type": "trend_analysis"
}
)
# Elastic's A2A Agent internally uses MCP tools:
# - platform.core.search (to find relevant articles)
# - platform.core.generate_esql (to analyze trends)
# - platform.core.index_explorer (to identify relevant indices)
return elastic_responseアーカイブエージェントはElastic の A2A エージェントから包括的な履歴データを受信し、それをレポーターに返します。
{
"message_type": "task_response",
"sender": "archive_agent",
"receiver": "reporter_agent",
"payload": {
"task_id": "archive_search_renewable_2024",
"status": "completed",
"archive_data": {
"historical_articles": [
{
"title": "Germany's Energiewende: Five Years of Solar Growth",
"published": "2022-06-15",
"key_points": [
"Germany added 7 GW annually 2020-2022",
"Policy subsidies drove 60% of growth"
],
"relevance_score": 0.94
},
{
"title": "France Balances Nuclear and Renewables",
"published": "2023-03-20",
"key_points": [
"France increased renewable target to 40% by 2030",
"Solar capacity doubled 2021-2023"
],
"relevance_score": 0.89
}
],
"trend_analysis": {
"coverage_frequency": "EU renewable stories increased 150% since 2019",
"emerging_themes": ["policy incentives", "grid modernization", "battery storage"],
"coverage_gaps": ["Small member states", "offshore wind permitting"]
},
"total_articles_found": 47,
"search_confidence": 0.91
}
}
}このステップでは、Elastic の A2A エージェントがニュースルームのワークフローにどのように統合されるかを示します。Archive Agent(ニュースルーム固有のエージェント)は、Elastic の A2A Agent(サードパーティの専門家)と連携して、Elasticsearch の強力な検索および分析機能を活用します。Elastic のエージェントは内部的に MCP を使用して Elasticsearch ツールにアクセスし、エージェント調整 (A2A) とツール アクセス (MCP) を明確に分離します。
ステップ5: 研究者はMCPサーバーを使用する
研究者エージェントは複数の MCP サーバーにアクセスして情報を収集します。
# Researcher Agent using MCP to access tools
async def gather_facts(self, queries):
results = []
# Use News API MCP Server
news_data = await self.mcp_client.invoke_tool(
server="news_api_mcp",
tool="search_articles",
parameters={
"query": queries[0],
"date_range": "2024-01-01 to 2024-09-30",
"sources": ["reuters", "bloomberg", "euractiv"]
}
)
# Use Statistics MCP Server
stats_data = await self.mcp_client.invoke_tool(
server="eurostat_mcp",
tool="get_energy_statistics",
parameters={
"metrics": ["solar_capacity_mw", "wind_capacity_mw"],
"countries": ["EU27"],
"year": 2024
}
)
# Use Fact-Checking MCP Server
verified_facts = await self.mcp_client.invoke_tool(
server="factcheck_mcp",
tool="verify_claims",
parameters={
"claims": [
"EU solar capacity increased 40% in 2024",
"Germany leads in wind installations"
]
}
)
return self._synthesize_research(news_data, stats_data, verified_facts)ステップ6: 研究者が報告者にデータを返す
研究者エージェントは、包括的な研究を A2A 経由で返送します。
{
"message_type": "task_response",
"sender": "researcher_agent",
"receiver": "reporter_agent",
"payload": {
"task_id": "research_eu_renewable_2024",
"status": "completed",
"research_data": {
"key_facts": [
{
"claim": "EU solar capacity reached 260 GW in 2024",
"source": "Eurostat Q3 2024 Report",
"confidence": 0.95,
"verified": true
},
{
"claim": "Germany installed 12 GW of wind capacity in 2024",
"source": "German Federal Network Agency",
"confidence": 0.92,
"verified": true
}
],
"statistics": {
"solar_growth_rate": "35%",
"wind_growth_rate": "28%"
},
"sources_count": 15
}
}
}ステップ7:記者が記事を書く
Reporter Agent は、調査データと独自の LLM 機能を使用して記事を作成します。書き込み中、Reporter はスタイルとテンプレートに MCP サーバーを使用します。
# Reporter Agent writing with MCP assistance
async def write_article(self, research_data, assignment):
# Get style guidelines via MCP
style_guide = await self.mcp_client.get_resource(
server="newsroom_mcp",
resource="style://editorial/ap_style_guide"
)
# Get article template via MCP
template = await self.mcp_client.get_resource(
server="newsroom_mcp",
resource="template://articles/news_story"
)
# Generate article using LLM + research + style
draft = await self.llm.generate(
prompt=f"""
Write a news article following these guidelines:
{style_guide}
Using this template:
{template}
Based on this research:
{research_data}
Assignment: {assignment}
"""
)
# Self-evaluate confidence in claims
confidence_check = await self._evaluate_confidence(draft)
return draft, confidence_checkステップ8:自信が低い場合は再調査を促します
レポーター エージェントは下書きを評価し、1 つの主張の信頼性が低いことを発見しました。研究者エージェントに別のリクエストを送信します:
{
"message_type": "collaboration_request",
"sender": "reporter_agent",
"receiver": "researcher_agent",
"payload": {
"request_type": "fact_verification",
"claims": [
{
"text": "France's nuclear phase-down contributed to 15% increase in renewable capacity",
"context": "Discussing policy drivers for renewable growth",
"current_confidence": 0.45,
"required_confidence": 0.80
}
],
"urgency": "high"
}
}研究者はファクトチェックMCPサーバーを使用して主張を検証し、更新された情報を返します。
{
"message_type": "collaboration_response",
"sender": "researcher_agent",
"receiver": "reporter_agent",
"payload": {
"verified_claims": [
{
"original_claim": "France's nuclear phase-down contributed to 15% increase...",
"verified_claim": "France's renewable capacity increased 18% in 2024, partially offsetting reduced nuclear output",
"confidence": 0.88,
"corrections": "Percentage was 18%, not 15%; nuclear phase-down is gradual, not primary driver",
"sources": ["RTE France", "French Energy Ministry Report 2024"]
}
]
}
}ステップ9: 記者が修正して編集者に提出する
記者は検証された事実を組み込み、完成した原稿を A2A 経由で編集者エージェントに送信します。
{
"message_type": "task_request",
"sender": "reporter_agent",
"receiver": "editor_agent",
"payload": {
"task_id": "edit_renewable_story",
"parent_task_id": "story_renewable_energy_2024",
"content": {
"headline": "Europe's Renewable Revolution: Solar and Wind Surge 30% in 2024",
"body": "[Full article text...]",
"word_count": 1185,
"sources": [/* array of sources */]
},
"editing_requirements": {
"check_style": true,
"check_facts": true,
"check_seo": true
}
}
}ステップ10: MCPツールを使用した編集者のレビュー
エディター エージェントは複数の MCP サーバーを使用して記事をレビューします。
# Editor Agent using MCP for quality checks
async def review_article(self, content):
# Grammar and style check
grammar_issues = await self.mcp_client.invoke_tool(
server="grammarly_mcp",
tool="check_document",
parameters={"text": content["body"]}
)
# SEO optimization check
seo_analysis = await self.mcp_client.invoke_tool(
server="seo_mcp",
tool="analyze_content",
parameters={
"headline": content["headline"],
"body": content["body"],
"target_keywords": ["renewable energy", "Europe", "solar", "wind"]
}
)
# Plagiarism check
originality = await self.mcp_client.invoke_tool(
server="plagiarism_mcp",
tool="check_originality",
parameters={"text": content["body"]}
)
# Generate editorial feedback
feedback = await self._generate_feedback(
grammar_issues,
seo_analysis,
originality
)
return feedback編集者は記事を承認し、送信します。
{
"message_type": "task_response",
"sender": "editor_agent",
"receiver": "reporter_agent",
"payload": {
"status": "approved",
"quality_score": 9.2,
"minor_edits": [
"Changed 'surge' to 'increased' in paragraph 3 for AP style consistency",
"Added Oxford comma in list of countries"
],
"approved_content": "[Final edited article]"
}
}ステップ11: パブリッシャーがCI/CD経由でパブリッシュする
最後に、プリンター エージェントは、CMS および CI/CD パイプラインの MCP サーバーを使用して承認された記事を公開します。
# Publisher Agent publishing via MCP
async def publish_article(self, content, metadata):
# Upload to CMS via MCP
cms_result = await self.mcp_client.invoke_tool(
server="wordpress_mcp",
tool="create_post",
parameters={
"title": content["headline"],
"body": content["body"],
"status": "draft",
"categories": metadata["categories"],
"tags": metadata["tags"],
"featured_image_url": metadata["image_url"]
}
)
post_id = cms_result["post_id"]
# Trigger CI/CD deployment via MCP
deploy_result = await self.mcp_client.invoke_tool(
server="cicd_mcp",
tool="trigger_deployment",
parameters={
"pipeline": "publish_article",
"environment": "production",
"post_id": post_id,
"schedule": "immediate"
}
)
# Track analytics
await self.mcp_client.invoke_tool(
server="analytics_mcp",
tool="register_publication",
parameters={
"post_id": post_id,
"publish_time": datetime.now().isoformat(),
"story_id": metadata["story_id"]
}
)
return {
"status": "published",
"post_id": post_id,
"url": f"https://newsroom.example.com/articles/{post_id}",
"deployment_id": deploy_result["deployment_id"]
}出版社はA2Aを通じて出版を確認します。
{
"message_type": "task_complete",
"sender": "printer_agent",
"receiver": "news_chief",
"payload": {
"task_id": "story_renewable_energy_2024",
"status": "published",
"publication": {
"url": "https://newsroom.example.com/articles/renewable-europe-2024",
"published_at": "2025-09-30T17:45:00Z",
"post_id": "12345"
},
"workflow_metrics": {
"total_time_minutes": 45,
"agents_involved": ["reporter", "researcher", "archive", "editor", "printer"],
"iterations": 2,
"mcp_calls": 12
}
}
}以下は、上記と同じエージェントを使用した付属のリポジトリ内の A2A ワークフローの完全なシーケンスです。
# | から | に | アクション | プロトコル | 説明 |
|---|---|---|---|---|---|
1 | ユーザー | ニュースチーフ | ストーリーの割り当て | HTTP ポスト | ユーザーがストーリーのトピックと角度を提出する |
2 | ニュースチーフ | 内部 | ストーリーを作成する | - | 固有のIDを持つストーリーレコードを作成します |
3 | ニュースチーフ | 記者 | 委任の割り当て | A2A | A2Aプロトコル経由でストーリー割り当てを送信します |
4 | 記者 | 内部 | 割り当てを受け入れる | - | 割り当てを内部に保存する |
5 | 記者 | MCP サーバー | アウトラインを生成 | MCP/HTTP | 記事のアウトラインと研究の質問を作成します |
6a | 記者 | 研究者 | 調査依頼 | A2A | 質問を送信します(6bと並行) |
6b | 記者 | アーキビスト | アーカイブを検索 | A2A JSONRPC | 歴史的な記事を検索します(6aと並行) |
7 | 研究者 | MCP サーバー | 研究上の質問 | MCP/HTTP | MCP経由でAnthropicを使用して質問に答えます |
8 | 研究者 | 記者 | リターンリサーチ | A2A | 調査の回答を返す |
9 | アーキビスト | Elasticsearch | 検索インデックス | ES REST API | news_archiveインデックスをクエリ |
10 | アーキビスト | 記者 | アーカイブに戻る | A2A JSONRPC | 過去の検索結果を返します |
11 | 記者 | MCP サーバー | 記事を生成する | MCP/HTTP | 研究/アーカイブの文脈で記事を作成する |
12 | 記者 | 内部 | ストアドラフト | - | 下書きを内部に保存 |
13 | 記者 | ニュースチーフ | 下書きを送信 | A2A | 完成した草稿を提出する |
14 | ニュースチーフ | 内部 | ストーリーを更新 | - | 下書きを保存し、ステータスを「draft_submitted」に更新します |
15 | ニュースチーフ | エディタ | レビュー草稿 | A2A | レビューのために編集者に自動ルーティング |
16 | エディタ | MCP サーバー | 総説 | MCP/HTTP | MCP経由でAnthropicを使用してコンテンツを分析します |
17 | エディタ | ニュースチーフ | 返品レビュー | A2A | 編集上のフィードバックと提案を送信します |
18 | ニュースチーフ | 内部 | ストアレビュー | - | 編集者のフィードバックを保存 |
19 | ニュースチーフ | 記者 | 編集を適用 | A2A | レビューのフィードバックをレポーターに転送する |
20 | 記者 | MCP サーバー | 編集を適用 | MCP/HTTP | フィードバックに基づいて記事を修正する |
21 | 記者 | 内部 | 下書きの更新 | - | 修正を加えて下書きを更新する |
22 | 記者 | ニュースチーフ | 返品修正 | A2A | 修正された記事を返す |
23 | ニュースチーフ | 内部 | ストーリーを更新 | - | 修正した下書きを保存し、ステータスを「修正済み」にする |
24 | ニュースチーフ | 出版社 | 記事を公開する | A2A | パブリッシャーへの自動ルーティング |
25 | 出版社 | MCP サーバー | タグを生成する | MCP/HTTP | タグとカテゴリを作成する |
26 | 出版社 | Elasticsearch | インデックス記事 | ES REST API | 記事をnews_archiveインデックスにインデックスします |
27 | 出版社 | ファイルシステム | マークダウンを保存 | ファイルI/O | 記事を.mdとして保存します/articles内のファイル |
28 | 出版社 | ニュースチーフ | 公開の確認 | A2A | 成功ステータスを返します |
29 | ニュースチーフ | 内部 | ストーリーを更新 | - | ストーリーのステータスを「公開済み」に更新します |
まとめ
A2A と MCP はどちらも、現代の拡張 LLM インフラストラクチャ パラダイムにおいて重要な役割を果たします。A2A は複雑なマルチエージェント システムに柔軟性を提供しますが、移植性が低くなり、運用が複雑になる可能性があります。MCP は、マルチエージェント オーケストレーションを処理するようには設計されていませんが、実装と保守がより簡単なツール統合のための標準化されたアプローチを提供します。
選択は二者択一ではありません。私たちのニュースルームの例で示されているように、最も洗練され効果的な LLM 対応システムは、多くの場合、両方のアプローチを組み合わせています。つまり、エージェントは A2A プロトコルを通じて調整と専門化を行いながら、MCP サーバーを通じてツールやリソースにアクセスします。このハイブリッド アーキテクチャは、MCP の標準化とエコシステムの利点に加えて、マルチエージェント システムの組織上の利点も提供します。これは、選択する必要が全くないかもしれないことを示唆している。単に両方を標準的なアプローチとして使うだけでよい。
開発者またはアーキテクトとして、両方のソリューションの最適な組み合わせをテストして決定し、特定のユースケースに適した結果を生み出すのはあなた次第です。それぞれのアプローチの長所、制限、適切な適用を理解することで、より効果的で保守性と拡張性に優れた AI システムを構築できるようになります。
デジタル ニュースルーム、顧客サービス プラットフォーム、リサーチ アシスタント、またはその他の LLM を利用したアプリケーションを構築する場合でも、調整ニーズ (A2A) とツール アクセス要件 (MCP) を慎重に検討することで、成功への道が開かれます。
参考資料
Elasticsearch エージェントビルダー: https://www.elastic.co/docs/solutions/search/elastic-agent-builder
A2A と MCP の統合: https://a2a-protocol.org/latest/topics/a2a-and-mcp/
モデルコンテキストプロトコル: https://modelcontextprotocol.io




