В эпоху, когда искусственный интеллект становится повсеместным, возникает критическая проблема: агенты, созданные на разных платформах, с разными фреймворками и библиотеками, не могут эффективно взаимодействовать друг с другом. Каждый агент — это как остров, изолированный от остального мира.
Google столкнулся с этой проблемой при масштабировании своих внутренних агентных систем. В апреле 2025 года компания представила Agent2Agent (A2A) Protocol — открытый стандарт для взаимодействия AI-агентов. Позже протокол был передан в Linux Foundation, став по-настоящему открытым проектом с сообществом разработчиков.
A2A можно сравнить с HTTP в ранних днях Интернета: это фундаментальный протокол, который позволяет различным системам общаться друг с другом без необходимости знать их внутреннее устройство.
A2A (Agent-to-Agent) — это открытый протокол стандартизации взаимодействия между AI-агентами. Он определяет:
Ключевая идея A2A — opaque execution: агенты могут сотрудничать, не раскрывая своего внутреннего состояния, памяти, инструментов или логики. Они взаимодействуют исключительно через объявленные возможности и обмен информацией.
A2A протокол имеет трехслойную архитектуру, что обеспечивает гибкость и расширяемость:
┌─────────────────────────────────────────────────────────────┐
│ Layer 3: Protocol Bindings │
│ • JSON-RPC Methods │
│ • gRPC RPCs │
│ • HTTP/REST Endpoints │
│ • Custom Bindings │
└─────────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────────┐
│ Layer 2: Abstract Operations │
│ • Send Message │
│ • Stream Message │
│ • Get Task │
│ • List Tasks │
│ • Cancel Task │
│ • Subscribe to Task │
│ • Push Notification Config │
└─────────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: Canonical Data Model │
│ • Task │
│ • Message │
│ • AgentCard │
│ • Part │
│ • Artifact │
│ • Extension │
└─────────────────────────────────────────────────────────────┘
A2A определяет восемь фундаментальных концепций:
A2A использует три основных протокольных привязки для реализации абстрактных операций:
JSON-RPC 2.0 — это протокол удаленного вызова процедур, использующий JSON для кодирования сообщений. A2A использует его как основной формат полезной нагрузки для всех запросов и ответов.
Структура JSON-RPC запроса:
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": { ... }
}
Структура JSON-RPC ответа:
{
"jsonrpc": "2.0",
"id": 1,
"result": { ... }
}
В A2A протоколе JSON-RPC методы используют PascalCase без слэшей. Каждый метод соответствует gRPC RPC методу в прототипе.
| Метод | Описание |
|---|---|
SendMessage |
Отправка сообщения, возвращает Task или Message |
SendStreamingMessage |
Потоковая отправка сообщения с SSE |
GetTask |
Получение статуса задачи |
ListTasks |
Список задач с фильтрацией |
CancelTask |
Отмена задачи |
SubscribeToTask |
Подписка на обновления задачи |
CreateTaskPushNotificationConfig |
Настройка push уведомлений |
GetTaskPushNotificationConfig |
Получение конфигурации push уведомлений |
ListTaskPushNotificationConfigs |
Список конфигураций push уведомлений |
DeleteTaskPushNotificationConfig |
Удаление конфигурации push уведомлений |
GetExtendedAgentCard |
Получение расширенной карточки агента |
A2A серверы предоставляют HTTP(S) endpoint для обработки JSON-RPC запросов.
Основные HTTP методы:
POST /a2a/v1 — отправка JSON-RPC запросовGET /.well-known/agent-card.json — получение Agent CardДля потоковой передачи A2A использует Server-Sent Events (SSE) — стандарт W3C для односторонней потоковой передачи данных от сервера к клиенту.
Пример заголовков SSE:
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
Transfer-Encoding: chunked
Технические детали приведенные в статье могут стать не актуальными в связи с изменениями версий протокола - он живой и активно развивается
Перед взаимодействием с агентом клиент должен получить его Agent Card.
HTTP запрос:
GET /.well-known/agent-card.json HTTP/1.1
Host: agent.example.com
Пример ответа
{
"name": "Math Agent",
"description": "Performs mathematical calculations",
"url": "https://agent.example.com/a2a/v1",
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"securitySchemes": {
"apiKey": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
}
},
"protocolVersions": ["1.0"]
}
A2A имеет официальный Python SDK, который упрощает создание клиентов и серверов.
pip install a2a-sdk
from a2a.client import A2AClient
from a2a.types import SendMessageRequest, Message, Part, TextPart
from uuid import uuid4
# Создаем клиента
client = A2AClient(
endpoint_url="https://agent.example.com/a2a/v1",
api_key="your-api-key" # Если требуется аутентификация
)
# Создаем сообщение
message = Message(
role="user",
parts=[TextPart(text="Calculate the factorial of 10")],
message_id=str(uuid4())
)
# Отправляем сообщение
request = SendMessageRequest(message=message)
response = client.send_message(request)
print(f"Task ID: {response.task.id}")
print(f"Task status: {response.task.status.state}")
from a2a.server import A2AServer
from a2a.types import Task, TaskStatus, TaskState, Artifact, Part, TextPart
from uuid import uuid4
import asyncio
# Создаем простой агент, который отвечает на математические вопросы
class MathAgent:
async def handle_message(self, message):
text = message.parts[0].text
# Простая логика для вычисления факториала
if "factorial" in text.lower():
# Извлекаем число из текста
import re
numbers = re.findall(r'\d+', text)
if numbers:
n = int(numbers[0])
result = 1
for i in range(1, n + 1):
result *= i
artifact = Artifact(
artifact_id=str(uuid4()),
name="factorial_result",
parts=[TextPart(text=f"The factorial of {n} is {result}")]
)
return Task(
id=str(uuid4()),
context_id=str(uuid4()),
status=TaskStatus(state=TaskState.completed),
artifacts=[artifact]
)
# Если не распознано, возвращаем стандартный ответ
artifact = Artifact(
artifact_id=str(uuid4()),
name="response",
parts=[TextPart(text="I can calculate factorials. Try asking: 'What is the factorial of 5?'")]
)
return Task(
id=str(uuid4()),
context_id=str(uuid4()),
status=TaskStatus(state=TaskState.completed),
artifacts=[artifact]
)
# Создаем сервер
server = A2AServer(
host="0.0.0.0",
port=8000,
agent=MathAgent()
)
# Запускаем сервер
asyncio.run(server.start())
from a2a.client import A2AClient
from a2a.types import SendMessageRequest, Message, TextPart
from uuid import uuid4
client = A2AClient(endpoint_url="https://agent.example.com/a2a/v1")
message = Message(
role="user",
parts=[TextPart(text="Write a long essay about AI")],
message_id=str(uuid4())
)
request = SendMessageRequest(message=message)
# Используем потоковую передачу
for event in client.stream_message(request):
if hasattr(event, 'task'):
print(f"Task started: {event.task.id}")
elif hasattr(event, 'artifact'):
print(f"Artifact update: {event.artifact.parts[0].text[:50]}...")
elif hasattr(event, 'status'):
print(f"Status: {event.status.state}")
from fastapi import FastAPI, Request, HTTPException
from a2a.types import Task
import json
app = FastAPI()
@app.post("/webhook/a2a-notifications")
async def handle_a2a_notification(request: Request):
# Получаем токен из заголовка
notification_token = request.headers.get("X-A2A-Notification-Token")
# Валидация токена (в реальном приложении проверяйте подписи и т.д.)
if not notification_token:
raise HTTPException(status_code=401, detail="Missing notification token")
# Получаем тело запроса
body = await request.json()
# Парсим Task из JSON
task = Task(**body)
# Обрабатываем уведомление
if task.status.state == "completed":
print(f"Task {task.id} completed successfully!")
# Здесь можно отправить уведомление пользователю, обновить БД и т.д.
elif task.status.state == "failed":
print(f"Task {task.id} failed: {task.status.message}")
return {"status": "ok"}
Запрос:
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [
{
"kind": "text",
"text": "Calculate 15 * 27"
}
],
"messageId": "9229e770-767c-417b-a0b0-f0741243c589"
},
"metadata": {}
}
}
Ответ (Task-based):
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"id": "363422be-b0f9-4692-a24d-278670e7c7f1",
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
"status": {
"state": "completed",
"timestamp": "2025-04-02T16:59:35.331Z"
},
"artifacts": [
{
"artifactId": "9b6934dd-37e3-4eb1-8766-962efaab63a1",
"name": "calculation",
"parts": [
{
"kind": "text",
"text": "15 * 27 = 405"
}
]
}
],
"history": [
{
"role": "user",
"parts": [
{
"kind": "text",
"text": "Calculate 15 * 27"
}
],
"messageId": "9229e770-767c-417b-a0b0-f0741243c589",
"taskId": "363422be-b0f9-4692-a24d-278670e7c7f1",
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4"
}
],
"kind": "task",
"metadata": {}
}
}
Коды ошибок JSON-RPC 2.0:
| Код | Название | Описание |
|---|---|---|
| -32768 | Parse error | Невалидный JSON в запросе |
| -32600 | Invalid Request | Неверный формат запроса |
| -32601 | Method not found | Метод не найден |
| -32602 | Invalid params | Неверные параметры |
| -32603 | Internal error | Внутренняя ошибка сервера |
| -32099 to -32000 | Server error | Ошибки сервера (implementation-defined) |
Запрос:
{
"jsonrpc": "2.0",
"id": 2,
"method": "GetTask",
"params": {
"id": "363422be-b0f9-4692-a24d-278670e7c7f1"
}
}
Ответ:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"id": "363422be-b0f9-4692-a24d-278670e7c7f1",
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
"status": {
"state": "completed",
"timestamp": "2025-04-02T16:59:35.331Z"
},
"artifacts": [
{
"artifactId": "9b6934dd-37e3-4eb1-8766-962efaab63a1",
"name": "calculation",
"parts": [
{
"kind": "text",
"text": "15 * 27 = 405"
}
]
}
],
"kind": "task"
}
}
Запрос:
{
"jsonrpc": "2.0",
"id": 3,
"method": "SendStreamingMessage",
"params": {
"message": {
"role": "user",
"parts": [
{
"kind": "text",
"text": "Write a poem about Python"
}
],
"messageId": "bbb7dee1-cf5c-4683-8a6f-4114529da5eb"
},
"metadata": {}
}
}
SSE Response (поток событий):
data: {"jsonrpc": "2.0", "id": 3, "result": {"id": "task-123", "status": {"state": "submitted"}, "kind": "task"}}
data: {"jsonrpc": "2.0", "id": 3, "result": {"taskId": "task-123", "artifact": {"parts": [{"text": "In the realm of code"}], "append": false, "kind": "artifact-update"}}}
data: {"jsonrpc": "2.0", "id": 3, "result": {"taskId": "task-123", "artifact": {"parts": [{"text": "Where Python flows"}], "append": true, "kind": "artifact-update"}}}
data: {"jsonrpc": "2.0", "id": 3, "result": {"taskId": "task-123", "status": {"state": "completed"}, "final": true, "kind": "status-update"}}
Шаг 1: Инициация
{
"jsonrpc": "2.0",
"id": 4,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "Book a flight"}],
"messageId": "c53ba666-3f97-433c-a87b-6084276babe2"
}
}
}
Ответ (input-required):
{
"jsonrpc": "2.0",
"id": 4,
"result": {
"id": "3f36680c-7f37-4a5f-945e-d78981fafd36",
"status": {
"state": "input-required",
"message": {
"role": "agent",
"parts": [{"kind": "text", "text": "Where to, where from, and when?"}]
}
},
"kind": "task"
}
}
Шаг 2: Ответ на запрос
{
"jsonrpc": "2.0",
"id": 5,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "NYC to London, October 10"}],
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
"taskId": "3f36680c-7f37-4a5f-945e-d78981fafd36",
"messageId": "0db1d6c4-3976-40ed-b9b8-0043ea7a03d3"
}
}
}
Загрузка изображения:
{
"jsonrpc": "2.0",
"id": 6,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [
{"kind": "text", "text": "Analyze this image"},
{
"kind": "file",
"file": {
"name": "input.png",
"mimeType": "image/png",
"bytes": "iVBORw0KGgoAAAANSUhEUgAAAAUA..."
}
}
],
"messageId": "file-upload-123"
}
}
}
Получение обработанного файла:
{
"jsonrpc": "2.0",
"id": 6,
"result": {
"id": "file-task-123",
"artifacts": [
{
"artifactId": "processed-img-123",
"parts": [
{
"kind": "file",
"file": {
"name": "output.png",
"mimeType": "image/png",
"uri": "https://storage.example.com/processed/task-123/output.png?token=xyz"
}
}
]
}
],
"kind": "task"
}
}
Настройка push уведомления:
{
"jsonrpc": "2.0",
"id": 7,
"method": "SendMessage",
"params": {
"message": {
"role": "user",
"parts": [{"kind": "text", "text": "Generate Q1 report"}],
"messageId": "report-123"
},
"configuration": {
"pushNotificationConfig": {
"url": "https://client.example.com/webhook/a2a-notifications",
"token": "secure-client-token",
"authentication": {"schemes": ["Bearer"]}
}
}
}
}
Push уведомление (HTTP POST на webhook):
POST /webhook/a2a-notifications HTTP/1.1
Host: client.example.com
Content-Type: application/json
X-A2A-Notification-Token: secure-client-token
{
"id": "report-task-123",
"status": {"state": "completed"},
"kind": "task"
}
Задача в A2A проходит через следующие состояния:
┌──────────────────────┐ ┌──────────────────┐ ┌───────────────────────┐
│ TASK_STATE_SUBMITTED │────▶│ TASK_STATE_WORKING │────▶│ TASK_STATE_COMPLETED │
└──────────────────────┘ └──────────────────┘ └───────────────────────┘
▲ │ │
│ ▼ ▼
│ ┌───────────────────────┐ ┌──────────────────┐
│ │TASK_STATE_INPUT_REQUIRED│ │TASK_STATE_FAILED │
│ └───────────────────────┘ └──────────────────┘
│ │
│ ▼
│ ┌──────────────────┐
└───────────────────│TASK_STATE_CANCELED │
└──────────────────┘
▲
│
│ ┌──────────────────┐
└───────────────────│TASK_STATE_REJECTED │
└──────────────────┘
▲
│
│ ┌──────────────────────┐
└───────────────────│TASK_STATE_AUTH_REQUIRED│
└──────────────────────┘
A2A v1.0.0 определяет 9 состояний задачи (TaskState enum):
| Состояние | Значение | Описание |
|---|---|---|
TASK_STATE_UNSPECIFIED |
0 | Неуказанное состояние (значение по умолчанию) |
TASK_STATE_SUBMITTED |
1 | Задача создана, но еще не началась |
TASK_STATE_WORKING |
2 | Задача выполняется |
TASK_STATE_COMPLETED |
3 | Задача успешно завершена |
TASK_STATE_FAILED |
4 | Задача завершилась с ошибкой |
TASK_STATE_CANCELED |
5 | Задача была отменена |
TASK_STATE_INPUT_REQUIRED |
6 | Требуется дополнительный ввод от пользователя |
TASK_STATE_REJECTED |
7 | Задача была отклонена |
TASK_STATE_AUTH_REQUIRED |
8 | Требуется аутентификация пользователя |
Запрос:
{
"jsonrpc": "2.0",
"id": 8,
"method": "ListTasks",
"params": {
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
"status": "working",
"pageSize": 10,
"includeArtifacts": false
}
}
Ответ:
{
"jsonrpc": "2.0",
"id": 8,
"result": {
"tasks": [
{
"id": "task-1",
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
"status": {"state": "working"},
"kind": "task"
},
{
"id": "task-2",
"contextId": "c295ea44-7543-4f78-b524-7a38915ad6e4",
"status": {"state": "working"},
"kind": "task"
}
],
"nextPageToken": "next-token-123",
"pageSize": 10,
"totalSize": 2
}
}
A2A использует стандартные веб-механизмы безопасности:
Agent Card содержит информацию о требованиях аутентификации:
{
"securitySchemes": {
"google": {
"type": "openIdConnect",
"openIdConnectUrl": "https://accounts.google.com/.well-known/openid-configuration"
},
"apiKey": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
}
},
"security": [{"google": ["openid", "profile", "email"]}]
}
API Key:
POST /a2a/v1 HTTP/1.1
Host: agent.example.com
Content-Type: application/json
X-API-Key: your-api-key
{...}
Bearer Token (OAuth 2.0):
POST /a2a/v1 HTTP/1.1
Host: agent.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
{...}
Basic Auth:
POST /a2a/v1 HTTP/1.1
Host: agent.example.com
Content-Type: application/json
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
{...}
При использовании push уведомлений, webhook должен валидировать запросы:
import hmac
import hashlib
from fastapi import Request, HTTPException
async def validate_webhook(request: Request, secret: str):
# Получаем подпись из заголовка
signature = request.headers.get("X-A2A-Signature")
if not signature:
raise HTTPException(status_code=401, detail="Missing signature")
# Получаем тело запроса
body = await request.body()
# Вычисляем HMAC-SHA256
expected_signature = hmac.new(
secret.encode(),
body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
raise HTTPException(status_code=401, detail="Invalid signature")
return True
Agent Card — это метаданные агента для discovery. Обязательные поля:
| Поле | Тип | Описание |
|---|---|---|
name |
string | Имя агента |
description |
string | Описание агента |
supportedInterfaces |
string[] | Поддерживаемые интерфейсы |
provider |
string | Поставщик агента |
version |
string | Версия агента |
documentationUrl |
string | URL документации |
capabilities |
string[] | Возможности агента |
securitySchemes |
object | Схемы безопасности |
securityRequirements |
string[] | Требования безопасности |
defaultInputModes |
string[] | Режимы ввода по умолчанию |
defaultOutputModes |
string[] | Режимы вывода по умолчанию |
skills |
string[] | Навыки агента |
signatures |
object[] | Подписи агента |
{
"name": "Math Assistant",
"description": "Агент для математических вычислений",
"supportedInterfaces": ["a2a-json-rpc", "a2a-grpc"],
"provider": "Example Corp",
"version": "1.0.0",
"documentationUrl": "https://example.com/docs/math-agent",
"capabilities": ["calculation", "analysis"],
"securitySchemes": {
"apiKey": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key"
}
},
"securityRequirements": ["apiKey"],
"defaultInputModes": ["text", "file"],
"defaultOutputModes": ["text"],
"skills": ["arithmetic", "algebra", "calculus"],
"signatures": [
{
"algorithm": "ES256",
"keyId": "key-1",
"signature": "base64-encoded-signature"
}
]
}
A2A определяет 9 специфичных кодов ошибок:
| Код | Название | Описание |
|---|---|---|
| -32001 | TaskNotFoundError | Задача не найдена |
| -32002 | TaskNotCancelableError | Задача не может быть отменена |
| -32003 | PushNotificationNotSupportedError | Push уведомления не поддерживаются |
| -32004 | UnsupportedOperationError | Операция не поддерживается |
| -32005 | ContentTypeNotSupportedError | Тип контента не поддерживается |
| -32006 | InvalidAgentResponseError | Неверный ответ агента |
| -32007 | ExtendedAgentCardNotConfiguredError | Расширенная карточка агента не настроена |
| -32008 | ExtensionSupportRequiredError | Требуется поддержка расширений |
| -32009 | VersionNotSupportedError | Версия не поддерживается |
Все временные метки в A2A должны использовать ISO 8601 формат с UTC timezone:
YYYY-MM-DDTHH:mm:ss.sssZ
Пример: "2025-04-02T16:59:35.331Z"
Это обеспечивает однозначную интерпретацию времени независимо от часовых поясов.
Текущая версия A2A протокола: v1.0.0 (выпущена в октябре 2025).
Все реализации должны указывать версию протокола, которую они поддерживают.
Все JSON поля в A2A должны использовать camelCase:
protocol_version → JSON protocolVersioncontext_id → JSON contextIddefault_input_modes → JSON defaultInputModessecurity_requirements → JSON securityRequirementsЭто обеспечивает согласованность и совместимость с JavaScript/TypeScript экосистемой.
Все enum значения в JSON должны использовать SCREAMING_SNAKE_CASE:
TASK_STATE_COMPLETED (не completed)TASK_STATE_AUTH_REQUIRED (не auth-required)TASK_STATE_UNSPECIFIED (не unspecified)Это соответствует ProtoJSON спецификации и обеспечивает однозначную интерпретацию.
A2A также определяет gRPC binding для прямого использования gRPC:
Endpoint: a2a.A2A
RPC Methods:
SendMessage(SendMessageRequest) returns (SendMessageResponse)SendStreamingMessage(SendMessageRequest) returns (stream SendMessageResponse)GetTask(GetTaskRequest) returns (GetTaskResponse)ListTasks(ListTasksRequest) returns (ListTasksResponse)CancelTask(CancelTaskRequest) returns (CancelTaskResponse)SubscribeToTask(SubscribeToTaskRequest) returns (stream SubscribeToTaskResponse)CreateTaskPushNotificationConfig(TaskPushNotificationConfig) returns (TaskPushNotificationConfig)GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig)ListTaskPushNotificationConfigs(ListTaskPushNotificationConfigsRequest) returns (ListTaskPushNotificationConfigsResponse)DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest) returns (Empty)GetExtendedAgentCard(GetExtendedAgentCardRequest) returns (GetExtendedAgentCardResponse)Service Definition:
service A2A {
rpc SendMessage(SendMessageRequest) returns (SendMessageResponse);
rpc SendStreamingMessage(SendMessageRequest) returns (stream SendMessageResponse);
rpc GetTask(GetTaskRequest) returns (GetTaskResponse);
rpc ListTasks(ListTasksRequest) returns (ListTasksResponse);
rpc CancelTask(CancelTaskRequest) returns (CancelTaskResponse);
rpc SubscribeToTask(SubscribeToTaskRequest) returns (stream SubscribeToTaskResponse);
rpc CreateTaskPushNotificationConfig(TaskPushNotificationConfig) returns (TaskPushNotificationConfig);
rpc GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest) returns (TaskPushNotificationConfig);
rpc ListTaskPushNotificationConfigs(ListTaskPushNotificationConfigsRequest) returns (ListTaskPushNotificationConfigsResponse);
rpc DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest) returns (Empty);
rpc GetExtendedAgentCard(GetExtendedAgentCardRequest) returns (GetExtendedAgentCardResponse);
}
Создадим систему из трех агентов, которые работают вместе:
# agent1.py - Агент анализа данных
from a2a.server import A2AServer
from a2a.types import Task, TaskStatus, TaskState, Artifact, TextPart
from uuid import uuid4
class DataAnalysisAgent:
async def handle_message(self, message):
text = message.parts[0].text
# Имитация анализа данных
artifact = Artifact(
artifact_id=str(uuid4()),
name="analysis_report",
parts=[TextPart(text=f"Data analysis complete. Found {len(text)} characters.")]
)
return Task(
id=str(uuid4()),
context_id=str(uuid4()),
status=TaskStatus(state=TaskState.completed),
artifacts=[artifact]
)
# agent2.py - Агент генерации отчетов
class ReportGenerationAgent:
async def handle_message(self, message):
text = message.parts[0].text
# Генерация отчета
artifact = Artifact(
artifact_id=str(uuid4()),
name="generated_report",
parts=[TextPart(text=f"Report generated based on: {text}")]
)
return Task(
id=str(uuid4()),
context_id=str(uuid4()),
status=TaskStatus(state=TaskState.completed),
artifacts=[artifact]
)
# agent3.py - Оркестратор
class OrchestratorAgent:
def __init__(self):
self.client = A2AClient(endpoint_url="http://localhost:8001/a2a/v1")
async def handle_message(self, message):
# Отправляем данные на анализ
analysis_task = self.client.send_message(
SendMessageRequest(message=message)
)
# Получаем результат анализа
analysis_result = self.client.get_task(analysis_task.task.id)
# Отправляем результат на генерацию отчета
report_message = Message(
role="user",
parts=[TextPart(text=analysis_result.task.artifacts[0].parts[0].text)],
message_id=str(uuid4())
)
report_task = self.client.send_message(
SendMessageRequest(message=report_message)
)
return report_task.task
import base64
from a2a.client import A2AClient
from a2a.types import SendMessageRequest, Message, Part, TextPart, FilePart
from uuid import uuid4
client = A2AClient(endpoint_url="https://image-agent.example.com/a2a/v1")
# Загружаем изображение
with open("input.jpg", "rb") as f:
image_data = base64.b64encode(f.read()).decode("utf-8")
message = Message(
role="user",
parts=[
TextPart(text="Analyze this image and describe what you see"),
FilePart(
name="input.jpg",
mime_type="image/jpeg",
data=image_data
)
],
message_id=str(uuid4())
)
request = SendMessageRequest(message=message)
response = client.send_message(request)
print(f"Task ID: {response.task.id}")
# Получаем результат
task = client.get_task(response.task.id)
for artifact in task.task.artifacts:
for part in artifact.parts:
if hasattr(part, 'file') and part.file.uri:
print(f"Processed image available at: {part.file.uri}")
from a2a.client import A2AClient
from a2a.types import SendMessageRequest, Message, TextPart
from uuid import uuid4
client = A2AClient(endpoint_url="https://booking-agent.example.com/a2a/v1")
# Шаг 1: Инициация
message1 = Message(
role="user",
parts=[TextPart(text="Book a hotel")],
message_id=str(uuid4())
)
response1 = client.send_message(
SendMessageRequest(message=message1)
)
task_id = response1.task.id
context_id = response1.task.context_id
print(f"Task started: {task_id}")
# Шаг 2: Ответ на запрос
message2 = Message(
role="user",
parts=[TextPart(text="New York, July 15-20")],
message_id=str(uuid4()),
context_id=context_id,
task_id=task_id
)
response2 = client.send_message(
SendMessageRequest(message=message2)
)
print(f"Task status: {response2.task.status.state}")
# Шаг 3: Подтверждение
message3 = Message(
role="user",
parts=[TextPart(text="Confirm the first option")],
message_id=str(uuid4()),
context_id=context_id,
task_id=task_id
)
response3 = client.send_message(
SendMessageRequest(message=message3)
)
print(f"Booking confirmed: {response3.task.status.state == 'completed'}")
MCP и A2A дополняют друг друга:
Пример интеграции:
# Агент использует MCP для доступа к инструментам
from mcp import MCPClient
# Агент использует A2A для взаимодействия с другими агентами
from a2a.client import A2AClient
class IntegratedAgent:
def __init__(self):
self.mcp_client = MCPClient(url="http://localhost:3001")
self.a2a_client = A2AClient(endpoint_url="http://localhost:8002/a2a/v1")
async def handle_a2a_request(self, message):
# Используем MCP инструменты для обработки запроса
tools_result = await self.mcp_client.call_tool("search_web", {"query": message.parts[0].text})
# Отправляем результат другому агенту через A2A
result_message = Message(
role="agent",
parts=[TextPart(text=tools_result)],
message_id=str(uuid4())
)
response = self.a2a_client.send_message(
SendMessageRequest(message=result_message)
)
return response.task
from a2a.types import A2AError, TaskNotFoundError, UnsupportedOperationError
try:
response = client.send_message(request)
except TaskNotFoundError as e:
print(f"Task not found: {e}")
except UnsupportedOperationError as e:
print(f"Operation not supported: {e}")
except A2AError as e:
print(f"A2A error: {e}")
# Используйте contextId для группировки связанных задач
context_id = str(uuid4())
# Все сообщения в одной сессии используют один contextId
message1 = Message(
role="user",
parts=[TextPart(text="Start project")],
message_id=str(uuid4()),
context_id=context_id
)
message2 = Message(
role="user",
parts=[TextPart(text="Add task")],
message_id=str(uuid4()),
context_id=context_id,
task_id=previous_task_id
)
# Используйте streaming для операций, которые занимают больше 5 секунд
for event in client.stream_message(request):
if hasattr(event, 'artifact'):
# Обновляем UI с промежуточными результатами
update_ui(event.artifact.parts[0].text)
def validate_agent_card(card):
required_fields = ["name", "description", "supportedInterfaces", "provider", "version", "capabilities", "securitySchemes", "securityRequirements", "defaultInputModes", "defaultOutputModes", "skills", "signatures"]
for field in required_fields:
if field not in card:
raise ValueError(f"Missing required field: {field}")
if not card["supportedInterfaces"]:
raise ValueError("Agent must have supportedInterfaces defined")
if not card["capabilities"]:
raise ValueError("Agent must have capabilities defined")
return True
A2A протокол v1.0.0 — это фундаментальный шаг в развитии экосистемы AI-агентов. Он решает проблему фрагментации, позволяя агентам, созданным на разных платформах, эффективно взаимодействовать друг с другом.
| Метод | Описание |
|---|---|
SendMessage |
Отправка сообщения, возвращает Task или Message |
SendStreamingMessage |
Потоковая отправка сообщения с SSE |
GetTask |
Получение статуса задачи |
ListTasks |
Список задач с фильтрацией |
CancelTask |
Отмена задачи |
SubscribeToTask |
Подписка на обновления задачи |
CreateTaskPushNotificationConfig |
Настройка push уведомлений |
GetTaskPushNotificationConfig |
Получение конфигурации push уведомлений |
ListTaskPushNotificationConfigs |
Список конфигураций push уведомлений |
DeleteTaskPushNotificationConfig |
Удаление конфигурации push уведомлений |
GetExtendedAgentCard |
Получение расширенной карточки агента |
| Тип | Описание |
|---|---|
text |
Текстовое содержимое |
file |
Файл (байты или URI) |
data |
Структурированные данные (JSON) |
ui |
UI компоненты |
| Состояние | Описание |
|---|---|
TASK_STATE_UNSPECIFIED |
Неуказанное состояние |
TASK_STATE_SUBMITTED |
Задача создана |
TASK_STATE_WORKING |
Задача выполняется |
TASK_STATE_COMPLETED |
Задача завершена |
TASK_STATE_FAILED |
Ошибка |
TASK_STATE_CANCELED |
Отменена |
TASK_STATE_INPUT_REQUIRED |
Требуется ввод |
TASK_STATE_REJECTED |
Отклонена |
TASK_STATE_AUTH_REQUIRED |
Требуется аутентификация |
A2A Protocol Documentation. a2a-protocol.org. URL: https://a2a-protocol.org/latest/ (дата обращения: 2026-04-08)
A2A GitHub Repository. GitHub. URL: https://github.com/a2aproject/A2A (дата обращения: 2026-04-08)
A2A Python SDK on PyPI. Python Package Index. URL: https://pypi.org/project/a2a-sdk/ (дата обращения: 2026-04-08)
Model Context Protocol (MCP). Anthropic. URL: https://modelcontextprotocol.org/ (дата обращения: 2026-04-08)
JSON-RPC 2.0 Specification. jsonrpc.org. URL: https://www.jsonrpc.org/specification (дата обращения: 2026-04-08)
Server-Sent Events (SSE) Specification. WHATWG. URL: https://html.spec.whatwg.org/multipage/server-sent-events.html (дата обращения: 2026-04-08)
HTTP/REST Best Practices. MDN Web Docs. URL: https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview (дата обращения: 2026-04-08)
OAuth 2.0 Authorization Framework. IETF. URL: https://datatracker.ietf.org/doc/html/rfc6749 (дата обращения: 2026-04-08)
Google A2A Announcement. Google Developers Blog. URL: https://developers.googleblog.com/2025/04/agent-to-agent-protocol.html (9 апреля 2025) (дата обращения: 2026-04-08)
Linux Foundation A2A Donation. LF AI & Data Blog. URL: https://lfaidata.foundation/blog/2025/06/23/a2a-joins-lf-ai-data/ (23 июня 2025) (дата обращения: 2026-04-08)
A2A Protocol Specification. a2a-protocol.org. URL: https://a2a-protocol.org/latest/specification.html (дата обращения: 2026-04-08)
A2A Python SDK Examples. GitHub. URL: https://github.com/a2aproject/A2A/tree/main/examples (дата обращения: 2026-04-08)