Учебник

A2A 1.0 Agent Card JSON на практике: как проверять заявления о способностях, форму skills и данные между агентами

Если проверка упала, сначала назовите слой: распарсилось, прошло сгенерированную схему или совпало с обязательной таблицей спецификации.

Прошлая заметка, обход открытия, дошла до well-known, каталога и Card. Как писать поля — в более раннем разборе полей Agent Card. Сегодня поля не заполняем заново и hop’ы не повторяем. Карта уже в руках или только что снята GET. Вопрос: как утверждать, что она законна, и не примут ли отправленный Task за мусор. Текст — в спецификации A2A 1.0 §4.4 и §3.3.4. Сайтовский a2a.json сам пишет: это ненормативный пакет JSON Schema из proto. Переезд 0.3 → 1.0 — на что нового в v1.0.

Зелёный — не проход

Бросить сгенерированный пакет в Ajv, указать на корень, увидеть зелёный — самый частый ложный проход на ревью. Корень не Card. AgentCard, AgentSkill, Task и Message живут под $defs. Проверять корень пакета — не проверять ничего. Верный указатель — #/$defs/AgentCard. Трафик — #/$defs/Task или #/$defs/Message. Не оборачивайте Task схемой Card.

Даже с верным указателем сгенерированный пакет часто без required. Сентябрьская копия 2026 AgentCard и AgentSkill на сайте ставит additionalProperties: false, но не перечисляет обязательные ключи таблицы спецификации. Пустой name, skill без tags всё ещё могут быть зелёными. Источник обязательности — таблица спецификации: name, description, version, supportedInterfaces, capabilities, defaultInputModes, defaultOutputModes, skills. Каждому skill по-прежнему нужны id / name / description / tags.

Значит, проверяете минимум дважды. Первый проход: парсится, и сгенерированный AgentCard не взрывается на лишнем ключе. Второй: таблица спецификации утверждает непустоту. Только первый ловит остаточный верхнеуровневый url 0.3 через additionalProperties: false; skill без тегов ускользает. Только второй, без пакета, пропускает заблудший inputSchema. Нужны оба.

Этот слой Что ловит Что пропускает
JSON.parseСломанный синтаксис, обрезка, висячие запятыеВерна ли форма
Сгенерированный #/$defs/AgentCardВерхнеуровневый url 0.3, inputSchema на skillНаписаны ли обязательные ключи спецификации
Обязательная таблица §4.4Пустой name, нет tags, пустые skillsСовпадают ли флаги с операцией, которую вы собираетесь послать

Четыре слоя, каждый ловит один класс ошибок

Третий слой — флаги способностей. Спецификация §3.3.4 прямая: streaming ложно, а вы всё равно подписываетесь — агент ДОЛЖЕН вернуть UnsupportedOperationError. pushNotifications ложно, а webhook всё равно — PushNotificationNotSupportedError. extendedAgentCard ложно, а расширенную карту всё равно снимаете — тот же класс. Схема зелёная, обязательные ключи на месте, следующий прыжок всё равно может отбить. Фикстура должна писать «карта законна» и «эту операцию флаги разрешают» двумя шагами.

Четвёртый слой — трафик. Отправляете Task или Message. Card заново не POST’ите. InvalidAgentResponseError про форму ответа, не про визитку. MIME должен совпасть с defaultInputModes или inputModes skill; промах — ContentTypeNotSupportedError. Не проверяйте A2A skill через MCP inputSchema. Подсказки 1.0 — examples и MIME.

В сгенерированном пакете ещё snake_case patternProperties (supported_interfaces, default_input_modes). JSON спецификации — camelCase. Новые карты пишите по таблице спецификации. Если CI принимает оба набора ключей, Diff пачкается первым. Не копируйте имена полей proto на публичную карту, если только вы сознательно держите слой совместимости и фикстура это говорит.

Card: указывайте на AgentCard, не на корень пакета

Прибейте копию a2a.json в репозитории, выровняйте с опубликованной версией пакета. CI не должен бить плавающий latest URL. Ajv (или любая реализация 2020-12) получает указатель схемы #/$defs/AgentCard. Экземпляр — только что снятая карта или agent-card.json в репозитории. Выдавайте path и keyword. Path не бьёт в фикстуру — сначала подозревайте указатель, потом карту.

Самый ценный отказ первого прохода — лишний ключ. В 1.0 точка входа в supportedInterfaces. Оставшийся верхнеуровневый url, protocolVersion или supportsAuthenticatedExtendedCard краснеет при additionalProperties: false. Это остаток 0.3, не «больше полей — безопаснее». Разбор полей уже описал переезд. Сегодня только: валидатор должен пометить эти ключи ошибками, не игнорировать.

У каждого интерфейса три проверки: продакшен-url — абсолютный HTTPS (gRPC — host:port), protocolBinding — привязка, которую говорит клиент, protocolVersion — версия протокола вроде 1.0, не собственная version агента. Оба зовутся version. Фикстура должна утверждать их раздельно. Первый элемент предпочтителен. Нет общей привязки — нет разговора.

Skills: спецификация требует поля, которые схема часто пропускает

Сгенерированный AgentSkill тоже часто без required. Skill без tags может быть зелёным в Ajv и иметь пустую поверхность поиска — это уже было в тексте про открытие. Сегодняшнее утверждение: у каждого skill непустые id, name, description и хотя бы один тег. Пустой массив skills не проходит таблицу спецификации. Запись NO_SPEC только с хостом должна краснеть до регистрации, не после ключевого прыжка в пустоту.

На skill не должно быть inputSchema. additionalProperties: false считает это лишним ключом. Это дело инструментов MCP; см. внутреннюю статью про Schema. Skill 1.0 показывает examples и MIME. Залить таблицу параметров функции в карту — первый проход должен упасть. Падение на проверке дешевле уточнений после Task.

Карта ниже должна покраснеть на первом или втором проходе. Это не «почти рабочий» черновик. Смешаны верхнеуровневый url 0.3, skill без тегов и MCP-образный inputSchema. Diff рядом с Returns Specialist в репозитории. Три красных должны сесть на три path.

{
  "name": "Returns Specialist",
  "description": "Classifies return requests.",
  "version": "1.0.3",
  "url": "https://agents.example.com/returns/a2a",
  "protocolVersion": "1.0",
  "supportedInterfaces": [
    {
      "url": "https://agents.example.com/returns/a2a",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    }
  ],
  "capabilities": {
    "streaming": true
  },
  "defaultInputModes": ["text/plain"],
  "defaultOutputModes": ["text/plain"],
  "skills": [
    {
      "id": "classify-return",
      "name": "Classify a return",
      "description": "Decide the request type.",
      "inputSchema": {
        "type": "object",
        "required": ["orderId"],
        "properties": {
          "orderId": { "type": "string" }
        }
      }
    }
  ]
}
Проверка Как выглядит отказ Следующий прыжок
parse + указатель AgentCardВисячая запятая; верхнеуровневый url; inputSchema skillПочинить JSON / выкинуть ключи 0.3
Обязательное спецификации / непустоПустой name; skill без tags; пустые skillsДописать поля из §4.4
Флаги vs операцияПодписка при streaming false; снятие незаявленной расширенной картыСменить клиент или флаги Card

Трафик: Task — не визитка

Только после прохода Card — message/send. Тело следует привязке: JSON-RPC, gRPC или HTTP+JSON. Бизнес-объект внутри — Message или Task, не AgentCard. Проверять запрос определением Card бессмысленно краснеет. В сгенерированном пакете отдельные Task, Message, Part, Artifact. Фикстуры трафика меняют указатель. Строку Card не переиспользуйте.

Ответ тоже проверяйте. Спецификация складывает несоответствующий ответ агента в InvalidAgentResponseError. Машина состояний — submitted / working / completed / failed / canceled / rejected плюс прерванные input-required и auth-required. Считать completed единственным успехом — и «добавьте одно предложение» станет инцидентом. Этих ключей нет на Card. Открытие удалось, разговор нет — тикет должен назвать слой.

JSON ниже — фикстура отчёта проверки, не официальный RPC каталога или A2A. Четыре слоя сложены в один объект, чтобы diffить рядом с Card репозитория и одним запросом message/send. Проверки подписи утверждают только форму: у каждого элемента signatures[] нужны protected и signature (JWS RFC 7515). Настоящие ключи и настоящая проверка — на security-ревью. Приватный ключ в фикстуру не кладите.

{
  "kind": "card-validation-report",
  "note": "CI/review fixture — not an official A2A RPC",
  "target": "agent-card.json",
  "schema": {
    "bundle": "https://a2a-protocol.org/latest/spec/a2a.json",
    "pointer": "#/$defs/AgentCard",
    "normative": false
  },
  "parse": true,
  "schemaPass": false,
  "schemaErrors": [
    { "path": "/url", "keyword": "additionalProperties" },
    { "path": "/skills/0/inputSchema", "keyword": "additionalProperties" }
  ],
  "specChecks": [
    { "id": "required-name", "pass": true },
    { "id": "skills-tags-nonempty", "pass": false },
    { "id": "no-top-level-url", "pass": false }
  ],
  "capability": {
    "streaming": true,
    "clientWillStream": true,
    "ok": true
  },
  "next": "fix-card"
}

Подписи, фикстуры, локальные diff

Спецификация разрешает signatures в форме RFC 7515. Есть массив — сначала утвердите две обязательные непустые строки, потом решайте, проверять ли подпись. Нет массива — карта не становится незаконной: поле необязательное. Публичную карту пишите так, будто её снимут. Статический секрет и пароль интранета в Card не пишите. Расширенная карта идёт за сессией. Не делите один кэш «уже проверено» с публичной картой.

Не лейите Plugin SKILL.md и MCP tools/list в ту же проверку Card. Навыки в ящике — для кодирующего агента этого репозитория. Самоописание другой команды — Card. Свалить оба в один capability.json — и три сорванных слоя сядут в одну строку тикета.

В браузере хватит: Форматировать JSON — парсятся ли карта и отчёт; Проверить JSON Schema — навести официальный пакет на AgentCard, затем сменить указатель для Task; JSON Diff — законную карту против красной, поймать лишние ключи и отсутствующие tags. Данные не уходят с машины. Дальше: разбор полей Agent Card и обход открытия. Что такое каталог — обзор Registry.

Связанное: A2A Agent Card JSON Schema, Как агенты находят друг друга, Google Agent Registry.

Частые вопросы

Ajv зелёный на a2a.json. Нужен ли второй проход?

Нужен. Пакет сайта сам называет себя ненормативным, а сгенерированные определения часто без required. Источник обязательности — таблица спецификации. Зелёный значит только: не наступили на лишние ключи и типы.

Одна схема может проверить и Card, и Task?

Нет. Меняйте указатель. AgentCard и Task — два $defs. Обернуть запрос в Card — и сообщение об ошибке отправит следующий прыжок не туда.

Можно ли повесить JSON Schema на вход skill?

Skill 1.0 не принимает inputSchema. Это инструмент MCP. Сгенерированный пакет считает это лишним ключом. Детерминированные параметры остаются на hop инструмента. В карту не пишите.

Карта без signatures незаконна?

Нет. signatures необязательны. Есть массив — утвердите две обязательные строки JWS. Нет — всё равно гоните обязательное §4.4 и флаги.

Итог и следующие шаги

В 2026-м «проверить Agent Card» складывается в четыре слоя: parse, указатель пакета, обязательная таблица спецификации, затем флаги и трафик. Каталог и открытие — дверь внутрь. Проверка — страж, который решает, можно ли делегировать.

Порядок выкладки: прибить a2a.json, указатель AgentCard; прогнать непустые утверждения таблицы спецификации; проверить флаги и MIME до отправки Task; фикстуры трафика переключить на Task / Message. Как писать поля — разбор полей. Как нашли карту — текст про открытие. Законную карту, красную карту и отчёт сверяйте в JSONVue.