블로그

Elasticsearch에서 A2A 프로토콜과 MCP를 사용하여 LLM 에이전트 뉴스룸 만들기: 2부

에이전트 협업을 위한 A2A 프로토콜과 Elasticsearch의 도구 액세스를 위한 MCP를 사용해 특화된 하이브리드 LLM 에이전트 뉴스룸을 구축하는 방법을 알아보세요.

A2A 및 MCP: 작동 중인 코드

이 글은 동일한 에이전트 내에서 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 에이전트를 사용하며, 이 에이전트는 다시 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_check

8단계: 낮은 신뢰도로 인한 재조사 트리거

리포터 에이전트가 초안을 평가한 결과 한 클레임의 신뢰도가 낮다는 것을 발견했습니다. 연구원 에이전트에게 또 다른 요청을 보냅니다:

{
  "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)을 신중하게 고려하면 성공의 길로 들어설 수 있습니다.

추가 리소스

관련 콘텐츠

채팅창을 넘어선 Agent Builder: Augmented Infrastructure 소개

Alexander Wert

Elasticsearch로 에이전틱 메모리 관리하기

Someshwaran Mohankumar

Elasticsearch 추론 API와 Hugging Face 모델 함께 사용하기

Jeffrey Rengifo

Elastic 에이전트 빌더와 GPT-OSS로 HR용 AI 에이전트 구축하기

Tomás Murúa

TypeScript로 Elasticsearch MCP 서버 생성

Jeffrey Rengifo

최첨단 검색 환경을 구축할 준비가 되셨나요?

충분히 고급화된 검색은 한 사람의 노력만으로는 달성할 수 없습니다. Elasticsearch는 여러분과 마찬가지로 검색에 대한 열정을 가진 데이터 과학자, ML 운영팀, 엔지니어 등 많은 사람들이 지원합니다. 서로 연결하고 협력하여 원하는 결과를 얻을 수 있는 마법 같은 검색 환경을 구축해 보세요.

직접 사용해 보세요