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

Зачем ИИ нужен JSON Schema? Structured Output, Function Calling и контракт

Около 14 мин

С 2024 года страницы продуктов пишут JSON Schema, Function Calling и Structured Outputs как одну функцию. Это не так. Schema — язык контракта. Function Calling значит «пожалуйста, выполни». Structured Outputs значит «модель должна выдать эту форму». Статья разделяет три слова и объясняет, почему вероятностная модель должна сидеть за проверяемой Schema, а не за фразой «верни JSON».

Кратко

Понятие Одной строкой Где живёт
JSON Schema Контракт полей, типов, обязательных ключей и enum — не функция модели Файл .json в git; валидатор в CI
Function Calling Модель предлагает вызов; хост исполняет; побочные эффекты здесь tools[] / arguments
Structured Output Финальный ответ должен совпасть с этой формой; обычно без побочных эффектов json_schema / output_type

Запомнить разделение: Schema — диаметр, Function Calling — рука наружу, Structured Outputs — объект, который отдают. Сплющить в «пусть модель выдаст JSON» — и следующий шаг парсит прозу.

Почему три слова всегда путают?

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

Смешение прячет три разные работы:

  • arguments Function Calling часто всё ещё строка. Хост должен снова вызвать JSON.parse. Типы, enum и лишние поля ещё не остановлены.
  • Structured Outputs управляют только финальным ответом. Они не ищут заказ, не двигают деньги, не пишут в базу. Побочные эффекты по-прежнему идут через Function Calling.
  • Schema, которая живёт только в prompt, не может упасть в CI. «Верни JSON» никогда не падает. Schema в git — может.

Вопрос не «умеет ли ИИ JSON?», а: кто, в какой момент, каким контрактом останавливает незаконный объект. Три слова называют трёх разных «кто».

Что такое JSON Schema (это не функция модели)

JSON Schema — документ, описывающий форму JSON: какие ключи, какие типы, что обязательно, какие значения enum, можно ли лишние поля. Это строже, чем «похоже на JSON», и API моделей цитируют его чаще, чем тип TypeScript или protobuf.

Агентам и Structured Outputs обычно хватает этих ключевых слов:

  • type / properties: как выглядит объект.
  • required: какой отсутствующий ключ валит проверку.
  • enum / pattern / числовые границы: превратить «примерно» в конечное множество.
  • additionalProperties: false: лишние ключи падают — в том числе поля, пронесённые prompt injection.

Детали ключевых слов — в учебник JSON Schema. Здесь важно: Schema существует прежде всего для валидаторов и тестов. Ясный description помогает модели выбрать инструмент; он не заменяет enum и required.

{
  "$id": "https://example.com/schemas/order.json",
  "type": "object",
  "properties": {
    "order_id": { "type": "string", "pattern": "^A-[0-9]{4,}$" },
    "amount": { "type": "number", "exclusiveMinimum": 0 },
    "status": { "type": "string", "enum": ["paid", "refunded", "pending"] }
  },
  "required": ["order_id", "amount", "status"],
  "additionalProperties": false
}

Контракт заказа выше можно скормить parameters Function Calling, json_schema Structured Outputs и ajv на хосте. Поле меняют в одном месте.

Что такое Function Calling?

Function Calling (и Tool Calling шире) — это не «модель выполняет функцию». Модель только выдаёт имя и аргументы. Хост парсит, проверяет, исполняет и кладёт результат обратно в разговор. Запросы склада, возвраты, тикеты — побочные эффекты живут здесь.

Вендоры пишут инструменты как tools[]. Объект parameters — подмножество Schema:

{
  "type": "function",
  "function": {
    "name": "lookup_order",
    "description": "Look up an order by ID",
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "pattern": "^A-[0-9]{4,}$"
        }
      },
      "required": ["order_id"],
      "additionalProperties": false
    },
    "strict": true
  }
}

гид по AI Agent рисует этот шаг внутри полного цикла. MCP Tool Schema vs Function Calling показывает, что MCP inputSchema и вендорские parameters — два написания одного словаря.

Уже выглядит структурированным. Дыры: arguments часто всё ещё строка; у результатов инструментов часто нет Schema; strict связывает только сторону модели. Без проверки хоста, число amount или строка — всё ещё вопрос сегодняшнего снимка.

Что такое Structured Outputs?

Structured Outputs (строгая форма JSON mode) ограничивают собственное финальное сообщение модели. Классификация, извлечение, объект сводки, handoff агента — внешнего побочного эффекта нет, но следующему шагу нужна фиксированная форма.

Имена полей различаются: response_format, text.format.json_schema, output_type. Ядро одно: отдать Schema декодеру и обрезать незаконные пути токенов.

{
  "type": "json_schema",
  "json_schema": {
    "name": "order",
    "strict": true,
    "schema": {
      "$id": "https://example.com/schemas/order.json",
      "type": "object",
      "properties": {
        "order_id": { "type": "string" },
        "amount": { "type": "number" },
        "status": {
          "type": "string",
          "enum": ["paid", "refunded", "pending"]
        }
      },
      "required": ["order_id", "amount", "status"],
      "additionalProperties": false
    }
  }
}

прогнозы DevDay 2026 записал этот слой, Function Calling и рантайм агента как одну JSON-трубу. Диаметр по-прежнему Schema: сначала задать его, потом подключать протоколы.

С strict: true пропущенные поля, плохие enum и лишние ключи часто останавливаются на стороне модели. Это заменяет prompt «ответь JSON-ом». Это не заменяет вторую проверку хоста и не заменяет Function Calling.

Как три слоя складываются?

Сплющить в «пусть модель выдаст JSON» — и работы пропадают. Разделить:

  1. JSON Schema — общий диаметр: словарь полей, обязательные ключи, enum. Не вызывает и не порождает. Только говорит, как выглядит «законное».
  2. Function Calling — рука наружу. Модель просит побочный эффект. Аргументы должны пройти Schema; результаты тоже.
  3. Structured Outputs — объект, который отдают. Когда шаг кончается, сам ответ должен быть этой формой.

Сравнение, чтобы их перестали считать синонимами:

Слой Что делает модель Без Schema
Function Calling Предлагает вызов, ждёт хост Аргументы — проза; лишние ключи могут попасть в биллинг
Structured Output Сразу выдаёт финальный объект Дальше поля выдирают из абзаца
JSON Schema Ничего — только линейка Линейку, живущую только в prompt, в CI не измерить

Многошаговый агент использует оба: Structured Outputs для объекта сортировки, Function Calling для поиска и возврата. AI-агенты и workflow JSON Schema про порядок ворот. Эта статья только называет три блока.

Почему ИИ нельзя обойтись без JSON Schema?

Потому что модель и программа — разные виды вещей. Модель продолжает токены по вероятности; программа читает объект по правилам. Если стык только «сегодня это выглядело как JSON», пропущенное поле, число-строка, enum "Paid", лишний note — каждый может стать фактом для следующего шага.

До 2024 цепочка была короткой: один чат, одна функция, человек видел сбой. В 2026 она длиннее: классифицировать → найти → вернуть деньги или человеку → передать объект другому агенту. Если любой шаг только «похож на JSON», следующие шаги замораживают ошибку.

Для того же заказа модель в любой момент может выдать этот «выглядит законно» объект:

{
  "order_id": "A-1042",
  "amount": "99.00",
  "status": "Paid",
  "note": "please hurry"
}

amount — строка, у status неверный регистр, появился note. Без Schema дальше могут склеить 99.00 в SQL как текст или счесть Paid неизвестным статусом и повторить. При отладке сначала форматировать локально, затем JSON Diff с объектом прошлой недели. Не вставляйте ответы с данными пользователя на публичный JSON-сайт.

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

Одна Schema, две проекции

Не пишите вручную «почти одинаковые» определения для Function Calling и Structured Outputs. Форма — один исходный файл, два экспортёра:

  1. Держать schemas/order.json в репозитории со стабильным $id и версией.
  2. Экспорт Function Calling: обрезать до function.parameters и включить strict. Поиску часто нужен только order_id — не запихивать весь заказ в аргументы.
  3. Экспорт Structured Outputs: тот же объект как json_schema / output_type для извлечения, сортировки и финального ответа.

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

Хост всё равно должен проверять

strict: true связывает только декодирование модели. Смена снимка, выключенный strict, второй разбор arguments, инструмент с открытым объектом — всё может обойти сторону модели. Ту же Schema гонять в трёх местах хоста:

  • До выполнения инструмента. parse → validate → execute. При ошибке вернуть структурированную ошибку. Не передавать сырую строку дальше.
  • После приёма финального объекта модели. Успех Structured Outputs — не ваше бизнес-правило (лимиты, валюта, доступ).
  • При записи состояния или фикстур eval. Checkpoint и eval должны быть законным JSON. Не угадывать прогресс по журналу чата.

Ограничения стороны модели и проверка хоста — две работы. Если strict выключен или вы сменили API, Schema, живущая только в prompt, исчезает.

Как внедрять в 2026 (сначала файлы, потом prompt)

  • Создать schemas/tools/, schemas/extract/ и schemas/state/. У каждого файла $id и версия.
  • Писать Schema раньше, чем «верни JSON». Предложение в prompt не упадёт в CI.
  • Побочные эффекты (запрос, платёж, письмо) — через Function Calling. Классификация, извлечение и handoff — через Structured Outputs.
  • Новые объекты по умолчанию с additionalProperties: false и явным enum. Для расширения — отдельный metadata, корень не ослаблять.
  • Больше десяти инструментов — сначала найти, потом вызвать. Не сваливать каталог в один запрос.
  • Секреты и пользовательские поля смотреть только в браузере: форматировать, JSON Diff, без загрузки.

Каталог может быть тонким. Важно, чтобы файлы шли раньше prompt. Модели сменятся, протоколы добавятся. Диаметр должен стабилизироваться первым.

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

  • Принимать Structured Outputs за Function Calling. Первое не выполняет код. Поиск и возвраты по-прежнему нуждаются в слое инструментов.
  • Писать «верни JSON» только в prompt. Нет Schema в git — нет теста, который может упасть.
  • Проверять только входы инструментов, не финальный объект. Инциденты извлечения и сортировки случаются на ответах, которые «уже похожи на JSON».
  • Писать вручную почти одинаковую Schema на каждый протокол. К третьей неделе они разъедутся. Экспортировать, не копировать.
  • Прятаться за широким type: object. Открытый объект — не ворота. Версионировать или добавить явный metadata.

FAQ

Могут ли Structured Outputs заменить Function Calling?

Нет. Structured Outputs ограничивают форму финального ответа. Function Calling просит хост выполнить побочный эффект. Поля заказа извлекать первым; склад или возврат — вторым. Они должны ссылаться на одну Schema, а не заменять друг друга.

У Function Calling уже есть parameters — нужен ли ещё файл Schema?

Да. parameters — проекция, не источник. Отдельный файл кормит Structured Outputs, MCP, оценки и ajv на хосте. Править только копию внутри tools[] — и к третьей неделе она разъедется с Schema извлечения.

Агента нет, только одно извлечение. Нужна ли Schema?

Ещё сильнее. Если результат сразу идёт в базу или таблицу, нет следующего шага, который «поймёт ещё раз». Structured Outputs плюс проверка хоста — минимальный полный набор.

Включил strict: true. Нужно ли ещё проверять на хосте?

Да. strict управляет декодированием. Хост управляет бизнес-правилами, вторым разбором и запасным путём, когда вендор выключает strict. Двое дешёвых ворот; убрать одни — и смена модели это покажет.

Как локально посмотреть отклонённый JSON?

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

Вывод

ИИ нужен JSON Schema не потому, что модели вдруг разучились читать естественный язык, а потому что вероятностный вывод должен встретить детерминированные ворота. Function Calling говорит «сделай это». Structured Outputs говорит «верни этот объект». Schema — диаметр, который они делят.

Начните сегодня с schemas/extract_order.json, потом выбирайте API. Модели сменятся. Протоколы сдвинутся. Диаметр должен стабилизироваться первым.

Дальше почитать

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