Вы просите модель вернуть JSON — а в ответ получаете аккуратный объект, обёрнутый в «Конечно, вот ваши данные:» и три абзаца пояснений, из-за которых JSON.parse() падает. Знакомо? Когда ответ нейросети идёт не человеку, а в код — в базу, в интерфейс, в следующий шаг пайплайна — «почти правильный» JSON не годится: парсер либо принимает строку целиком, либо не принимает вообще. Именно эту проблему решает структурированный вывод (structured output) — режим, в котором провайдер гарантирует, что ответ будет валидным JSON заданной формы. Разберём, как это устроено у OpenAI, Anthropic и Google, чем это отличается от простой просьбы «ответь в формате JSON» и как составить схему, которую модель действительно выполнит.
Чем структурированный вывод отличается от «JSON в промпте»
Долгое время единственным способом получить JSON было попросить об этом словами: «верни ответ строго в виде JSON, без пояснений». Это работает в большинстве случаев, но не гарантированно. Модель может добавить вводную фразу, обернуть ответ в Markdown-блок ```json, забыть закрывающую скобку, поставить запятую после последнего элемента или придумать поле, которого вы не просили. На тысяче запросов такие сбои неизбежны, а каждый из них — это исключение в продакшене.
Отдельная промежуточная ступень — JSON-режим (JSON mode). Он гарантирует, что на выходе будет синтаксически корректный JSON, но не контролирует его структуру: набор полей, их типы и обязательность остаются на усмотрение модели. То есть парситься ответ будет всегда, но что именно окажется внутри — вопрос открытый.
Настоящий структурированный вывод идёт дальше. Вы передаёте схему (JSON Schema), а провайдер на уровне генерации ограничивает модель так, чтобы каждый следующий токен не нарушал эту схему. Технически это называют grammar-constrained sampling — выборка с ограничением по грамматике: модель просто не может сгенерировать символ, который сделает JSON невалидным или не соответствующим схеме. Результат — ответ, который гарантированно и парсится, и содержит ровно те поля нужных типов, что вы описали.
Три провайдера — три реализации
Идея у всех одна, но параметры и нюансы отличаются. Ниже — актуальное состояние на середину 2026 года.
OpenAI: Structured Outputs
OpenAI разделяет два механизма. Первый — response_format со схемой: вы передаёте объект {"type": "json_schema", "json_schema": {"strict": true, "schema": {…}}}, и модель отвечает объектом, который валиден по схеме. Второй — строгий вызов функций: при описании инструмента добавляете "strict": true, и аргументы, которые модель передаёт в функцию, тоже гарантированно соответствуют схеме. Это прямое продолжение механизма вызова инструментов (tool calling), только с жёсткой проверкой формы.
response_format = {
"type": "json_schema",
"json_schema": {
"name": "extract_contact",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"email": {"type": "string"},
"plan": {"type": "string", "enum": ["free", "pro", "enterprise"]}
},
"required": ["name", "email", "plan"],
"additionalProperties": false
}
}
}
Важная особенность: в ответе появляется отдельное поле refusal. Если модель по соображениям безопасности отказалась отвечать, она вернёт не «сломанную» под вашу схему структуру, а именно отказ в этом поле — его стоит проверять программно перед тем, как парсить основной ответ. Как получить ключ и сделать первый запрос, разбирали в отдельной заметке про OpenAI API.
Claude (Anthropic): output_config и strict tool use
У Anthropic структурированный вывод стал штатной функцией и вышел из беты. Основной параметр теперь — output_config.format (ранее в бете он назывался output_format, старое имя какое-то время ещё работает). Внутри — тип json_schema и сама схема:
output_config = {
"format": {
"type": "json_schema",
"schema": {
"type": "object",
"properties": {
"sentiment": {"type": "string", "enum": ["pos", "neg", "neutral"]},
"score": {"type": "number"}
},
"required": ["sentiment", "score"],
"additionalProperties": false
}
}
}
Второй механизм — строгие инструменты: у описания tool можно указать "strict": true, и тогда имя и аргументы вызова гарантированно валидны. Функция доступна на моделях Claude 4.5 и новее. Есть удобные обёртки в SDK: в Python это client.messages.parse() с моделями Pydantic, в TypeScript — хелперы вроде zodOutputFormat(), которые сами выводят схему из ваших типов. Подробности подключения — в заметке про Claude API.
Из ограничений: до 20 строгих инструментов на запрос, несовместимость с префиллингом ответа (заранее заданным началом реплики ассистента) и с цитированием источников. Раньше типичным приёмом было «подсунуть» модели начало ответа в виде {, чтобы заставить её сразу писать JSON, — с нативным структурированным выводом этот костыль больше не нужен.
Gemini: responseMimeType и responseSchema
У Google механизм включается двумя полями в конфигурации генерации. responseMimeType выставляется в "application/json", а responseSchema описывает форму ответа. Поддерживаются базовые типы (string, number, integer, boolean, object, array, null), enum для задач классификации и описания полей через description, которые дополнительно направляют модель.
generation_config = {
"response_mime_type": "application/json",
"response_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"tags": {"type": "array", "items": {"type": "string"}}
},
"required": ["title", "tags"]
}
}
Нюанс Gemini — управление порядком полей: модели важно, в каком порядке идут ключи, и для стабильности его задают явно. Как и у остальных, поддерживается не весь JSON Schema, а очень большие и глубоко вложенные схемы могут быть отклонены. Первый запрос и получение ключа описаны в заметке про Gemini API.
Как составить схему, которую модель точно выполнит
Несмотря на разные названия параметров, правила у всех провайдеров похожи, потому что упираются в одну и ту же механику ограниченной генерации.
Во-первых, поддерживается лишь подмножество JSON Schema. Экзотические конструкции (сложные oneOf, регулярные выражения в pattern, рекурсивные ссылки) могут не работать или замедлять генерацию — держите схему простой. Во-вторых, у OpenAI в строгом режиме все поля должны быть в required, а additionalProperties обязательно выставлен в false; если поле по смыслу необязательное, добавьте ему тип ["string", "null"] вместо того, чтобы убирать из required. В-третьих, используйте enum везде, где значение выбирается из фиксированного набора: это резко снижает шанс, что модель придумает свой вариант написания. И, наконец, поле description внутри схемы — не украшение: модель его читает, и грамотное пояснение к полю работает как мини-инструкция.
Подводные камни
Гарантия валидности схемы не означает гарантии смысла. Модель вернёт корректный по форме объект, но значения внутри всё равно нужно проверять: число может оказаться выдуманным, дата — несуществующей, а строка в enum прийти в другом регистре (например, POS вместо pos). Отдельно стоит обрабатывать обрыв генерации по лимиту токенов — если ответ упёрся в max_tokens, JSON окажется незакрытым несмотря на все ограничения, поэтому увеличивайте лимит с запасом под размер схемы.
Ещё один момент — задержка на первом запросе. Провайдеры компилируют грамматику из вашей схемы, и первый вызов с новой схемой отрабатывает медленнее; дальше скомпилированная грамматика кешируется (у Anthropic, например, на сутки), и повторные запросы идут быстро. Наконец, помните про безопасность: любой ответ может оказаться отказом (refusal у OpenAI, stop_reason: "refusal" у Claude), и такой случай нужно ловить до парсинга.
Когда что использовать
Если задача — извлечь данные и получить готовый объект (парсинг писем, разбор резюме, классификация тикетов), берите режим со схемой ответа: response_format у OpenAI, output_config.format у Claude, responseSchema у Gemini. Если же модель должна выбрать и вызвать действие — отправить письмо, сходить в базу, дёрнуть внешний сервис, — это территория строгих инструментов и tool calling. Структурированный вывод особенно важен там, где ответ модели встраивается в цепочку — в RAG-пайплайнах, в AI-агентах, в автоматических интеграциях, — то есть везде, где на другом конце стоит не человек, а код, которому нужен предсказуемый контракт данных. Выбор конкретного провайдера при этом чаще упирается не в сам JSON (он есть у всех), а в цену, лимиты и качество модели — это мы разбирали в сравнении нейросетей.
Вывод
Структурированный вывод превращает нейросеть из генератора текста в надёжный источник данных. Три крупнейших провайдера сегодня дают одну и ту же гарантию — валидный JSON по вашей схеме — разными параметрами: response_format и strict-функции у OpenAI, output_config.format и строгие инструменты у Claude, responseMimeType с responseSchema у Gemini. Держите схему простой, отмечайте обязательные поля, используйте enum для фиксированных значений, проверяйте отказы и обрывы — и парсинг ответов модели перестанет быть источником ночных инцидентов.
Частые вопросы
JSON-режим гарантирует только синтаксически корректный JSON, но не его структуру — набор полей и типы остаются на усмотрение модели. Структурированный вывод дополнительно заставляет ответ соответствовать вашей схеме: нужные поля нужных типов гарантированно на месте.
Нет. Гарантируется только форма — валидный JSON по схеме. Значения внутри (числа, даты, категории) модель всё равно может выдумать или прислать в другом регистре, поэтому их нужно проверять на своей стороне.
У OpenAI это модели с поддержкой Structured Outputs через response_format, у Anthropic — Claude 4.5 и новее через output_config.format, у Google — Gemini через responseSchema. Точный актуальный список смотрите в документации провайдера.
Нет. Раньше начало ответа подставляли вручную, чтобы модель сразу писала JSON. С нативным структурированным выводом этот костыль не нужен, а у Claude он и вовсе несовместим с режимом JSON-схемы.
Источники
- 1.Introducing Structured Outputs in the API — OpenAIhttps://openai.com/index/introducing-structured-outputs-in-the-api/
- 2.Structured outputs — Claude Platform Docshttps://platform.claude.com/docs/en/build-with-claude/structured-outputs
- 3.Structured output — Google Gemini APIhttps://ai.google.dev/gemini-api/docs/structured-output



