Structured outputs в OpenAI API: 5 сценариев, когда гарантия формата молча ломается
Компания OpenAI ещё в августе 2024 года добавила в API так называемый strict-режим для структурированных выходов (structured outputs), и за полтора года он стал стандартом де-факто, но на практике разработчики продолжают получать мусор в ответах, несмотря на формально корректные схемы.

Структурированные выходы (structured outputs, когда нейросеть отдаёт ответ в строгом формате, а не свободным текстом) обещают железную гарантию формата, но пять конкретных сценариев ломают эту гарантию так, что ошибку можно не заметить неделями.
Что стоит за обещанием strict-режима?
Strict-режим работает принципиально иначе, чем старый JSON Mode. JSON Mode обещал синтаксически корректный JSON и ничего больше: поля могли пропасть, типы «поплыть», ключи появиться из воздуха. Strict-режим компилирует JSON Schema (описание структуры ответа) в автомат и на каждом шаге генерации обнуляет вероятность токенов (минимальных единиц текста), которые уводят с валидного пути.
Модель физически не может вернуть невалидную структуру: нужных токенов у неё в этот момент просто нет. Сейчас этот подход в том или ином виде поддерживают все крупные провайдеры, а на моделях, которые разворачивают на собственных серверах, ту же роль выполняет grammar-constrained decoding (декодирование с грамматическими ограничениями) в инструментах llama.cpp, vLLM и SGLang.
Автор разбора на Хабре фиксирует пять конкретных ситуаций, когда код выглядит правильным, а гарантия не работает. Для тех, кто строит автоматизацию на базе ИИ-ответов, каждая из них чревата «тихими» ошибками, которые всплывают спустя дни или недели.
Какие ограничения strict-режим молча выбрасывает?
Первый и самый распространённый сценарий: ограничения из Pydantic Field (популярной Python-библиотеки для описания структур данных) молча исчезают при отправке в API.
Разработчик аккуратно прописывает:
- паттерн идентификатора пользователя (формат «USR-» и шесть цифр)
- диапазон возраста от 0 до 120
- длину имени от 1 до 100 символов
Запрос проходит, ответ парсится, тесты зелёные. Через неделю в базе обнаруживаются пользователи 250 лет от роду и идентификаторы вида «USER123», которые никакому паттерну не соответствуют.
Причина: strict-режим понимает не всю JSON Schema, а её подмножество. Из поддержки вычеркнуты ключевые слова: pattern, minimum, maximum, multipleOf, minItems, maxItems, minLength, maxLength. Современные обёртки часто «санитизируют» схему перед отправкой, тихо выбрасывая неподдерживаемые ключи. Ограничения были в коде, но до декодера не доехали.
Как Optional-поле ломает всю схему?
Второй сценарий знаком всем, кто извлекает данные из писем, где часть информации может отсутствовать.
Допустим, нужно извлечь адрес доставки из письма. Поле postal_code или весь блок shipping_address может быть пустым. Разработчик пишет Optional[str] = None, и API отвечает ошибкой валидации.
Дело в том, что strict-режим требует перечислить все свойства объекта в списке обязательных (required). Опциональности через отсутствие в required здесь не существует. Единственный способ сказать «поля может не быть», это использовать тип-объединение с null.
Pydantic для конструкции Optional[X] = None генерирует схему, где поле в required не попадает. Решение: убрать = None, оставив Optional[str]. Тогда поле обязательно присутствует в ответе, но его тип допускает null. Модель либо вернёт адрес, либо честно вернёт null, и это лучше, чем молчаливое отсутствие ключа.
Ещё одна ловушка того же порядка: флаг additionalProperties: false нужен на каждом уровне вложенности, включая вложенные модели. Pydantic сам ставит его только в корне. Хелперы chat.completions.parse и pydantic_function_tool дописывают автоматически, а при ручной сборке схемы обход дерева придётся написать самому.
Когда parsed возвращает пустоту вместо данных?
Третий сценарий: пайплайн извлечения данных работает стабильно, но примерно один запрос из двухсот падает с ошибкой, потому что parsed равен None.
Гарантия схемы вроде бы абсолютная. Откуда пустота? Вместе со strict-режимом в API появился новый тип ответа: отказ. Модель может не выполнить запрос, если сочтёт его нарушающим политики безопасности. Никакого невалидного JSON при этом не появляется, потому что JSON не появляется вообще: parsed остаётся пустым, а причина отказа лежит в отдельном поле refusal.
С точки зрения логики strict-режима всё честно. Но ветка обработки отказа в коде обычно не написана, и пайплайн падает.
Аргументы за structured outputs
- Структурная гарантия реальна. Декодер математически не пропустит невалидный JSON. Для задач, где важна форма ответа (таблицы, списки, карточки товаров), это избавляет от регулярных выражений и ручного парсинга.
- Стандартизация. Все крупные провайдеры поддерживают подход. Для команды это значит один формат взаимодействия с любой моделью.
- Автоматизация без человека. Когда ИИ-агент (автономная программа, действующая от имени пользователя) должен передать результат в следующий сервис, структурированный выход незаменим.
Почему гарантия формы не равна гарантии смысла?
- Семантика вне зоны контроля. Значение
confidence: 0.51иconfidence: 0.97одинаково валидны по схеме, а решения по ним принимаются разные. Strict-режим проверяет форму, не содержание. - Молчаливое урезание схемы. Обёртки тихо выбрасывают неподдерживаемые ключи. Разработчик уверен, что диапазон проверяется, а проверка уехала в пустоту.
- Ложное чувство безопасности. Зелёные тесты на десяти примерах не ловят ошибку, которая проявляется на одном запросе из двухсот. На масштабе в тысячи обращений это десятки «тихих» сбоев в день.
- Refusal-ветка. Отказ модели по политикам безопасности ломает пайплайн, если обработка этого случая не предусмотрена.
Даже валидная по схеме структура ничего не говорит о корректности содержимого. : Автор разбора на Хабре
Structured outputs, это не серебряная пуля, а половина решения. По моим наблюдениям, команды, которые переходят на strict-режим, первые две недели радуются отсутствию битого JSON, а потом начинают ловить ошибки посерьёзнее: валидные по форме, но бессмысленные по содержанию ответы.
Позиция простая: структуру отдайте декодеру, семантику заберите себе. Валидаторы на стороне кода (Pydantic field_validator, ретрай с обратной связью через Instructor) обязательны. Ветку обработки отказа пишите сразу, не после первого падения в проде.
Оговорка: для простых задач (классификация, выбор из списка) strict-режим работает без сюрпризов. Проблемы начинаются, когда схема усложняется и появляются вложенные опциональные объекты, паттерны, диапазоны.
Из доступных в РФ аналогов: YandexGPT и GigaChat пока не поддерживают strict-режим в том же виде, но извлечение структурированных данных через промпт (промпт, текстовая инструкция для модели) актуально для любого провайдера, и описанные ловушки с валидацией на стороне кода применимы один в один.
Что делать с этим прямо сейчас?
Автору на Дзене. Если вы используете ИИ для автоматической обработки комментариев, парсинга писем или генерации карточек, не доверяйте structured outputs без валидатора на выходе. Проверяйте результат кодом, а не глазами.
Маркетологу и аналитику. При массовом извлечении данных из текстов (отзывы, заявки, лиды) закладывайте ретрай-логику: модель получает текст ошибки валидации и генерирует ответ заново. Библиотека Instructor делает это из коробки.
Разработчику и предпринимателю. Убирайте = None из Optional-полей, проверяйте additionalProperties: false на всех уровнях вложенности, обрабатывайте refusal до обращения к parsed. Три правила, которые закрывают три из пяти описанных багов.
Strict-режим продолжит развиваться, и список поддерживаемых ключей JSON Schema, вероятно, будет расширяться. Но архитектурный принцип останется: декодер отвечает за форму, код отвечает за смысл. Те, кто выстроит валидацию сейчас, не будут переписывать пайплайн при каждом обновлении API.

Основатель dzen.guru. Эксперт по монетизации и продвижению на Дзен. Автор курса «Старт на Дзен 2026».
Читайте также

ИИ-агенты это не ускорение, если нет процесса: пилот Авито на 100 инженерах не показал роста
Мультимодальные ИИ-агенты (ИИ-агенты это программы, которые сами выполняют задачи инженера: создают файлы, редактируют код, запускают команды) обещают ускорить…

Семантический поиск без GPU и облака: «Первая Форма» уложила конвейер в 1,3 секунды на CPU
Семантический поиск (способность системы находить документы по смыслу, а не по точному совпадению слов) обычно требует облака, видеокарт и отдельных баз…

Как нейросеть дает ложные срабатывания
Нейросеть, которая ищет уязвимости в коде, часто кричит «пожар» там, где горит только лампочка кофемашины: тысячи ложных срабатываний хоронят настоящие…
Комментарии