Agent-to-Agent Protocol (A2A)

Протокол Google A2A для межагентного взаимодействия: Agent Cards, Task lifecycle, Streaming, сравнение с MCP.

Agent-to-Agent Protocol (A2A) — это открытый протокол от Google для взаимодействия AI-агентов друг с другом. A2A позволяет агентам, созданным разными разработчиками и на разных фреймворках, обмениваться задачами, данными и статусами выполнения. Протокол был представлен в апреле 2025 года.

Представьте офис, где работают специалисты из разных компаний: дизайнер, программист, маркетолог. Каждый говорит «на своём языке» и использует свои инструменты. A2A — это как общий корпоративный мессенджер с единым форматом задач: любой специалист может поставить задачу другому, отследить статус и получить результат — независимо от того, из какой он компании. Только вместо людей — AI-агенты.

Суть в двух словах

Сейчас AI-агенты — это изолированные системы. Агент на LangChain не умеет общаться с агентом на CrewAI. Корпоративный агент-планировщик не может делегировать подзадачу агенту-исследователю от другого вендора.

A2A решает эту проблему: единый стандарт позволяет любому агенту отправить задачу другому агенту, получать обновления о прогрессе и забирать результат.

Три ключевых концепции:

  • Agent Card — визитка агента (JSON-файл), описывающая его возможности, навыки и способы связи
  • Task — единица работы с жизненным циклом: submitted → working → completed / failed
  • Streaming — обновления в реальном времени через Server-Sent Events (SSE)

A2A и MCP — не конкуренты, а дополнения. MCP стандартизирует подключение агента к инструментам (GitHub, база данных). A2A стандартизирует общение между агентами. Вместе они создают экосистему, где агенты и используют инструменты, и кооперируются друг с другом.

ПромптМультиагентная система с A2A
Агент-координатор получает задачу: «Подготовь отчёт о конкурентах в сегменте AI-поиска»
Ответ модели

Координатор (через A2A):

  1. Находит Agent Card агента-исследователя → Навыки: web-research, data-collection → Отправляет Task: «Собери данные о Perplexity, Exa, You.com»

  2. Находит Agent Card агента-аналитика → Навыки: data-analysis, report-generation → (ждёт результат исследователя)

  3. Исследователь: status=working → completed → Возвращает Artifact: собранные данные

  4. Координатор отправляет данные аналитику → Task: «Проанализируй и создай отчёт»

  5. Аналитик: status=completed → Artifact: готовый отчёт в Markdown

Зачем нужен A2A

Проблема: изолированные агенты

Современные AI-агенты работают изолированно. Каждый фреймворк (LangChain, CrewAI, AutoGen, Claude Agent SDK) имеет свой формат взаимодействия. Это создаёт проблемы:

  • Vendor lock-in — агенты одного фреймворка не общаются с агентами другого
  • Дублирование — каждая организация создаёт агентов с одинаковым функционалом
  • Масштабирование — сложно строить системы из агентов разных команд
Без A2A: изолированные системы
LangChain-агент → свой протокол → LangChain-агент CrewAI-агент → свой протокол → CrewAI-агент AutoGen-агент → свой протокол → AutoGen-агент Между фреймворками — стена. Нужен кастомный код для каждой пары.
С A2A: экосистема агентов
Любой агент → A2A Protocol → Любой агент LangChain → A2A → CrewAI ✓ AutoGen → A2A → Claude Agent SDK ✓ Один стандарт для всех, агенты находят друг друга через Agent Cards.

Пять принципов A2A

  1. Agentic — агенты взаимодействуют как равноправные участники, без необходимости делиться внутренней архитектурой
  2. Built on standards — HTTP, JSON-RPC, SSE — широко поддержанные веб-стандарты
  3. Secure — поддержка корпоративной аутентификации и авторизации
  4. Support long-running tasks — задачи могут выполняться минуты, часы, дни
  5. Modality-agnostic — поддержка текста, файлов, изображений, структурированных данных

Ключевые концепции

Agent Card — визитка агента

Agent Card — это JSON-файл, который описывает возможности агента. Каждый A2A-совместимый агент публикует свою карточку по стандартному URL: /.well-known/agent.json

Task Lifecycle — жизненный цикл задачи

Каждая задача проходит через состояния:

СтатусОписание
submittedЗадача отправлена, ещё не принята
workingАгент работает над задачей
input-requiredАгент запрашивает дополнительные данные
completedЗадача выполнена, результат доступен
failedЗадача не выполнена (ошибка)
canceledЗадача отменена клиентом
Статус input-required позволяет агенту запросить уточнение. Например, агент-исследователь может спросить: «Нужна информация за какой период — последний месяц или год?». Клиент отправляет ответ, и агент продолжает работу. Это делает A2A пригодным для сложных задач с неопределённостью.

Artifacts — результаты работы

Artifacts — это данные, которые агент возвращает по завершении задачи. Каждый артефакт имеет тип MIME и может содержать текст, JSON, файлы или изображения.

A2A vs MCP: дополнение, не конкуренция

Плюсы

  • Взаимодействие между AI-агентами
  • Делегирование задач между системами
  • Обнаружение агентов (Agent Cards)
  • Долгие задачи с жизненным циклом
  • Мультимодальные данные (текст, файлы)

Минусы

  • Подключение агента к данным и инструментам
  • Доступ к GitHub, Slack, PostgreSQL и др.
  • Resources, Tools, Prompts
  • Синхронные операции (запрос-ответ)
  • Стандартизация tool calling

Пример совместного использования: AI-агент для HR получает задачу «найди кандидата на позицию Python-разработчика». Через MCP он обращается к базе данных резюме (MCP-сервер PostgreSQL) и к LinkedIn (MCP-сервер). Через A2A он делегирует техническое интервью агенту-интервьюеру от другого вендора.

Партнёры A2A (количество компаний по категориям)
Облачные платформы (Google, SAP, Salesforce)8%
AI-фреймворки (LangChain, CrewAI, AutoGen)12%
Корпоративный софт (ServiceNow, Workday)10%
Стартапы и инструменты20%
A2A был представлен в апреле 2025 года и продолжает развиваться. Спецификация может меняться, экосистема готовых реализаций ещё формируется. Для продакшена стоит следить за обновлениями спецификации на GitHub.

Техническая реализация

Структура Agent Card

{
  "name": "Research Agent",
  "description": "Агент для исследования тем и сбора информации из открытых источников",
  "url": "https://research-agent.example.com",
  "version": "1.0.0",
  "capabilities": {
    "streaming": true,
    "pushNotifications": true,
    "stateTransitionHistory": true
  },
  "authentication": {
    "schemes": ["Bearer"]
  },
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain", "application/json"],
  "skills": [
    {
      "id": "web-research",
      "name": "Web Research",
      "description": "Поиск и анализ информации в интернете",
      "tags": ["research", "web", "analysis"],
      "examples": [
        "Исследуй тему 'квантовые компьютеры в 2025'",
        "Найди статистику по рынку AI в России"
      ]
    },
    {
      "id": "summarize",
      "name": "Document Summarization",
      "description": "Суммаризация документов и статей",
      "tags": ["summary", "documents"]
    }
  ]
}

Отправка задачи (JSON-RPC)

import httpx
import json

A2A_SERVER = "https://research-agent.example.com"

async def send_task(description: str, session_id: str | None = None):
    """Отправка задачи агенту через A2A."""
    payload = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": "tasks/send",
        "params": {
            "id": "task-001",
            "sessionId": session_id or "session-001",
            "message": {
                "role": "user",
                "parts": [
                    {
                        "type": "text",
                        "text": description
                    }
                ]
            }
        }
    }

    async with httpx.AsyncClient() as client:
        response = await client.post(
            A2A_SERVER,
            json=payload,
            headers={"Authorization": "Bearer <token>"}
        )
        return response.json()

# Использование
result = await send_task(
    "Собери информацию о топ-5 AI-фреймворках для агентов в 2025 году"
)

# Ответ содержит Task с текущим статусом
# {
#   "jsonrpc": "2.0",
#   "id": 1,
#   "result": {
#     "id": "task-001",
#     "sessionId": "session-001",
#     "status": {"state": "working"},
#     "artifacts": []
#   }
# }

Подписка на обновления (SSE Streaming)

import httpx
import json

async def subscribe_to_task(task_id: str):
    """Подписка на обновления задачи через SSE."""
    payload = {
        "jsonrpc": "2.0",
        "id": 2,
        "method": "tasks/sendSubscribe",
        "params": {
            "id": task_id,
            "message": {
                "role": "user",
                "parts": [{"type": "text", "text": "Начни исследование"}]
            }
        }
    }

    async with httpx.AsyncClient() as client:
        async with client.stream(
            "POST",
            A2A_SERVER,
            json=payload,
            headers={"Accept": "text/event-stream"}
        ) as response:
            async for line in response.aiter_lines():
                if line.startswith("data: "):
                    event = json.loads(line[6:])
                    result = event.get("result", {})

                    # Обработка обновлений статуса
                    if "status" in result:
                        state = result["status"]["state"]
                        print(f"Статус: {state}")

                        if state == "completed":
                            for artifact in result.get("artifacts", []):
                                for part in artifact.get("parts", []):
                                    print(f"Результат: {part.get('text', '')}")
                            break

                        elif state == "input-required":
                            msg = result["status"].get("message", {})
                            print(f"Агент спрашивает: {msg}")
                            # Здесь нужно отправить ответ
                            break

                        elif state == "failed":
                            msg = result["status"].get("message", {})
                            print(f"Ошибка: {msg}")
                            break

Минимальный A2A-сервер (Python)

from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
import json
import asyncio

app = FastAPI()

# Agent Card
AGENT_CARD = {
    "name": "Summary Agent",
    "description": "Суммаризация текстов",
    "url": "https://summary.example.com",
    "version": "1.0.0",
    "capabilities": {"streaming": True},
    "defaultInputModes": ["text/plain"],
    "defaultOutputModes": ["text/plain"],
    "skills": [{
        "id": "summarize",
        "name": "Summarize",
        "description": "Создать краткое содержание текста"
    }]
}

# Хранилище задач (в продакшене — база данных)
tasks: dict = {}

@app.get("/.well-known/agent.json")
async def agent_card():
    return AGENT_CARD

@app.post("/")
async def handle_jsonrpc(request: Request):
    body = await request.json()
    method = body.get("method")

    if method == "tasks/send":
        return await handle_task_send(body)
    elif method == "tasks/get":
        return await handle_task_get(body)
    # ... другие методы

async def handle_task_send(body: dict):
    params = body["params"]
    task_id = params["id"]
    message = params["message"]
    text = message["parts"][0]["text"]

    # Создаём задачу
    tasks[task_id] = {
        "id": task_id,
        "status": {"state": "working"},
        "artifacts": []
    }

    # Выполняем суммаризацию (в реальности — вызов LLM)
    summary = f"Краткое содержание: {text[:100]}..."
    tasks[task_id]["status"]["state"] = "completed"
    tasks[task_id]["artifacts"] = [{
        "parts": [{"type": "text", "text": summary}]
    }]

    return {
        "jsonrpc": "2.0",
        "id": body["id"],
        "result": tasks[task_id]
    }
  1. Забыть про Agent Card — без /.well-known/agent.json другие агенты не смогут обнаружить ваш агент.
  2. Не обрабатывать input-required — если ваш агент не умеет запрашивать уточнения, сложные задачи будут провалиться.
  3. Игнорировать session_id — сессия позволяет вести многоходовый диалог. Без неё каждый запрос — изолированный.
  4. Не реализовывать tasks/get — клиент должен иметь возможность проверить статус задачи по ID, особенно для долгих задач.

Проверьте себя

Проверьте себя

1. Что такое Agent Card в протоколе A2A?

2. Как A2A и MCP соотносятся друг с другом?

3. Какой статус задачи означает, что агент запрашивает дополнительные данные?