AI-разработка

Почему AI-агентам теперь нужен JSON Schema: от Tool Calling к структурированным workflow

Около 15 мин

До 2024 большинство «агентов» — это промпт плюс две-три функции. В 2026 так уже нельзя: Function Calling описывает параметры, MCP регистрирует инструменты, Structured Outputs ограничивает возвращаемые значения, A2A согласует возможности, Agents SDK маршрутизирует по объектам handoff. Слои развивались отдельно, но сошлись на одном языке контракта — JSON Schema. Статья объясняет, почему он перестал быть «синтаксисом валидации в документации» и стал runtime агента, и куда класть Schema, когда вы переходите от одиночного Tool Calling к многошаговому workflow.

Кратко

Этап Без Schema Со Schema
Одиночный Tool Calling Параметры — строки, типы на удачу, лишнее поле тоже пройдёт parameters / inputSchema отсекают до вызова
Многошаговый агент Нижестоящий парсит прозу, handoff — по слову «billing» Объект handoff с output_type, маршрутизация тестируема
Переиспользование между протоколами Function Calling, MCP, A2A — свои копии, которые расходятся Одна Schema в Git, три адаптера на экспорт

Сразу вывод: JSON Schema — не «документ для модели», а валидатор, который хост агента гоняет перед каждым шагом, при передаче и на оценке.

Почему «теперь» нужен, а не «всегда был нужен»?

Ранний Tool Calling выживал из-за короткой цепочки: модель выдаёт имя функции и кусок параметров, хост исполняет, результат возвращается в диалог. Ошибка — повтор, пользователь видит. Schema в лучшем случае жила в README: «хорошо бы иметь».

В 2025–2026 цепочка удлинилась. Агент по тикетам сначала классифицирует, потом ищет заказ, потом решает — возврат или человек — и передаёт объект другому агенту. Если любой прыжок лишь «похож на JSON», следующие шаги примут ошибку за факт. Тогда Schema из документа становится runtime-шлюзом: не прошло — дальше не идём.

Второе давление — протоколы. В MCP Tool Schema vs Function Calling мы писали: API вендора использует parameters, MCP — inputSchema. A2A Extensions кладёт возможности в Agent Card. Три протокола не делят runtime, но все понимают подмножество JSON Schema. Не зафиксируете Schema сразу — будете вести три «почти одинаковых» определения и на третьей неделе обнаружите, что order_id где-то string, где-то number.

Прогноз DevDay 2026 говорит прямее: Structured Outputs, Function Calling и runtime агента сходятся в одну JSON-трубу. Schema — её калибр. Калибр не задан — любой следующий протокол потечёт.

У Tool Calling уже есть «структура». Где дыры?

В tools[] Function Calling уже лежит parameters, похожий на Schema. Поэтому многие команды считают: «мы уже структурированы». В проде ломается обычно одно из четырёх.

Первое: arguments всё ещё строка. Модель возвращает:

{
  "tool_calls": [{
    "id": "call_8f2a",
    "type": "function",
    "function": {
      "name": "create_refund",
      "arguments": "{\"order_id\":\"A-1042\",\"amount\":\"99.00\",\"reason\":\"late\"}"
    }
  }]
}

Хост обязан сделать ещё один JSON.parse. amount — строка или число, reason входит ли в enum, лишний note можно ли пустить в бухгалтерию — если parameters не принуждает хост, всё держится на везении модели. Записать Schema недостаточно: она сама не включится. strict: true ограничивает только сторону модели, хост всё равно валидирует сам.

Второе: у результата инструмента нет Schema. role: tool чаще всего свободная строка. Следующий шаг модель снова «поймёт» как текст. Успешный запрос, нулевой склад, нет прав — если форма разная, многошаговый workflow на третьем прыжке уйдёт в неверную ветку.

Третье: типы слишком широкие и не отсекают. priority с одним type: string позволяет модели выдать "urgent!!!". Schema без enum, required и additionalProperties: false — это комментарий.

Четвёртое: успех одного вызова маскирует провал workflow. Первый шаг lookup_order вернул лишний status: "ok", агент возврата принял status за статус заказа. Локальный happy path этого не покажет — взорвётся регрессионный набор. Ценность Schema здесь: «объект, который этот прыжок может отдать следующему» становится утверждаемым тестом, а не записью диалога.

Отлаживая такой вложенный JSON, сначала локально отформатируйте и проверьте синтаксис, затем сделайте Diff с определением, которое работало на прошлой неделе. Не вставляйте ответы инструментов с пользовательскими данными или секретами на публичный JSON-сайт.

У JSON Schema в агенте четыре роли

Если Schema — только «инструкция к параметрам инструмента», вы пропустите три слоя, которые реально растут в 2026. Полная картина:

  1. Вход инструмента. parameters Function Calling, inputSchema MCP. Решает, «что модель может запросить».
  2. Выход инструмента. Объект ответа запроса, записи, внешнего API. Решает, «чему следующий прыжок может верить». Этот слой сейчас чаще всего отсутствует.
  3. Передача агента (handoff). Результат классификации, намерение, уверенность, кому передать. В Agents SDK это основание маршрута, не саммари чата.
  4. Состояние workflow и оценка. Чекпоинты, трассы, eval-фикстуры. Долгая задача должна восстанавливаться из JSON, а не угадывать прогресс по истории диалога.

Пример Schema входа (инструмент возврата):

{
  "$id": "https://example.com/schemas/tools/create_refund.json",
  "type": "object",
  "properties": {
    "order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
    "amount": { "type": "number", "exclusiveMinimum": 0 },
    "reason": { "type": "string", "enum": ["late", "damaged", "duplicate", "other"] }
  },
  "required": ["order_id", "amount", "reason"],
  "additionalProperties": false
}

Пример Schema handoff (триаж → биллинг):

{
  "$id": "https://example.com/schemas/handoffs/triage_to_billing.json",
  "type": "object",
  "properties": {
    "intent": { "type": "string", "enum": ["refund", "exchange", "human"] },
    "order_id": { "type": "string" },
    "confidence": { "type": "number", "minimum": 0, "maximum": 1 },
    "from": { "const": "triage" },
    "to": { "const": "billing_agent" }
  },
  "required": ["intent", "order_id", "confidence", "from", "to"],
  "additionalProperties": false
}

Общее у двух файлов важнее различий: оба объекты, оба с enum, оба запрещают лишние поля, оба живут в Git, оба гоняет ajv (или аналог) в CI. Сменится снимок модели, протокол станет MCP или Function Calling — валидатор переписывать не надо. Как писать ключевые слова — в учебнике JSON Schema.

От одиночного вызова к структурированному workflow: на каких дверях стоит Schema

У одиночного Tool Calling одна дверь: валидны ли параметры. У структурированного workflow агента их минимум четыре.

Дверь 1: до выбора инструмента. Когда в каталоге больше десяти инструментов, совать всю Schema в один запрос дорого и повышает ложный выбор. Если сначала поиск, потом вызов — сам результат поиска должен быть коротким списком со Schema, а не сгенерированным описанием инструментов.

Дверь 2: до исполнения. parse → validate → execute. Validate не прошёл — верните структурированную ошибку (нет поля, неверный тип, вне enum), не бросайте сырую строку следующему прыжку. Этот же шаг отсекает лишние ключи в стиле prompt injection.

Дверь 3: до передачи. Триаж обязан отдать объект по Schema handoff — только тогда стартует billing. Условие маршрута: intent === "refund" && confidence >= 0.8, а не «в ответе мелькнуло слово возврат».

Дверь 4: восстановление и оценка. Долгая задача пишет чекпоинт валидным JSON:

{
  "run_id": "run_20260903_014",
  "step": "await_refund_ack",
  "state": {
    "order_id": "A-1042",
    "intent": "refund",
    "amount": 99.0
  },
  "schema": "https://example.com/schemas/state/refund_flow.json"
}

Набор оценок — те же объекты: дан вход, утверждаем, что выход проходит Schema и ключевые поля равны. Когда агент «поглупел», сначала Diff Schema и фикстур этой недели и прошлой — потом вините модель. Часто авария — лишний required или недостающее значение enum.

Как одну Schema скормить Function Calling, MCP и A2A

Не пишите слой протокола по отдельности. Рекомендуемый курс — один нормативный файл, три проекции:

Протокол Куда садится Schema Как экспортировать из одного файла
Function Calling function.parameters Скопировать type/properties/required, включить strict
MCP inputSchema Тот же объект как часть tools/list
Structured Outputs text.format.json_schema Handoff / итоговый ответ — те же определения объектов
A2A Agent Card / Extension metadata Внешнее объявление возможностей ссылается на тот же $id

При проекции следите за различиями подмножества: strict вендора часто требует, чтобы все property были в required, и отключает часть oneOf / $ref. Нормативный файл может быть полнее; экспортёр режет под цель, а CI делает структурный Diff «норматив vs проекция», чтобы никто не правил одну сторону руками.

MCP отвечает за поставку инструментов, Function Calling — за то, как модель выражает намерение вызова, A2A — за обнаружение и согласование между агентами. Schema не заменяет эти три слоя: это общий словарь полей. Тот же вывод, что в двух предыдущих статьях, только «словарь» из концепции становится файлами в репозитории.

На практике: JSON Schema как единый источник истины

Что можно сделать сразу, не дожидаясь ни одной презентации:

  • В репозитории заведите schemas/tools/, schemas/handoffs/, schemas/state/. У каждого файла стабильный $id и версия.
  • Скриптом экспортируйте три адаптации: Function Calling / MCP / Structured Outputs. В адаптерах поля руками не править.
  • CI через ajv (или аналог) валидирует: примеры payload, исторические фикстуры, прод-выборки (после деперсонализации) должны проходить текущую Schema.
  • Новые инструменты по умолчанию — additionalProperties: false и явный enum. Нужны открытые расширения — отдельный объект metadata, корень не расширяйте.
  • Больше десяти инструментов — маршрутизация или поиск. Не сваливайте весь каталог в один запрос.
  • Хост принимает arguments и как строку, и как объект, чтобы ужесточение типа у вендора не уронило весь сайт алертами.
  • Payload с секретами или пользовательскими данными смотрите только локально в браузере: формат, Diff, без загрузки.

Каталог может быть тонким. Важно сначала файл, потом промпт. «Верни JSON» в промпте в CI не упадёт; Schema — упадёт.

Частые ошибки

  • Schema в системном промпте — и готово. Ограничение на стороне модели и валидация на хосте — разные вещи. Без проверки хоста, если выключить strict или сменить снимок модели, контракт исчезает.
  • Валидировать только вход, не выход и не handoff. Аварии многошаговых агентов чаще случаются, когда «прошлый шаг вернул неожиданную форму».
  • Для каждого протокола своя «почти такая же» Schema. На третьей неделе они разойдутся. Экспортируйте, не копируйте.
  • Прятаться за широким type: object. Открытый объект = нет двери. Настоящее расширение — номер версии или явный metadata.
  • Считать Schema корпусом для обучения модели. Сначала это то, что гоняют валидатор и тесты. Ясное описание помогает выбрать инструмент, но не заменяет enum и required.
  • Менять поля на визуальном холсте и не возвращать в Git. Узлы на холсте — не источник истины. Сначала экспортируйте Schema, потом холст читает файлы.

FAQ

Можно ли выкатить Tool Calling без Schema?

Прототип — да. Когда инструменты фиксированы, шагов меньше трёх и сбой видит человек, строковых параметров часто хватает. Как только появляются ветки, автоповтор или второй агент, ошибки без Schema расползаются молча.

Не заменят ли JSON Schema слои Structured Outputs или MCP?

Нет. Это слои, которые потребляют Schema, а не другая система типов. Тренд 2026 — больше протоколов ссылаются на одну Schema, а не изобретают новый язык полей.

draft-07 или 2020-12?

Идите за своей библиотекой валидации и подмножеством вендора. В сценариях агентов реально нужны type, properties, required, enum, additionalProperties. Выбрать подмножество, которое стыкуется между CI и API модели, важнее, чем гнаться за свежим draft.

Внутренний закрытый API — нужны ли MCP и A2A?

Не обязательно. Внутренний замкнутый контур закрывается Function Calling + Schema. Ценность запаса MCP / A2A: когда та же Schema уже в репозитории, следующий протокол — это экспортёр, а не изобретение полей заново.

Как отлаживать упавший JSON инструмента или handoff?

Форматируйте локально в браузере, сверьте со Schema — какого required не хватает — затем Diff с прошлой версией. JSONSort данные не загружает. Не вставляйте ответы с секретами на публичный сайт.

Вывод

AI-агентам JSON Schema «теперь» нужен не потому, что модель вдруг разучилась читать естественный язык, а потому что workflow стал длиннее, протоколов больше, и ошибку обязан остановить хост. Tool Calling даёт намерение вызова; Structured Outputs — итоговый объект; MCP и A2A — поставку и согласование. Склеивают слои файлы Schema в репозитории: их можно валидировать, Diff-ить и экспортировать.

С сегодняшнего дня вместо более длинного промпта сначала напишите schemas/tools/create_refund.json. Модель сменится, протокол изменится — калибр лучше зафиксировать раньше.

Дальше по теме

Журнал: первая публикация