Structured Outputs в 2026: JSON-контракты для production LLM
Structured outputs в 2026: OpenAI Responses API, Claude structured JSON и strict tools, Gemini responseSchema, schema limits, retries и app-side validation.
Structured outputs в production нужны не потому, что “JSON красивее текста”, а потому что приложению нужен контракт, а не свободный prose. Если модель возвращает неструктурированный текст, вы получаете хрупкий парсинг, бесконечные edge cases и слабую совместимость между шагами пайплайна. Если модель возвращает output по схеме, у вас появляется надёжный интерфейс между LLM и кодом.
В 2026 важно различать не один, а три разных режима:
final JSON output: модель должна отдать конечный JSON-объект;
tool / function arguments: модель должна заполнить параметры действия по схеме;
app-side validation: даже валидный JSON нужно прогонять через Pydantic/Zod и бизнес-правила.
Structured output для LLM похож на typed API response. Без него вы просите модель “ответь как-нибудь понятно”. С ним вы говорите: “верни объект с такими полями, и только с ними”. Для backend-кода это разница между “надеемся, что распарсится” и “работаем по контракту”.
Не сводите structured outputs только к JSON mode. Валидный JSON сам по себе не решает production-задачу. Вам нужен либо schema-constrained final output, либо строго валидированные tool arguments. И почти всегда нужна вторая линия защиты на стороне приложения.
Модель возвращает текст вроде «Категория: billing, priority: high, next step: refund review». Дальше начинаются regex, split(':') и хрупкие if-ы.
С техникой
Модель возвращает объект `{category, priority, next_step}` по схеме. Код работает с типизированным контрактом, а не с текстом, который надо угадывать.
ПромптSchema-first extraction
Извлеки из тикета: «С меня дважды списали 12 990 тг, аккаунт premium, операция была вчера вечером». Верни object с полями `issue_type`, `amount`, `currency`, `account_tier`, `needs_human_review`.
У OpenAI в current docs structured outputs завязаны на Responses API и JSON Schema. Это уже не старый фокус на chat.completions как основной путь.
Практически есть два production-паттерна:
schema-constrained final output через structured output format;
strict function calling через tools с strict: true.
Что важно по official docs:
structured outputs поддерживают только subset of JSON Schema;
все поля обычно проектируются как required;
для optional-полей docs рекомендуют union с null;
function calling можно сделать strict, чтобы модель выдавала только валидные arguments;
response может завершиться refusal, и это нужно обрабатывать как отдельный path, а не как “сломанный JSON”.
То есть у OpenAI production-выбор обычно такой:
если нужен конечный typed result для вашего приложения, используйте structured final output;
если модель должна вызвать действие, используйте strict function calling;
если side effect дорогой или рискованный, final decision всё равно должен проходить app-side checks.
Не пытайтесь через function calling решать все задачи подряд. Если вам не нужно реальное действие, а нужен просто объект-результат, final structured JSON обычно проще, дешевле в оркестрации и понятнее для downstream-кода.
У Claude в 2026 уже есть отдельный structured output path, а не только старый паттерн “делайте fake tool и считайте, что это structured output”.
Anthropic docs разводят два режима:
structured JSON output через output_config.format;
tool use для action-oriented workflows.
Это важное улучшение относительно старых материалов. Теперь у Claude не обязательно моделировать extraction-задачу как tool call, если вам нужен просто конечный JSON.
При этом tool use всё ещё остаётся правильным выбором, когда:
модель должна вызвать реальный инструмент;
вы строите agent loop;
нужно строго ограничить допустимые аргументы действия.
Anthropic отдельно пишет про strict tool schemas и про то, что grammar compilation кэшируется примерно на 24 часа. Это значит, что новые схемы могут давать более дорогой и медленный first request, но затем становятся нормальным production path.
Даже если провайдер гарантирует schema-valid output, остаются три класса ошибок:
semantic errors: JSON валиден, но смысл неверный;
business rule errors: формат ок, но значение недопустимо;
policy errors: модель выбрала опасное или запрещённое действие.
Поэтому production stack почти всегда такой:
provider-level schema enforcement;
Pydantic / Zod validation;
business validation;
optional retry / fallback;
audit logging.
Примеры бизнес-проверок:
amount > 0;
email принадлежит разрешённому домену;
priority=critical требует human review;
refund=true запрещён без transaction lookup.
Если модель вернула объект строго по схеме, это ещё не разрешение выполнять действие. Для billing, legal, healthcare, security и agentic workflows schema-valid output должен быть только входом в policy layer, а не финальным решением.
У нас support workflow. Модель сначала классифицирует тикет, потом иногда вызывает action в CRM. Где нужен final JSON, а где tool schema?
Ответ модели
Классификация и triage должны возвращаться как final JSON. CRM action должен идти отдельным шагом через strict tool/function schema после app-side validation и policy checks.