# A2A 에이전트사용하기

### 유즈케이스: 금융 멀티에이전트 시스템

* 본 매뉴얼에서는 다음과 같은 멀티에이전트 시스템을 A2A로 구축하는 예시를 다룹니다:

```
사용자 → 마스터 에이전트
             ├─ A2A → 잔액조회 에이전트 (A팀 개발)
             ├─ A2A → 이체처리 에이전트 (B팀 개발)
             └─ A2A → 상담 에이전트 (C팀 개발)
```

* 각 팀은 **독립적으로** 서브에이전트를 개발/배포/테스트하고, 마스터 에이전트는 A2A 프로토콜로 서브에이전트와 통신합니다.

***

### Part 1. 서브에이전트 만들기

#### 1-1. 워크플로우 생성 + Python 단계 추가

* 에이전트 > 워크플로우에서 새 워크플로우를 생성하고, 에이전트 > 워크플로우 > 워크플로우 상세 > 리비전 정보 탭에서 Python 단계를 추가합니다.

**잔액조회 에이전트 (A팀) 예시:**

```python
async def run(data):
    """잔액조회 에이전트: 모의 데이터 기반 잔액 응답"""
    question = data.get("question", "")

    accounts = {
        "입출금": {"number": "110-***-1234", "balance": 1234500},
        "적금": {"number": "230-***-5678", "balance": 5000000},
        "CMA": {"number": "350-***-9012", "balance": 2300000},
    }

    total = sum(a["balance"] for a in accounts.values())
    lines = []
    for name, info in accounts.items():
        lines.append("  - {} ({}): {:,}원".format(name, info["number"], info["balance"]))

    return {
        "question": question,
        "text": "잔액 조회 결과입니다.\n\n" + "\n".join(lines) + "\n\n총 잔액: {:,}원".format(total)
    }
```

#### 1-2. A2A 에이전트 노출 설정

* 에이전트 > 워크플로우 > 워크플로우 상세 > **A2A 에이전트** 탭에서 토글을 켜고 설정합니다.

<figure><img src="/files/BGU3XloRVbwEtBnupgw5" alt=""><figcaption></figcaption></figure>

* A2A 에이전트 이름과 설명을 입력합니다.&#x20;

<table><thead><tr><th width="200">필드</th><th>예시 값</th><th>설명</th></tr></thead><tbody><tr><td>A2A 에이전트로 노출</td><td>ON</td><td>토글 활성화</td></tr><tr><td>에이전트 이름</td><td><code>잔액조회-에이전트</code></td><td>미입력시, 워크플로우 이름 사용</td></tr><tr><td>에이전트 설명</td><td><code>은행 잔액 조회 A2A 에이전트</code></td><td>에이전트 기능 설명</td></tr><tr><td>Capabilities</td><td><code>{"streaming": true}</code></td><td>선택 — 기본값 자동 설정</td></tr></tbody></table>

> **참고**: 저장만으로는 Agent Card가 생성되지 않습니다. 저장한 다음에 **워크플로우를 배포해야 자동으로 생성**됩니다.

#### 1-3. 워크플로우 배포

* 배포하면 다음이 자동으로 처리됩니다:
  * **Agent Card 생성** — A2A v1.0 스펙의 에이전트 메타데이터
  * **Skills 자동 파생** — MCP 도구가 활성화되어 있으면 skills 메타데이터에 자동 추가 (호출 방법에는 영향 없음)
  * **인증 스키마** — GenOS Auth Key Bearer가 자동 설정

#### 1-4. Agent Card 확인

* 에이전트> A2A 에이전트의 목록에 생성된 Agent Card를 확인합니다.

<figure><img src="/files/3fA6hXXNq2INiyRBCkhS" alt=""><figcaption></figcaption></figure>

* 목록에서 Agent Card를 클릭하면 상세 정보를 볼 수 있습니다.

<figure><img src="/files/SudvLmlHWta3z5CYhLA2" alt=""><figcaption></figcaption></figure>

***

### Part 2. A2A로 에이전트 호출하기

#### 2-1. Agent Card 디스커버리

* A2A 스펙에 따라, 에이전트 정보를 `/.well-known/agent.json`에서 조회합니다.

```bash
GET /a2a/agents/{agent_card_id}/.well-known/agent.json
```

```json
{
  "name": "잔액조회-에이전트",
  "description": "은행 잔액 조회 A2A 에이전트",
  "url": "http://genos.example.com/a2a/32",
  "version": "1.0",
  "provider": { "organization": "GenOS" },
  "capabilities": { "streaming": true, "pushNotifications": false },
  "skills": [
    { "id": "balance-inquiry", "name": "balance-inquiry", "description": "잔액 조회 도구" }
  ],
  "securitySchemes": {
    "bearer": { "type": "http", "scheme": "bearer", "description": "GenOS Auth Key" }
  },
  "security": [{ "bearer": [] }]
}
```

* 마스터 에이전트는 이 정보를 보고 "이 에이전트가 잔액 조회를 할 수 있다"고 판단합니다.

#### 2-2. tasks/send — 동기 호출

* 에이전트에게 메시지를 보내고 완료될 때까지 기다립니다.

```bash
POST /a2a/{agent_card_id}
Content-Type: application/json
Authorization: Bearer <GenOS Auth Key>

{
  "jsonrpc": "2.0",
  "id": "req-001",
  "method": "tasks/send",
  "params": {
    "message": {
      "parts": [
        { "type": "text", "text": "내 계좌 잔액 알려줘" }
      ]
    }
  }
}
```

**응답:**

```json
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {
    "id": "73d9e645-e389-4c2d-8af0-72ec54180259",
    "status": { "state": "completed" },
    "history": [
      {
        "role": "user",
        "parts": [{ "type": "text", "text": "내 계좌 잔액 알려줘" }]
      },
      {
        "role": "agent",
        "parts": [{ "type": "text", "text": "잔액 조회 결과입니다.\n\n  - 입출금 (110-***-1234): 1,234,500원\n  - 적금 (230-***-5678): 5,000,000원\n  - CMA (350-***-9012): 2,300,000원\n\n총 잔액: 8,534,500원" }]
      }
    ],
    "artifacts": [
      {
        "name": "response",
        "parts": [{ "type": "text", "text": "잔액 조회 결과입니다..." }]
      }
    ]
  }
}
```

#### 2-3. tasks/sendSubscribe — 스트리밍 호출

* 실시간으로 진행 상황을 받으려면 스트리밍 엔드포인트를 사용합니다.

```bash
POST /a2a/{agent_card_id}/stream
Content-Type: application/json
Authorization: Bearer <GenOS Auth Key>

{
  "message": {
    "parts": [{ "type": "text", "text": "잔액 조회해줘" }]
  }
}
```

**SSE 이벤트 스트림:**

```
event: task/status
data: {"id":"task-123","state":"working"}

event: task/artifact
data: {"id":"task-123","artifact":{"name":"response","parts":[{"type":"text","text":"잔액 조회 결과..."}]}}

event: task/status
data: {"id":"task-123","state":"completed"}
```

#### 2-4. tasks/get — 이전 태스크 조회

```bash
POST /a2a/{agent_card_id}
Authorization: Bearer <GenOS Auth Key>

{
  "jsonrpc": "2.0",
  "id": "req-002",
  "method": "tasks/get",
  "params": {
    "id": "73d9e645-e389-4c2d-8af0-72ec54180259",
    "historyLength": 10
  }
}
```

#### 2-5. tasks/cancel — 진행 중 태스크 취소

```bash
POST /a2a/{agent_card_id}
Authorization: Bearer <GenOS Auth Key>

{
  "jsonrpc": "2.0",
  "id": "req-003",
  "method": "tasks/cancel",
  "params": { "id": "73d9e645-..." }
}
```

***

### Part 3: 마스터 에이전트에서 A2A 서브에이전트 호출하기

* 마스터 에이전트를 Python 단계로 구현하여, A2A 프로토콜로 서브에이전트를 호출합니다.

#### 3-1. 마스터 에이전트 Python 코드

> **보안 참고**: 서브에이전트의 Auth Key를 코드에 직접 넣지 마세요. 워크플로우 리비전의 **환경변수**에 시크릿으로 등록하고, `os.environ`으로 읽어 사용합니다.

```python
import json
import os
import httpx

GATEWAY_URL = os.environ.get("LLMOPS_GATEWAY_API_URL", "http://llmops-gateway-api-service:8080")

# 서브에이전트 Auth Key는 워크플로우 환경변수에 시크릿으로 등록
# (Admin UI > 워크플로우 > 리비전 > 환경변수)
# 예: A2A_AUTH_KEY_32 = "xyz789..." (Agent Card ID=32의 워크플로우 Auth Key)


async def send_a2a_message(client, agent_card_id, text):
    """gateway 경유 A2A tasks/send (환경변수의 Auth Key 사용)"""
    auth_key = os.environ.get("A2A_AUTH_KEY_{}".format(agent_card_id), "")
    resp = await client.post(
        "{}/a2a/{}".format(GATEWAY_URL, agent_card_id),
        headers={
            "Authorization": "Bearer {}".format(auth_key),
            "Content-Type": "application/json"
        },
        json={
            "jsonrpc": "2.0",
            "id": "master-req",
            "method": "tasks/send",
            "params": {
                "message": {
                    "parts": [{"type": "text", "text": text}]
                }
            }
        },
        timeout=60.0
    )
    return resp.json().get("result", {})


async def run(data):
    """마스터 에이전트: A2A로 서브에이전트 호출 (gateway 경유)"""
    question = data.get("question", "")

    # 호출할 서브에이전트 Agent Card ID 목록 (환경변수에서)
    agent_card_ids = [
        int(k.replace("A2A_AUTH_KEY_", ""))
        for k in os.environ if k.startswith("A2A_AUTH_KEY_")
    ]

    async with httpx.AsyncClient(timeout=30.0) as client:

        # 각 에이전트에게 메시지 전송 (gateway 경유 — Auth Key 인증)
        results = {}
        for card_id in agent_card_ids:
            result = await send_a2a_message(client, card_id, question)
            if result.get("status", {}).get("state") == "completed":
                # 에이전트 응답에서 텍스트 추출
                history = result.get("history", [])
                agent_msgs = [m for m in history if m.get("role") == "agent"]
                if agent_msgs:
                    text_parts = [
                        p["text"] for p in agent_msgs[-1].get("parts", [])
                        if p.get("type") == "text"
                    ]
                    results[agent["name"]] = "\n".join(text_parts)

    # 3. 결과 종합
    lines = ["[마스터 에이전트 — A2A]", ""]
    lines.append("사용자 질문: {}".format(question))
    lines.append("탐색된 A2A 에이전트: {}개".format(len(agents)))
    lines.append("")

    for name, text in results.items():
        lines.append("--- {} ---".format(name))
        lines.append(text)
        lines.append("")

    if not results:
        lines.append("(응답한 서브에이전트가 없습니다)")

    return {"question": question, "text": "\n".join(lines)}
```

#### 3-2. 실행 결과

마스터 에이전트를 배포한 후 **테스트** 버튼으로 동작을 검증합니다.

**마스터 에이전트 테스트 결과 — A2A로 서브에이전트 호출 성공:**

<figure><img src="/files/LRwXTbXgoVepkLcRdm78" alt=""><figcaption></figcaption></figure>

마스터 에이전트를 호출하면:

```
[마스터 에이전트 — A2A]

사용자 질문: 내 계좌 잔액 알려줘
탐색된 A2A 에이전트: 1개

--- 잔액조회-에이전트 ---
잔액 조회 결과입니다.

  - 입출금 (110-***-1234): 1,234,500원
  - 적금 (230-***-5678): 5,000,000원
  - CMA (350-***-9012): 2,300,000원

총 잔액: 8,534,500원
```

#### 3-3. MCP vs A2A 마스터 에이전트 비교

| 항목        | MCP 방식               | A2A 방식                          |
| --------- | -------------------- | ------------------------------- |
| 서브에이전트 호출 | `tools/call` (도구 호출) | `tasks/send` (메시지 전송)           |
| 응답 형식     | 텍스트/JSON 단일 응답       | Task 객체 (history, artifacts)    |
| 상태 추적     | 없음                   | Task 상태 (submitted → completed) |
| 대화 이력     | 없음                   | history에 user/agent 메시지 보존      |
| 스트리밍      | 없음                   | SSE 지원                          |
| 적합한 경우    | 단순 조회/계산             | 복잡한 처리, 확인 필요, 대화형              |

#### 3-4. MCP vs A2A 언제 무엇을 써야 하나?

<table data-header-hidden><thead><tr><th width="160.8182373046875">항목</th><th width="268.4544677734375">MCP</th><th>A2A</th></tr></thead><tbody><tr><td><strong>용도</strong></td><td>도구 호출 (단발성)</td><td>에이전트 간 대화 (multi-turn)</td></tr><tr><td><strong>호출 패턴</strong></td><td>요청 → 즉시 응답</td><td>요청 → 상태 변화 → 최종 응답</td></tr><tr><td><strong>적합한 예</strong></td><td>잔액 조회, 환율 계산</td><td>이체 처리 (확인 요청 포함), 상담 에이전트</td></tr><tr><td><strong>상태 관리</strong></td><td>없음</td><td>Task 상태 (submitted → working → completed)</td></tr><tr><td><strong>스트리밍</strong></td><td>없음</td><td>SSE 지원 (진행 상황 실시간 전달)</td></tr></tbody></table>

* **동시 사용 가능**: 하나의 워크플로우를 MCP 도구 + A2A 에이전트로 동시에 노출할 수 있습니다.

***

### Part 4: 에이전트 간 multi-turn 대화

* A2A의 핵심 장점은 **multi-turn 대화**입니다. 같은 Task ID로 여러 메시지를 주고받을 수 있습니다.

#### 4-1. 이체 처리 시나리오 (실제 검증 완료)

**이체처리 에이전트 (B팀)** — 이체 시 사용자 확인이 필요한 경우:

```
1차 호출:
마스터 → tasks/send "100만원을 김철수에게 이체해줘"
       ← status: input-required
         "이체 정보를 확인해주세요.
          받는 사람: 김철수
          금액: 1,000,000원
          진행할까요? (네/아니오)"

2차 호출 (같은 task_id):
마스터 → tasks/send "네, 진행해주세요"
       ← status: completed
         "이체가 완료되었습니다.
          거래번호: TX-20260331-001
          받는 사람: 김철수
          금액: 1,000,000원
          잔액: 234,500원"
```

> 위 시나리오는 Kind 환경에서 실제 검증되었습니다. 워크플로우가 `a2a_status: "input-required"`를 응답하면 A2A 핸들러가 해당 상태로 전환합니다.

#### 4-2. Multi-turn 호출 코드

```python
# 1차: 이체 요청
result = await send_a2a_message(client, headers, agent_id, "100만원을 김철수에게 이체해줘")
task_id = result["id"]
# status: "input-required"

# 2차: 확인 응답 (같은 task_id)
resp = await client.post(
    "{}/api/admin/a2a/{}".format(ADMIN_API_URL, agent_id),
    headers={**headers, "Content-Type": "application/json"},
    json={
        "jsonrpc": "2.0",
        "id": "req-002",
        "method": "tasks/send",
        "params": {
            "id": task_id,  # ← 기존 Task에 메시지 추가
            "message": {
                "parts": [{"type": "text", "text": "네, 진행해주세요"}]
            }
        }
    },
    timeout=60.0
)
# status: "completed"
```

#### 4-3. Task 상태 흐름

```
submitted → working → completed    (정상 완료)
submitted → working → failed       (실행 오류)
submitted → rejected               (에이전트가 거부 — 미배포 등)
submitted → working → input-required → working → completed  (multi-turn)
submitted → working → canceled     (사용자 취소)
```

***

### Part 5: 에러 처리

#### 태스크 상태 기반 에러

<table><thead><tr><th width="115.18182373046875">상태</th><th width="188.181884765625">의미</th><th width="207.4544677734375">발생 상황</th><th>마스터 에이전트 대응</th></tr></thead><tbody><tr><td><code>rejected</code></td><td>에이전트가 작업 거부</td><td>워크플로우 미배포</td><td>다른 에이전트 시도 또는 사용자에게 안내</td></tr><tr><td><code>failed</code></td><td>실행 중 오류</td><td>헬스체크 실패, 실행 오류</td><td>재시도 또는 에러 메시지 전달</td></tr><tr><td><code>canceled</code></td><td>사용자/시스템 취소</td><td>tasks/cancel 호출</td><td>취소 안내</td></tr></tbody></table>

#### JSON-RPC 에러

<table><thead><tr><th width="135.3636474609375">코드</th><th>메시지</th><th>의미</th></tr></thead><tbody><tr><td>-32700</td><td>Parse error</td><td>JSON 파싱 실패</td></tr><tr><td>-32600</td><td>Invalid request</td><td>필수 필드 누락</td></tr><tr><td>-32601</td><td>Method not found</td><td>지원하지 않는 메서드</td></tr><tr><td>-32001</td><td>Task not found</td><td>존재하지 않는 태스크 ID</td></tr><tr><td>-32002</td><td>Unsupported operation</td><td>완료된 태스크에 메시지 전송 시도</td></tr></tbody></table>

#### 에러 응답 예시

```json
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {
    "id": "task-123",
    "status": {
      "state": "failed",
      "message": "워크플로우 실행 오류: Model not loaded"
    },
    "metadata": {
      "error": {
        "reason": "TASK_EXECUTION_ERROR",
        "domain": "a2a-protocol.org",
        "metadata": {
          "genos_error_code": "10020034",
          "genos_error_msg": "워크플로우 실행 오류"
        }
      }
    }
  }
}
```

***

### Part 6: 인증

#### 인증 구조 개요

* A2A 인증은 **외부 호출**과 **내부 통신**으로 나뉩니다.

```
                         [외부 호출 — Auth Key 필요]
사용자/외부 시스템 ──→ Gateway (마스터 Auth Key 검증) ──→ 마스터 에이전트

                         [에이전트 간 호출 — 서브 Auth Key 필요, 환경변수로 주입]
마스터 에이전트 ──→ Gateway (서브 Auth Key) ──→ 서브에이전트 A
                                            ──→ 서브에이전트 B
```

* **사용자는 마스터 에이전트의 Auth Key만 알면 됩니다.** 마스터가 서브에이전트를 호출할 때 필요한 Auth Key는 워크플로우 환경변수에 시크릿으로 등록합니다.

#### 인증 구조

<table><thead><tr><th width="210.6363525390625">호출 경로</th><th width="81.54541015625">인증</th><th width="221.54541015625">Auth Key</th><th>설명</th></tr></thead><tbody><tr><td>외부 → 마스터 에이전트</td><td><strong>필요</strong></td><td>마스터 워크플로우 Auth Key</td><td>사용자가 마스터에게 요청</td></tr><tr><td>마스터 → 서브에이전트</td><td><strong>필요</strong></td><td>서브 워크플로우 Auth Key</td><td>환경변수에 시크릿 등록, gateway 경유</td></tr><tr><td>외부 → 서브에이전트 직접</td><td><strong>필요</strong></td><td>서브 워크플로우 Auth Key</td><td>서브를 직접 호출할 때</td></tr><tr><td>디스커버리 (<code>.well-known/agent.json</code>)</td><td><strong>불필요</strong></td><td>없음</td><td>A2A 스펙 — 누구나 조회 가능</td></tr></tbody></table>

#### Auth Key 발급 및 등록 방법

**1단계: 서브에이전트 워크플로우에서 Auth Key 발급**

```
서브에이전트 워크플로우 > 인증 키 탭 > 생성
→ token: "abc123..." (64자 문자열)
```

**2단계: 마스터 에이전트 워크플로우의 환경변수에 시크릿 등록**

```
마스터 에이전트 워크플로우 > 리비전 > 환경변수
→ A2A_AUTH_KEY_32 = "abc123..."   (서브에이전트 Agent Card ID=32의 Auth Key)
```

> **주의**: Auth Key를 Python 코드에 직접 넣지 마세요. 반드시 환경변수로 등록하고 `os.environ.get()`으로 읽어 사용합니다.

**3단계: 마스터 에이전트 외부 호출용 Auth Key 발급**

```
마스터 에이전트 워크플로우 > 인증 키 탭 > 생성
→ token: "xyz789..." (64자 문자열)
→ 이 token을 외부 클라이언트(사용자)에게 전달
```

#### 외부에서 호출할 때

```bash
# 1. 디스커버리 (인증 없이)
GET /.well-known/agent.json

# 2. A2A 호출 (Auth Key 필요)
POST /a2a/{agent_card_id}
Authorization: Bearer <워크플로우 Auth Key>
Content-Type: application/json

{"jsonrpc":"2.0","method":"tasks/send","params":{...}}
```

#### Gateway 인증 플로우 (상세)

```
외부 클라이언트
  → POST /a2a/32
    Authorization: Bearer abc123...
  → Gateway:
    1. agent_card_id=32 → admin-api 조회 → workflow_id=14
    2. AuthKeyBearer(workflow, resource_id=14, token=abc123...) 검증
    3. IP 확인, 만료일 확인, Rate Limit 확인
  → admin-api: A2A 프로토콜 핸들러 실행
  ← JSON-RPC 응답
```

***

### Part 7: 채팅 UI에서 멀티턴 서브에이전트 테스트

* 마스터 에이전트를 채팅 앱과 연결하면 사용자가 자연어로 대화하면서 A2A 멀티턴 흐름을 직접 경험할 수 있습니다.

#### 7-1. 채팅 앱 진입

* 채팅 목록에서 마스터 에이전트가 연결된 챗봇을 클릭합니다.

<figure><img src="/files/bZb78ZprKpMNHhnETLFV" alt=""><figcaption></figcaption></figure>

#### 7-2. Turn 1 — 잔액 조회 (단순 완료)

* "잔액 조회해줘"를 입력하면 마스터 에이전트가 **잔액조회 서브에이전트(A2A)**&#xB97C; 호출하고 결과를 반환합니다.

<figure><img src="/files/5tdVq7klMcQl37dcI1im" alt=""><figcaption></figcaption></figure>

```
[A2A 잔액조회에이전트]

사용자 질문: 잔액 조회해줘
서브에이전트 상태: completed

잔액 조회 결과입니다.
  - 입출금 (110-***-1234): 1,234,500원
  - 적금 (230-***-5678): 5,000,000원
  - CMA (350-***-9012): 2,300,000원
총 잔액: 8,534,500원
```

#### 7-3. Turn 1 — 이체 요청 (`input-required`)

* "홍길동에게 10만원 이체해줘"를 입력하면 마스터 에이전트가 **이체처리 서브에이전트(A2A)** 로 라우팅합니다. 이체 에이전트는 확인이 필요하므로 `input-required` 상태를 반환합니다.

<figure><img src="/files/KnrjIZLvFPWXJkQxuIDg" alt=""><figcaption></figcaption></figure>

```
[A2A 이체에이전트]

사용자 질문: 홍길동에게 10만원 이체해줘
서브에이전트 상태: input-required

이체 정보를 확인해주세요.
받는 분: 홍길동
금액: 100,000원
진행하시겠습니까? (네/아니오)
```

* 마스터 에이전트는 이 `task_id`를 `chatId` 기준으로 저장합니다.

#### 7-4. Turn 2 — 확인 (`input-required → completed`)

* "네, 확인합니다"를 입력하면 마스터 에이전트가 **같은 `task_id`** 로 이체 에이전트에 메시지를 이어 보냅니다. 이체 에이전트가 처리를 완료하고 `completed` 상태를 반환합니다.

<figure><img src="/files/qf31poEWNe3bmNH4RPOA" alt=""><figcaption></figcaption></figure>

```
[A2A 이체에이전트]

사용자 질문: 네, 확인합니다
서브에이전트 상태: completed

이체가 완료되었습니다.
받는 분: 홍길동, 금액: 100,000원
거래번호: TX-20260331-001234
```

#### 7-5. 전체 멀티턴 대화 흐름

<figure><img src="/files/aRwf4IWJkqCacpfJUtTj" alt=""><figcaption></figcaption></figure>

#### 7-6. 멀티턴 라우팅 구조 요약

```
사용자: "잔액 조회해줘"
  → 마스터: _is_transfer() = False  →  잔액조회 에이전트(32)
  ← completed 즉시 반환

사용자: "홍길동에게 10만원 이체해줘"
  → 마스터: _is_transfer() = True   →  이체처리 에이전트(33)
  ← input-required  →  _pending[chatId] = {agent_id: 33, task_id: "..."}

사용자: "네, 확인합니다"
  → 마스터: _pending[chatId] 존재  →  이체처리 에이전트(33), 동일 task_id 재전송
  ← completed  →  _pending[chatId] 제거
```

> **포인트**: 마스터 에이전트가 `chatId`를 키로 `_pending` 딕셔너리를 관리하므로, 사용자 입장에서는 자연스러운 멀티턴 대화처럼 보입니다. 실제로는 A2A `tasks/send`에 동일한 `task_id`가 전달되어 서브에이전트가 이전 대화 맥락을 이어받습니다.

***

### FAQ

**Q: A2A 설정을 저장했는데 Agent Card가 생성되지 않습니다.**&#x20;

* A: 설정 저장만으로는 생성되지 않습니다. 워크플로우를 배포해야 Agent Card가 자동으로 생성됩니다.

**Q: MCP와 A2A를 동시에 사용할 수 있나요?**&#x20;

* A: 예. MCP 도구 탭과 A2A 에이전트 탭은 독립적입니다. 하나의 워크플로우를 MCP 도구로도, A2A 에이전트로도 동시에 노출할 수 있습니다. MCP는 단순 조회에, A2A는 대화형 처리에 적합합니다.

**Q: 워크플로우를 중지하면 A2A 에이전트도 사라지나요?**&#x20;

* A: Agent Card 정보는 유지되지만 비활성화됩니다. 재배포하면 다시 활성화됩니다.

**Q: 외부 시스템에서 A2A로 접근하려면?**&#x20;

* A: Agent Card 디스커버리 URL로 에이전트 정보를 조회하고, `securitySchemes`에 명시된 인증 방식(Bearer)으로 워크플로우 Auth Key를 전달하여 `tasks/send`를 호출합니다.

**Q: 마스터 에이전트에서 서브 에이전트를 호출하려면 Auth Key가 필요한가요?**&#x20;

* A: 예. 서브에이전트별 Auth Key가 필요합니다. 하지만 코드에 직접 넣지 않고, 마스터 워크플로우의 **환경변수**에 시크릿으로 등록합니다. 사용자는 마스터 Auth Key만 알면 되고, 서브에이전트 Auth Key는 개발자가 환경변수로 관리합니다.

**Q: A2A 에이전트 목록이 사이드바에 안 보입니다.**&#x20;

* A: 관리자에게 `a2a_agent` 리소스 타입에 대한 접근 권한을 요청하세요.

**Q: multi-turn 대화는 어떻게 하나요?**&#x20;

* A: 첫 `tasks/send` 응답에서 받은 `task_id`를 다음 요청의 `params.id`에 넣으면, 같은 태스크에 메시지가 추가됩니다. 에이전트가 `input-required` 상태를 반환하면 사용자 입력을 기다리는 것입니다.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://genos-docs.gitbook.io/default/v1.8.5/basic-tutorials/guides/workflow/a2a.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
