Начало / Миграция с 0.3 на 1.0
Миграция с 0.3 на 1.0
Что изменилось и в каком порядке обновлять
Версия 1.0 — первая стабильная версия A2A. Она делает протокол строже и понятнее, но содержит ломающие изменения во взаимодействии, поэтому код, написанный для 0.3, придётся обновить. Страница систематизирует и переводит документ «What's New in v1.0» из официальной документации.
С чего начать#
Официальная документация расставляет приоритеты так.
| Приоритет | Что сделать |
|---|---|
| Критично, сразу | Обновить разбор частей (Part) и событий потока (способ различения типов). Обновить разбор Agent Card (структура). Добавить заголовок A2A-Version во все запросы. |
| Высокий, в течение месяца | Перейти на курсорную пагинацию. Обновить обработку значений состояния и ролей. Добавить поддержку параметра returnImmediately. |
| Средний, в течение трёх месяцев | Реализовать проверку подписи Agent Card. Проверять требуемые расширения. Привести временные метки к ISO 8601. Реализовать новые типы ошибок. |
| Низкий, по возможности | Использовать расширенные метаданные. Реализовать аутентификацию mTLS. |
Переименование операций#
| Версия 0.3.0 | Версия 1.0 |
|---|---|
message/send |
SendMessage |
message/stream |
SendStreamingMessage |
tasks/get |
GetTask |
| (не было) | ListTasks — новая операция |
tasks/cancel |
CancelTask |
tasks/resubscribe |
SubscribeToTask |
agent/getAuthenticatedExtendedCard |
GetExtendedAgentCard |
tasks/pushNotificationConfig/set |
CreateTaskPushNotificationConfig |
tasks/pushNotificationConfig/get |
GetTaskPushNotificationConfig |
tasks/pushNotificationConfig/list |
ListTaskPushNotificationConfigs |
tasks/pushNotificationConfig/delete |
DeleteTaskPushNotificationConfig |
Модель запросов всех операций push-уведомлений изменилась, а TaskPushNotificationConfig стала плоской. Оригинал отмечает, что на время перехода предусмотрены псевдонимы имён операций.
Значения перечислений#
Все значения перечислений теперь записываются в стиле SCREAMING_SNAKE_CASE с префиксом типа. Так требует спецификация ProtoJSON. Это один из самых заметных ломающих изменений.
| Версия 0.3.0 | Версия 1.0 |
|---|---|
"submitted" |
"TASK_STATE_SUBMITTED" |
"working" |
"TASK_STATE_WORKING" |
"completed" |
"TASK_STATE_COMPLETED" |
"failed" |
"TASK_STATE_FAILED" |
"canceled" |
"TASK_STATE_CANCELED" |
"rejected" |
"TASK_STATE_REJECTED" |
"input-required" |
"TASK_STATE_INPUT_REQUIRED" |
"auth-required" |
"TASK_STATE_AUTH_REQUIRED" |
"user" |
"ROLE_USER" |
"agent" |
"ROLE_AGENT" |
// 0.3.0
{ "status": { "state": "completed", "timestamp": "2024-03-15T10:15:00Z" } }
// 1.0
{ "status": { "state": "TASK_STATE_COMPLETED", "timestamp": "2024-03-15T10:15:00.000Z" } }Время теперь указывается строго по ISO 8601 в UTC с миллисекундами: YYYY-MM-DDTHH:mm:ss.sssZ.
Part#
Структура частей полностью переработана. Вместо отдельных TextPart, FilePart и DataPart теперь одно сообщение Part.
Что изменилось:
- удалены отдельные типы
TextPart,FilePart,DataPart; - удалено поле-различитель
kind; - удалён вложенный объект
file; - появилось единое сообщение
Partс полемoneof content: тип определяется тем, какое поле есть:text,raw,urlилиdata; mediaTypeзаменяетmimeTypeи доступно для всех видов частей;filenameдоступно для всех видов частей, а не только для файлов;raw— встроенное двоичное содержимое (в JSON — base64);url— ссылка на файл, заменяетfile.fileWithUri.
// 0.3.0
{ "kind": "text", "text": "Hello world" }
{ "kind": "file", "file": { "fileWithUri": "https://example.com/doc.pdf", "mimeType": "application/pdf" } }
{ "kind": "data", "data": { "key": "value" } }
// 1.0
{ "text": "Hello world", "mediaType": "text/plain" }
{ "url": "https://example.com/doc.pdf", "filename": "doc.pdf", "mediaType": "application/pdf" }
{ "data": { "key": "value" }, "mediaType": "application/json" }Код, который определял тип по kind, нужно заменить проверкой наличия поля:
// 0.3.0
if (part.kind === "text") { return part.text; }
else if (part.kind === "file") { /* part.file.fileWithUri или fileWithBytes */ }
else if (part.kind === "data") { return part.data; }
// 1.0
if ("text" in part) { return part.text; }
else if ("url" in part) { return fetchFile(part.url); }
else if ("raw" in part) { return decodeBase64(part.raw); }
else if ("data" in part) { return part.data; }В Message и Artifact появилось поле extensions[] с URI расширений.
События потока#
Тип события больше не определяется полем kind: его определяет имя JSON-поля-обёртки.
- удалено поле
kind; - удалено логическое поле
finalуTaskStatusUpdateEvent: завершение потока определяется механизмом закрытия потока конкретной привязки; - новый шаблон: тип события определяется именем члена JSON,
statusUpdateилиartifactUpdate.
// 0.3.0
{ "kind": "status-update", "taskId": "...", "contextId": "...", "status": {}, "final": true }
// 1.0
{ "statusUpdate": { "taskId": "...", "contextId": "...", "status": {} } }// 0.3.0
if (event.kind === "status-update") { handleStatusUpdate(event); }
else if (event.kind === "artifact-update") { handleArtifactUpdate(event); }
// 1.0
if ("statusUpdate" in event) { handleStatusUpdate(event.statusUpdate); }
else if ("artifactUpdate" in event) { handleArtifactUpdate(event.artifactUpdate); }Дополнительно уточнено, что допускается несколько одновременных потоков одной задачи, и все они получают одни и те же события в одном порядке. Подробнее: «Обновления задач».
Agent Card#
Структура карточки переработана: сведения об интерфейсах собраны в один список.
| Что | Как было в 0.3.0 | Как стало в 1.0 |
|---|---|---|
| Версия протокола | protocolVersion в карточке |
В каждом интерфейсе: supportedInterfaces[].protocolVersion |
| Основной адрес | url |
supportedInterfaces[0].url |
| Предпочтительный транспорт и дополнительные интерфейсы | preferredTransport, additionalInterfaces |
Единый список supportedInterfaces[] (у каждого url, protocolBinding, protocolVersion) |
| Расширенная карточка | supportsAuthenticatedExtendedCard в корне |
capabilities.extendedAgentCard |
| Подписи | Не было | Необязательное поле signatures |
// 0.3.0
{
"protocolVersion": "0.3",
"url": "https://agent.example.com/a2a",
"preferredTransport": "JSONRPC",
"supportsAuthenticatedExtendedCard": true,
"additionalInterfaces": [...]
}
// 1.0
{
"supportedInterfaces": [
{ "url": "https://agent.example.com/a2a", "protocolBinding": "JSONRPC", "protocolVersion": "1.0" }
],
"capabilities": { "extendedAgentCard": true },
"signatures": [...]
}// 0.3.0
const endpoint = agentCard.url;
const transport = agentCard.preferredTransport;
const supportsExtended = agentCard.supportsAuthenticatedExtendedCard;
// 1.0
const primaryInterface = agentCard.supportedInterfaces[0];
const endpoint = primaryInterface.url;
const transport = primaryInterface.protocolBinding;
const supportsExtended = agentCard.capabilities.extendedAgentCard;Адрес публикации остался прежним: /.well-known/agent-card.json. Подробнее: «Agent Card».
Пагинация#
Операция ListTasks появилась только в 1.0, и она сразу курсорная. Если вы реализовывали постраничный список по-своему, перейдите на pageToken и nextPageToken:
let pageToken = undefined;
do {
const response = await listTasks({ pageToken, pageSize: 50 });
// обработать response.tasks
pageToken = response.nextPageToken;
} while (pageToken);Подробнее: «Методы».
Ошибки#
HTTP+JSON-ответы с ошибкой теперь используют ProtoJSON-представление google.rpc.Status вместо RFC 9457. Тип содержимого изменился с application/problem+json на application/json. Для ошибок A2A в details (JSON-RPC: data) ДОЛЖЕН быть объект google.rpc.ErrorInfo с полями reason и domain: "a2a-protocol.org". Примеры и список ошибок: «Ошибки».
HTTP-адреса и идентификаторы#
- Без префикса
/v1. В HTTP+JSON-привязке убран префикс/v1. БылоPOST /v1/message:send, сталоPOST /message:sendиGET /tasks/{id}. Версию при необходимости владелец агента может включить в базовый адрес. - Простые идентификаторы. Все идентификаторы теперь простые значения. Составные идентификаторы вида
tasks/{taskId}ушли: операции, где раньше использовались составные идентификаторы, теперь принимают родителя и ресурс отдельными полями. Например,tasks/{taskId}/pushNotificationConfigs/{configId}превратилось в отдельныеtask_idиid. Идентификаторы прямо соответствуют ключам в базе данных.
OAuth 2.0#
Поддержка OAuth 2.0 приведена в соответствие с актуальными рекомендациями по безопасности (BCP).
- Удалены потоки:
ImplicitOAuthFlow(риск утечки токена через историю и журналы браузера) иPasswordOAuthFlow(риск раскрытия учётных данных). - Добавлен поток:
DeviceCodeOAuthFlow(RFC 8628) для консольных инструментов, IoT-устройств и сред без удобного ввода. - Усилена безопасность: в
AuthorizationCodeOAuthFlowдобавлено полеpkceRequired(RFC 7636): оно показывает, обязателен ли PKCE. PKCE рекомендуется всем OAuth-клиентам и обязателен для публичных клиентов.
// 0.3.0: поток implicit (теперь удалён)
{ "implicitFlow": { "authorizationUrl": "https://auth.example.com/authorize", "scopes": { "read": "Read access" } } }
// 1.0: вместо него код авторизации + PKCE
{
"authorizationCodeFlow": {
"authorizationUrl": "https://auth.example.com/authorize",
"tokenUrl": "https://auth.example.com/token",
"pkceRequired": true,
"scopes": { "read": "Read access" }
}
}Что нового в 1.0#
- Новая операция
ListTasksс фильтрацией и курсорной пагинацией. - Мультитенантность. Поле
tenantво всех запросах и вAgentInterface: так можно обслуживать несколько агентов с одной точки входа. - Режим выполнения. Параметр
returnImmediatelyвSendMessageConfiguration: ждать завершения задачи (по умолчанию) или вернуться сразу. - Подпись Agent Card. JWS и каноническая форма JSON (RFC 8785).
- Требуемые расширения. Расширение в Agent Card можно пометить как обязательное (
required). - Согласование версий. Заголовок
A2A-Versionи версия протокола в каждом интерфейсе. - Взаимная аутентификация TLS. Объявление схемы mTLS среди схем безопасности.
- Новые зависимости.
google.rpc.StatusиErrorInfo, RFC 8785, RFC 7515, руководства по проектированию API Google и ISO 8601.
Порядок миграции#
Оригинал советует мигрировать поэтапно.
- Слой совместимости. Научитесь разбирать оба шаблона различения типов (старый и новый), определять версию протокола и поддерживать обе структуры Agent Card на время перехода.
- Двойная поддержка. Переведите все API на выдачу формата 1.0, оставив читателей для 0.3.0. Добавьте обработку заголовка
A2A-Versionи курсорную пагинацию рядом со старой постраничной. - Только 1.0. Уберите код совместимости с 0.3.0: старый разбор различителей, постраничную выдачу и поддержку двух форматов.
Стратегия обратной совместимости#
Каждый интерфейс (AgentInterface) объявляет собственную версию протокола. Агент может поддерживать несколько версий одновременно, открыв несколько интерфейсов. Клиент выбирает подходящий интерфейс из Agent Card, а SDK могут поддерживать несколько версий протокола и постепенно отказываться от старых.
Что проверить тестами#
- работу с данными и формата 0.3.0, и формата 1.0;
- проверку подписи Agent Card;
- курсорную пагинацию в крайних случаях: пустой результат, единственная страница;
- обработку новых типов ошибок;
- проверку требуемых расширений.
Расхождения внутри оригинала#
Пока мы сверяли источники, нашлись места, где документы описывают одно и то же по-разному. Это не ошибки перевода; сверяйтесь с a2a.proto.
- У события
TaskArtifactUpdateEventна странице «What's New» есть полеindex, а в таблице спецификации (раздел 4.2.2) его нет. - Примеры заголовка
A2A-Versionв разных местах спецификации показывают разные значения. - Для требований безопасности Agent Card встречаются два названия:
securityиsecurity_requirements.