기존 에이전트 프레임워크와 A2A 통합: Hermes Agent 데모
에이전트 오케스트레이션에 A2A가 중요한 이유
Agent2Agent Protocol (A2A)는 Google이 2025년 4월에 도입한 에이전트 간 통신을 위한 개방형 표준입니다. AI 에이전트가 다른 에이전트의 역량을 발견하고, 태스크를 전송하며, 스트리밍 출력을 수신하고, 태스크의 라이프사이클을 추적하는 방식을 정의합니다 — 모두 JSON-RPC 2.0 위에서, 두 에이전트가 프레임워크, 모델 공급자, 또는 배포 토폴로지를 공유한다고 가정하지 않고서 말입니다.
A2A는 MCP가 다루지 않는 간극을 채웁니다. MCP는 에이전트를 도구와 데이터 소스에 연결합니다 — 이는 도구 호출 프로토콜입니다. A2A는 에이전트를 에이전트에 연결합니다. 특수한 역량(가격 책정 로직, 카탈로그 검색, RFQ 처리)이 필요한 에이전트는 A2A 엔드포인트를 통해 해당 역량을 노출하는 원격 에이전트에 작업을 위임하고, 결과를 A2A 태스크로 수신하고, 자신의 워크플로를 계속할 수 있습니다. 두 프로토콜은 상호 보완적입니다: MCP는 에이전트에게 손을 주고; A2A는 동료를 줍니다.
A2A 사양은 세 가지 핵심 프리미티브를 정의합니다:
- Agent Card —
/.well-known/agent-card.json에 위치한 JSON 문서로, 에이전트의 아이덴티티, 역량, 스킬, 서비스 엔드포인트를 기술합니다. 이것이 에이전트가 서로를 발견하는 방식입니다. - Task — 작업의 단위입니다. 클라이언트는
message/send(비스트리밍) 또는message/stream(스트리밍)을 통해 태스크를 전송합니다. 태스크는 상태를 거쳐 전환됩니다:submitted,working,input-required,completed,failed,canceled. - Artifact — 태스크 실행 중에 생성되는 구조화된 출력으로, 가용해지는 대로 클라이언트에 스트리밍됩니다.
B2B 배포에서 가치 제안은 구체적입니다: 모든 것을 수행하는 하나의 모놀리식 에이전트를 구축하는 대신, 각자 도메인을 소유하는 특화된 에이전트들을 구성하고 — 이들이 공유 메모리나 하드코딩된 함수 호출이 아니라 프로토콜을 통해 조정합니다.
통합 문제
대부분의 기존 에이전트 프레임워크는 A2A를 말하지 않습니다. 자체 네이티브 API 표면, 자체 태스크 모델, 자체 스트리밍 메커니즘을 가지고 있습니다. Hermes Agent(Nous Research 제작)는 /v1/chat/completions에서 OpenAI 호환 API Server를 노출하고, /v1/runs 및 /v1/runs/{id}/events에서 runs 기반 SSE 스트리밍 인터페이스를 노출합니다. OpenClaw는 POST /v1/responses에서 OpenResponses 호환 API를 제공합니다. LangGraph는 자체 그래프 실행 모델을 가지고 있습니다. CrewAI는 자체 크루 디스패치를 가지고 있습니다.
이러한 프레임워크 어느 것도 A2A를 지원하기 위해 내부를 재작성하지 않을 것입니다. 그래야 할 이유도 없습니다 — 이들의 네이티브 API는 자체 생태계를 잘 지원합니다. 질문은: 코드를 변경하지 않고 A2A 에이전트 네트워크에 참여할 수 있는가입니다.
답은 브리지 계층입니다 — A2A 프로토콜과 프레임워크의 네이티브 API 사이에 위치하는 컴포넌트입니다. 브리지는 한쪽에 A2A JSON-RPC 표면(Agent Card, 태스크 라이프사이클, 스트리밍)을 구현하고, 다른 쪽에는 프레임워크의 네이티브 호출로 변환합니다. 프레임워크는 자신이 A2A를 통해 호출되고 있다는 것을 모릅니다. A2A 클라이언트는 어느 프레임워크가 태스크를 실행하는지 모릅니다.
이 글은 Hermes Agent를 데모로 사용하여 브리지 패턴을 안내합니다. 동일한 패턴은 OpenClaw 및 기타 프레임워크에도 적용됩니다 — 브리지 핸들러만 바뀝니다.
브리지 아키텍처
이 패턴의 작동하는 참조 구현은 a2a_daemon_engine입니다 — 게이트웨이 모듈로 실행되어 실행을 플러그인 가능한 핸들러로 라우팅하는 A2A 프로토콜 데몬입니다. 아키텍처는 다음과 같습니다:
A2A Client (모든 에이전트 또는 애플리케이션)
│
POST /{endpoint_id}/a2a (message/send | message/stream)
│
▼
Gateway (auth, routing, SSE 클라이언트 관리)
│
▼
A2A Daemon Executor
│
resolve_agent(uuid) → 데이터베이스 조회
load_agent_handler() ← 데이터 기반, 메타데이터 기반 라우팅
│
├── HermesAgentHandler
│ → POST /v1/runs → GET /v1/runs/{id}/events → SSE 스트리밍
│ → POST /v1/chat/completions (비스트리밍)
│
└── [AnyFrameworkHandler] ← 동일한 인터페이스, 다른 변환
→ 프레임워크의 네이티브 API
브리지는 세 개의 계층으로 구성됩니다:
A2A 프로토콜 계층 — JSON-RPC 디스패치, Agent Card 서빙, 태스크 상태 머신, A2A SDK EventQueue를 처리합니다. 이는 프레임워크에 무관합니다. 모든 통합에서 동일합니다.
게이트웨이 전송 계층 — HTTP, 인증, SSE 클라이언트 라이프사이클, 라우팅을 처리합니다. 이 역시 프레임워크에 무관합니다. 게이트웨이가 와이어를 소유하고; 브리지가 변환을 소유합니다.
프레임워크 핸들러 — 유일한 프레임워크 특정 코드입니다.
ask_model()인터페이스를 구현합니다: A2A 메시지 파트와 컨텍스트를 수신하고, 프레임워크의 네이티브 API를 호출하고, 응답을 다시 A2A 메시지 파트로 변환하고, (스트리밍의 경우) 토큰 델타를 SSE 채널로 전달합니다.
새 프레임워크를 추가한다는 것은 하나의 핸들러 클래스를 작성한다는 의미입니다. 나머지 모든 것 — 프로토콜 표면, 게이트웨이 디스패치, SSE 관리, 태스크 퍼시스턴스 — 은 공유 인프라입니다.
데모: Hermes Agent 브리지
HermesAgentHandler가 작동 예시입니다. A2A 태스크 시맨틱을 Hermes Agent API Server로 브리지합니다. 핸들러는 두 가지 실행 모드를 지원합니다:
비스트리밍: message/send와 message_response
핸들러는 Hermes API Server의 POST /v1/chat/completions를 호출합니다 — OpenAI 호환 엔드포인트입니다. 요청은 변환된 A2A 메시지 파트를 채팅 컴플리션 페이로드로 담습니다. Hermes가 요청을 처리하고(모델 추론, 도구 호출, 에이전트 추론) 단일 응답을 반환합니다. 핸들러는 응답을 ROLE_AGENT를 가진 A2A Message로 변환하여 SDK EventQueue에 내보냅니다.
클라이언트는 전체 에이전트 텍스트와 함께 단일 JSON-RPC 응답을 받습니다. 중간 상태 이벤트도, 스트리밍 청크도 없습니다 — 요청은 Hermes가 완료될 때까지 블록됩니다.
스트리밍: message/send와 task_execution, stream: true
핸들러는 POST /v1/runs를 호출하여 run을 생성한 다음, GET /v1/runs/{id}/events에 SSE 연결을 엽니다. Hermes는 이벤트가 발생하는 대로 스트리밍합니다: 토큰 델타, 추론 메타데이터, 도구 호출/결과 알림, 승인 요청, 라이프사이클 이벤트(run.created, run.completed, run.failed).
핸들러는 백그라운드 스레드에서 드레인 루프를 실행합니다. 각 message.delta 이벤트는 연결된 클라이언트에게 실시간 전달을 위해 게이트웨이의 SSE 매니저로 푸시됩니다. 핸들러는 토큰을 단일 버퍼에 누적합니다. run.completed가 도착하면, 누적된 텍스트는 단일 A2A Message로 SDK EventQueue에 내보내지고, COMPLETED 상태 이벤트는 SSE에만 푸시됩니다.
이 이중 경로 설계 — 실시간 청크를 위한 SSE, 최종 누적 메시지를 위한 SDK EventQueue — 는 아래에서 논의하는 A2A SDK v2의 제약에 대한 대응입니다.
에이전트 경계를 넘은 휴먼 인 더 루프 승인
Hermes는 휴먼 인 더 루프 승인 게이트를 지원합니다 — 에이전트가 민감한 작업을 실행하기 위해 권한이 필요할 때, 일시 정지하고 승인 요청을 내보냅니다. 브리지는 이를 A2A INPUT_REQUIRED 상태로 변환하며, 이는 호출하는 에이전트(또는 인간 운영자)에게 입력이 필요함을 알립니다. 응답은 POST /v1/runs/{id}/approval을 통해 돌아오고, run이 계속됩니다.
이것이 브리지 패턴의 가치가 드러나는 부분입니다. A2A는 INPUT_REQUIRED을 일급 태스크 상태로 정의합니다. Hermes는 자체 승인 메커니즘을 가지고 있습니다. 브리지가 하나를 다른 것으로 매핑하고, 호출하는 에이전트 — 자체는 완전히 다른 프레임워크에서 실행되는 A2A 클라이언트일 수 있습니다 — 는 Hermes 특정 세부사항이 아니라 표준 프로토콜 상태 전환을 봅니다. 에이전트 위임 체인은 인간 승인이 필요한 단계(구매 권한 부여, 데이터 접근 결정, 견적 승인)를 포함할 수 있으며, A2A 프로토콜이 그 게이트를 프레임워크 경계를 넘어 투명하게 전달합니다.
A2A 상태 매핑
A2A는 태스크 상태 머신을 정의합니다: submitted → working → input-required | completed | failed | canceled. 각 프레임워크는 자체 이벤트 어휘를 가집니다. 브리지가 그 사이를 매핑합니다.
Hermes 이벤트-대-상태 매핑:
| Hermes SSE Event | A2A Task State | Bridge Action |
|---|---|---|
run.created |
WORKING |
취소 지원을 위해 run_id 등록 |
message.delta |
WORKING |
토큰 누적; 청크별 SSE 내보냄 |
reasoning.available |
WORKING |
추론 메타데이터 — 토큰 내보냄 없음 |
tool.call / tool.result |
WORKING |
도구 실행 메타데이터 전용 |
approval.required |
INPUT_REQUIRED |
승인 청크 내보냄; pending_approval 저장 |
run.completed |
COMPLETED |
스트림 이벤트 설정; 최종 텍스트 누적 |
run.failed |
FAILED |
에러 청크 내보냄; FAILED 상태 설정 |
POST /v1/runs/{id}/stop |
CANCELED |
tasks/cancel을 통한 외부 취소 |
POST /v1/runs/{id}/approval |
(run 계속) | operation=\"approval_response\"로 해결 |
이 표가 브리지의 핵심입니다. 모든 프레임워크 통합은 동등한 표를 만듭니다 — 왼쪽에 프레임워크의 네이티브 이벤트, 오른쪽에 A2A 상태, 가운데에 브리지 액션. 참조 구현의 HERMES_INTEGRATION.md 문서가 정확한 Hermes 이벤트 형식, 구성 키, 엔드투엔드 흐름 세부사항을 기록합니다.
A2A SDK v2 제약과 이중 경로 수정
A2A SDK v2(a2a-sdk==1.0.2)는 on_message_send 경로에 두 가지 제약을 부과하며, 이는 모든 브리지 구현의 형태를 결정지었습니다:
- 단일 Message만. SDK EventQueue에 여러
Message객체를 내보내면InvalidAgentResponseError: Multiple Message objects received.가 발생합니다. - TaskStatusUpdateEvent 불가. 상태 이벤트는
InvalidAgentResponseError: Received TaskStatusUpdateEvent in message mode.를 발생시킵니다.
단순한 브리지는 토큰 델타마다 하나의 Message를 내보낼 것입니다 — 자연스러운 스트리밍 패턴입니다. SDK는 이를 거부합니다. 또한 message/send 경로에서 상태 이벤트(WORKING, COMPLETED)도 거부합니다.
수정은 이중 경로 출력 채널입니다:
- SSE (게이트웨이 관리): 토큰 청크는 실시간으로 SSE에 푸시됩니다. 연결된 클라이언트는 발생하는 대로 스트리밍 출력을 봅니다. 상태 이벤트(WORKING, COMPLETED, FAILED)도 SSE에만 갑니다.
- SDK EventQueue: 스트림이 완료된 후, 전체 응답 텍스트를 담은 단일 누적
Message가 SDK EventQueue에 내보내집니다. 이것이 JSON-RPCmessage/send응답이 반환하는 것입니다.
클라이언트는 SSE를 통한 실시간 스트리밍과 SDK를 통한 깨끗한 단일 메시지 JSON-RPC 응답을 모두 받습니다. 두 채널 모두 작동합니다; 어느 쪽도 SDK 제약을 위반하지 않습니다. 이 패턴은 프레임워크에 무관합니다 — 백엔드가 Hermes, OpenClaw, 또는 향후의 어떤 핸들러든 상관없이 적용됩니다.
동일한 패턴을 OpenClaw에 적용하기
OpenClaw의 Gateway API는 Hermes와 다른 네이티브 표면을 노출하지만, 브리지 패턴은 동일합니다. OpenClawHandler는 동일한 ask_model() 인터페이스를 구현하고 A2A 태스크 시맨틱을 OpenClaw Gateway 호출로 변환합니다:
- 비스트리밍은 A2A 메시지 파트를 OpenResponses 입력 아이템으로 변환하여
POST /v1/responses에 매핑됩니다. 응답은 다시 A2AMessage로 변환됩니다. - 스트리밍은
stream: true와 함께POST /v1/responses에 매핑되며, SSE 이벤트 스트림을 소비하고 토큰 델타를 A2A SSE 채널로 전달합니다. OpenResponses 스트리밍 형식은 토큰 청크에response.output_text.delta를, 완료에response.completed를 사용합니다 — 다른 이벤트 이름, 동일한 브리지 구조. - 에이전트 선택은
model필드(openclaw/<agentId>) 또는x-openclaw-agent-id헤더를 사용하며, A2A 에이전트 메타데이터에서 매핑됩니다. - 세션 연속성은 안정적인 세션 라우팅을 위해 OpenClaw의
previous_response_id또는user필드를 활용하며, 이는 A2A 태스크 퍼시스턴스에 자연스럽게 매핑됩니다.
OpenClaw의 상태 매핑 표는 다음과 같을 것입니다:
| OpenClaw Event | A2A Task State | Bridge Action |
|---|---|---|
response.created |
WORKING |
응답 ID 등록 |
response.output_text.delta |
WORKING |
토큰 누적; SSE 내보냄 |
response.completed |
COMPLETED |
최종 텍스트 누적; Message 내보냄 |
response.failed |
FAILED |
에러 내보냄; FAILED 설정 |
동일한 열, 동일한 구조, 다른 이벤트 이름. 브리지 핸들러만 프레임워크 간에 바뀌는 유일한 부분입니다.
구성: 메타데이터 기반 라우팅
에이전트 라우팅은 환경 변수 기반이 아니라 메타데이터 기반입니다. 데이터베이스의 각 에이전트 레코드는 metadata JSON 컬럼에 핸들러 구성을 담습니다. Hermes 기반 에이전트의 경우:
{
"module_name": "a2a_daemon_engine.handlers.a2a_hermes_handler",
"class_name": "HermesAgentHandler",
"hermes_api_url": "http://127.0.0.1:8642",
"hermes_api_key": "hermes-local-key",
"hermes_model": "hermes-agent",
"hermes_timeout": 300.0
}
OpenClaw 기반 에이전트의 경우, 메타데이터는 OpenClaw 핸들러를 가리킵니다:
{
"module_name": "a2a_daemon_engine.handlers.a2a_openclaw_handler",
"class_name": "OpenClawHandler",
"openclaw_api_url": "http://127.0.0.1:3000",
"openclaw_api_key": "openclaw-key",
"openclaw_agent_id": "pricing-agent",
"openclaw_timeout": 300.0
}
구성 해석은 우선순위 체인을 따릅니다: 에이전트 메타데이터(DB) → setting dict → Config 기본값(환경 변수). 에이전트별 재정의가 전역 기본값보다 우선합니다. 두 에이전트가 서로 다른 프레임워크를 가리킬 수 있습니다 — 하나는 추론 중심 태스크를 위해 Hermes로, 다른 하나는 워크플로 오케스트레이션 태스크를 위해 OpenClaw로 — 그리고 A2A 프로토콜 표면은 호출하는 에이전트에게 동일하게 보입니다.
이것이 가능하게 하는 것
브리지 패턴은 처음부터 조립하기 어려운 세 가지 역량을 제공합니다:
1. 스트리밍을 갖춘 에이전트 간 위임. A2A 클라이언트 에이전트는 Hermes 기반 에이전트에게 태스크를 전송하고 SSE를 통해 실시간 토큰 스트리밍을 받을 수 있습니다. 호출하는 에이전트는 원격 에이전트가 Hermes를 실행한다는 것을 알 필요가 없습니다 — Agent Card와 JSON-RPC 인터페이스를 가진 A2A 엔드포인트를 봅니다.
2. 에이전트 경계를 넘은 휴먼 인 더 루프 승인. 프레임워크 네이티브 승인 게이트(Hermes 승인 요청, OpenClaw 운영자 승인)가 A2A INPUT_REQUIRED 상태로 매핑됩니다. 에이전트 위임 체인은 인간 승인이 필요한 단계를 포함할 수 있으며, A2A 프로토콜이 해당 상태 전환을 체인의 각 에이전트가 어느 프레임워크에서 실행되든 상관없이 원래 에이전트 또는 운영자에게 다시 전달합니다.
3. 하나의 게이트웨이에서 멀티 프레임워크 라우팅. 동일한 게이트웨이, 동일한 실행기, 동일한 A2A 프로토콜 표면이 서로 다른 프레임워크의 핸들러를 지원합니다. 새 프레임워크를 추가하는 것은 하나의 핸들러 클래스를 작성하고 하나의 에이전트 레코드를 삽입하는 것입니다 — 새 배포도, 새 프로토콜 구현도 아닙니다.
참조 구현은 또한 서버리스 A2A를 위한 AWS Lambda 디스패치, 양방향 스트리밍을 갖춘 실험적 gRPC 전송, 그리고 복합 파티션 키를 통한 멀티테넌트 격리를 갖춘 이중 백엔드 퍼시스턴스(DynamoDB 또는 PostgreSQL)를 지원합니다. a2a_daemon_engine 저장소와 Hermes 통합 가이드에 전체 구현, 구성 참조, 상태 매핑 세부사항이 포함되어 있습니다.
중견 유통업체는 카탈로그 에이전트와 대화하고 카탈로그 에이전트는 재고 에이전트와 대화하는 견적 에이전트가 필요합니다 — 각각 서로 다른 프레임워크로 지원되고, 각각 서로 다른 팀이 소유합니다. A2A는 이 에이전트들에게 공유 프로토콜을 줍니다. 브리지 계층은 Hermes Agent, OpenClaw 및 기타 모든 프레임워크가 내부를 재작성하지 않고 참여할 수 있게 합니다. a2a_daemon_engine은 Hermes Agent로 시연된 해당 브리지 패턴의 작동하는 참조 구현입니다.
범위가 정해진 구축을 요청하세요
1주간의 디스커버리. 시스템 인벤토리, 워크플로 맵, 고정된 범위를 얻습니다 — 우리와 함께 구축하든 하지 않든.
귀하의 시스템을 위해 이것을 구축하고 싶으신가요?
여기의 각 문서는 실제 프로덕션 작업에서 나왔습니다. 대상 시스템과 워크플로가 있다면, 1주 내에 빌드를 범위 정의할 수 있습니다.
범위 정의 빌드 요청1주 발견. 시스템 인벤토리, 워크플로 맵, 고정 범위를 받습니다 — 우리와 빌드할지 여부와 관계없이.