Elasticsearch에서 A2A 프로토콜과 MCP를 사용하여 LLM 에이전트 뉴스룸 만들기: 2부
에이전트 협업을 위한 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: 작동 중인 코드
이 글은 동일한 에이전트 내에서 A2A와 MCP 아키텍처를 모두 구현하여 두 프레임워크의 고유한 이점을 제대로 활용할 수 있는 이점을 설명한 "Elasticsearch에서 A2A 프로토콜과 MCP를 사용하여 LLM 에이전트 뉴스룸 만들기!" 글의 후속 글입니다. 직접 데모를 실행하려는 경우 저장소를 사용할 수 있습니다.
뉴스룸 에이전트가 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 에이전트를 사용하며, 이 에이전트는 다시 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 에이전트(뉴스룸 전용 에이전트)는 Elastic의 A2A 에이전트(타사 전문가)와 협력하여 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단계: 기자가 기사 작성
리포터 에이전트는 리서치 데이터와 자체 LLM 기능을 사용하여 기사를 작성합니다. 글을 작성하는 동안 리포터는 스타일과 템플릿을 위해 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_check8단계: 낮은 신뢰도로 인한 재조사 트리거
리포터 에이전트가 초안을 평가한 결과 한 클레임의 신뢰도가 낮다는 것을 발견했습니다. 연구원 에이전트에게 또 다른 요청을 보냅니다:
{
"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 워크플로우의 전체 시퀀스입니다.
# | 에서 | To | 액션 | 프로토콜 | 설명 |
|---|---|---|---|---|---|
1 | 사용자 | 뉴스 수석 | 스토리 할당 | HTTP POST | 사용자가 스토리 주제와 각도를 제출합니다. |
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 | 뉴스_아카이브 인덱스 쿼리 |
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 | 기사를 뉴스_아카이브 인덱스에 색인화 |
27 | 게시자 | 파일 시스템 | 마크다운 저장 | 파일 I/O | .md로 문서 저장 아티클의 파일 |
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




