A2A란 무엇인가
지난 장 마지막 줄에서 던진 질문 — 들은 서로 어떻게 말을 거는가. 답이 다. Agent-to-Agent. Google이 2025년에 공개한 은 이 질문에 표준 규격으로 답한다.
A2A를 한 줄로
A2A는 에이전트끼리 직접 부르기 위한 HTTP 위의 표준 프로토콜이다. 그게 전부다.
복잡하게 들리면 비유로 본다 — 브라우저와 웹 서버 사이엔 HTTP가 있고, 마이크로서비스 사이엔 gRPC·REST가 있다. 사이에는 가 있다. 을 서로 다른 회사·프레임워크 사이에서도 가능하게 만드는 공통 언어다.
사내 호출이면 함수 한 줄로 된다. 외부 에이전트면 표준이 있어야 부를 수 있다.
MCP, function-calling과 어디가 다른가
비슷한 이름들이 많다. 셋의 자리를 분명히 나눠 둔다.
| 프로토콜 | 누구 사이 | 무엇을 노출 |
|---|---|---|
| function-calling | ↔ 함수 | 한 모델 안의 도구 호출 |
| MCP | LLM ↔ 도구 서버 | 도구·리소스·프롬프트를 표준화 |
| ↔ | 다른 에이전트 전체를 호출 |
핵심은 추상화 레벨이다. function-calling은 한 모델의 손, MCP는 도구 카탈로그, 는 완성된 에이전트 자체다. 한 시스템이 외부와 닿는 가장 높은 층위다.
왜 표준이 필요한가
회사가 만든 가 다른 회사의 를 불러야 하는 상황을 상상해 본다. 보험사 견적 에이전트가 정비소의 견적 에이전트를 호출한다. 둘은 다른 회사, 다른 프레임워크, 다른 모델로 만들었다.
표준 없이는 N×N 어댑터를 짜야 한다. 보험사가 거래하는 정비소가 100개면 100개의 어댑터다. 은 이 N×N을 1개의 규격으로 바꾼다. 각자 한 번씩 를 지원하면 모두가 서로 부를 수 있다.
Agent Card — 에이전트의 명함
A2A의 첫 번째 핵심 개념은 다. 에이전트가 자신을 설명하는 공개 JSON 문서다. 식별·엔드포인트·스킬·인증·캐퍼빌리티가 한 파일에 들어 있다.
규격상 권장 위치는 /.well-known/agent-card.json이다. 도메인만 알면 명함이 거기 있다. 를 호출하기 전에 클라이언트가 먼저 이 파일을 fetch한다. 의 출발점이다.
Agent Card의 5대 필드
규격은 더 많지만, 항상 보게 되는 다섯 개부터 외운다. 의 뼈대다.
- name — 사람·기계가 읽는 식별자.
- url — . 실제 호출이 들어가는 URL.
- skills — 배열. 각 스킬이 한 작업 단위.
- capabilities — streaming/pushNotifications 같은 기능 선언.
- securitySchemes — 어떤 인증으로 부를 수 있는지 (OAuth, API key, Bearer 등).
여기에 description, version, defaultInputModes/OutputModes 같은 바깥에서 같이 보는 필드가 따라붙는다.
Agent Card 실제 예시
스펙의 GeoSpatial Route Planner 예시를 단순화한 JSON. 표준이 어떤 모양인지 한 눈에 본다. 이 어떻게 선언되는지 주목한다. 카드는 의 정식 직렬화 형태다.
# Verified against: https://a2a-protocol.org/v0.3.0/specification/
# Verified at: 2026-06-02
agent_card = {
"protocolVersion": "0.3.0",
"name": "Insurance Quote Agent",
"description": "차량 정보로 자동차 보험 견적을 산출",
"url": "https://insurer.example.com/a2a",
"version": "1.2.0",
"preferredTransport": "JSONRPC",
"capabilities": {
"streaming": True,
"pushNotifications": True,
},
"defaultInputModes": ["text/plain", "application/json"],
"defaultOutputModes": ["application/json"],
"skills": [{
"id": "quote.car",
"name": "자동차 견적",
"description": "차종·연식·운전자로 보험료 산출",
"tags": ["insurance", "car"],
"examples": ["2022 카니발 만 5세 운전자 견적"],
}],
"securitySchemes": {
"bearer": {"type": "http", "scheme": "bearer"},
},
"security": [{"bearer": []}],
}명함을 fetch하는 코드
의 가장 단순한 형태는 well-known URL에서 한 번 fetch하는 것. 그 다음 zod·pydantic으로 검증한다. 를 부르기 전 한 번만 하면 된다. 의 명세를 런타임에 읽어 오는 출발점이다.
# Verified against: https://a2a-protocol.org/v0.3.0/specification/
# Verified at: 2026-06-02
import httpx
from pydantic import BaseModel
class Capabilities(BaseModel):
streaming: bool = False
pushNotifications: bool = False
class Skill(BaseModel):
id: str
name: str
description: str
class AgentCard(BaseModel):
protocolVersion: str
name: str
description: str
url: str
version: str
capabilities: Capabilities
defaultInputModes: list[str]
defaultOutputModes: list[str]
skills: list[Skill]
def fetch_card(domain: str) -> AgentCard:
url = f"https://{domain}/.well-known/agent-card.json"
raw = httpx.get(url, timeout=5).json()
return AgentCard.model_validate(raw)
card = fetch_card("insurer.example.com")
print(card.name, [s.id for s in card.skills])Capabilities 한 줄로
capabilities는 가 지원하는 운영 모드를 선언한다. 두 가지가 가장 흔하다.
- streaming — Task 진행 중 부분 결과를 SSE로 푸시한다. 사용자에게 생각하는 동안 보여줄 수 있다.
- pushNotifications — 장기 실행 Task의 완료를 webhook으로 통보한다. 폴링이 필요 없다.
의 이 필드를 보고 호출자는 어떤 모드로 부를지 결정한다. 같은 도 streaming이 켜져 있으면 다른 패턴으로 호출한다. 8장에서 본격적으로 다룬다.
Discovery vs Registry
같은 듯 다른 두 개념.
- 는 행위 — 명함을 찾는 절차. well-known URL을 직접 fetch하거나 검색 API를 부른다.
- 는 장소 — 카드들을 한 곳에 모아 인덱싱한 디렉토리. GitHub의 awesome 리스트, 사내 카탈로그, 공개 마켓플레이스가 모두 레지스트리가 될 수 있다.
작은 조직은 well-known URL만으로 충분하다. 사용 가능한 가 수십·수백 개로 늘면 레지스트리가 필수가 된다.
사내 통합 사례 — 왜 디스커버리가 답인가
사례 하나로 정당화한다. 한 회사가 LangGraph로 만든 CS 에이전트, CrewAI로 만든 리서치 에이전트, 자체 구현한 결제 에이전트를 운영한다. 마케팅 팀의 가 이 셋을 모두 부를 일이 생긴다.
- 하드코딩 방식 — 마케팅 에이전트가 세 엔드포인트 + 세 인증 + 세 스키마를 직접 알아야 한다. CS 팀이 엔드포인트를 바꾸면 마케팅 코드도 같이 고친다.
- + 디스커버리 — 마케팅 에이전트는 레지스트리에 질문만 한다. “결제 처리할 수 있는 에이전트?” 레지스트리가 카드를 돌려주면, 거기 적힌 url로 부른다. 엔드포인트가 바뀌어도 카드만 갱신되면 끝.
N×N 어댑터 지옥이 프로토콜 1개 + 레지스트리 1개로 줄어든다.
A2A를 도입할 때의 체크리스트
처음 도입할 때 꼭 짚는 다섯 가지.
- ****를 well-known 경로에 올렸는가.
- 한 줄 설명이 모델이 읽기 좋은가 — 호출자 모델이 이걸 보고 부를지 정한다.
- 인증 스킴이 일관된가 — Bearer만 쓸지, OAuth까지 지원할지.
- streaming 필요한가 — 응답이 길면 capabilities에 켠다.
- 버전 정책이 있는가 — 카드의
version변경 정책과 호환성 규칙.
이 다섯 개를 안 정하고 시작하면 6개월 뒤 마이그레이션 비용이 폭증한다. 의 기반은 명함이 잘 정의되어 있다는 단순한 사실 위에 선다.
다음 장으로
명함은 누구인지·어디 있는지를 알려 줄 뿐이다. 실제 호출은 메시지로 한다. 는 명함이고, 사이의 진짜 대화는 메시지로 흐른다.
다음 장에서는 그 메시지를 다룬다. 동기·비동기, 큐, 브로드캐스트, pub/sub — 위에서 흐르는 통신의 기초부터 짚는다.