Structured Output
structured output — ответ модели по заданной схеме
Structured Output ограничивает форму ответа схемой: обязательными полями, типами, enum и вложенными объектами. Это снимает многие ошибки парсинга, но не подтверждает смысл значений. Приложение всё равно обрабатывает отказ, обрыв, сетевую ошибку и проверяет данные по своим бизнес-правилам.
Коротко
Structured Output задаёт машине контракт ответа. Вместо свободного текста модель должна вернуть структуру, совместимую с поддерживаемой схемой. Это полезно для извлечения данных, классификации и передачи результата в код, но поле правильного типа всё ещё может содержать неверное значение.
Зачем нужна схема
Допустим, приложение извлекает данные из резюме. Оно ждёт такой объект:
{
"name": "Анна Смирнова",
"skills": ["Python", "SQL"],
"experience_years": 4,
"needs_review": false
}
В свободном режиме модель может переименовать skills в competencies, записать стаж строкой или добавить пояснение перед JSON. Схема заранее ограничивает набор допустимых форм.
Что может описывать схема
В зависимости от API поддерживаются:
- объекты и массивы;
- строки, числа, boolean и null;
- обязательные поля;
- enum;
- вложенные структуры;
- ограничения на дополнительные ключи;
- описания полей.
Провайдеры обычно поддерживают не весь JSON Schema, а определённое подмножество. Сложные рекурсивные конструкции, некоторые validators и комбинации могут быть недоступны. Схему проверяют при запуске и держат версионированной рядом с кодом.
Как ограничивается генерация
Реализация может использовать constrained decoding: на каждом шаге разрешаются только токены, которые ещё могут привести к структуре по схеме. Так модель не сможет закрыть массив не той скобкой или поставить строку там, где допустим только boolean.
Это ограничение формы, не мысли. Если поле называется date, модель может вернуть синтаксически подходящую, но несуществующую дату. Если enum содержит approved, наличие этого значения не означает, что операция действительно одобрена.
Надёжный pipeline
входные данные
↓
запрос со схемой
↓
проверка статуса ответа
↓
парсинг и локальная валидация
↓
бизнес-правила и права доступа
↓
действие или ручная проверка
Локальная валидация всё равно полезна. Она защищает от несовместимости SDK, ошибок интеграции и изменений между версиями схемы.
Пример схемы
{
"type": "object",
"properties": {
"category": {
"type": "string",
"enum": ["billing", "delivery", "other"]
},
"summary": {"type": "string"},
"needs_human": {"type": "boolean"}
},
"required": ["category", "summary", "needs_human"],
"additionalProperties": false
}
Такая схема стабилизирует ключи и типы. Она не проверяет, верно ли определена категория и не пропущена ли важная деталь обращения.
Structured Output и JSON Mode
JSON Mode ограничивает ответ синтаксисом JSON. Он подходит, когда структура гибкая или приложение само её проверяет.
Structured Output добавляет контракт полей и типов. Он удобнее, когда данные передаются между компонентами и формат известен заранее.
Оба режима требуют обработки:
- отказа модели;
- превышения лимита;
- сетевого сбоя;
- недоступной модели;
- семантически неверного значения;
- изменения версии контракта.
Structured Output и Function Calling
Схема ответа описывает данные, которые нужно вернуть пользователю или приложению. Function Calling описывает запрос модели к инструменту.
В обоих случаях аргументы могут быть структурированы, но полномочия различаются. Объект {"action":"delete"} не должен удалять данные только потому, что соответствует schema. Приложение проверяет allowlist, права пользователя, область действия и необходимость подтверждения.
Когда режим особенно полезен
- извлечение полей из документов;
- классификация обращений;
- маршрутизация задач;
- построение карточек и таблиц;
- генерация параметров для внутреннего этапа;
- ответы API, которые читает другой сервис;
- многошаговый агент с узким набором состояний.
Для художественного текста строгая схема может только мешать. Там часто достаточно обычного ответа или лёгкого контейнера с несколькими полями.
Как проектировать хорошую схему
Делать поля однозначными
score непонятен без шкалы. confidence_0_to_1 точнее, но уверенность модели всё равно не является измеренной вероятностью. Иногда лучше needs_review с понятным правилом.
Разрешать неизвестность
Если данных может не быть, это стоит выразить через null, отдельный статус или поле missing_information. Иначе модель начнёт заполнять обязательный слот догадкой.
Не смешивать данные и команды
Поле с извлечённым адресом и команда отправить посылку — разные уровни. Сначала данные проверяются, затем отдельный компонент решает, можно ли выполнять действие.
Версионировать контракт
Добавление обязательного поля может сломать старого клиента. Версия схемы, миграция и обратная совместимость нужны так же, как в обычном API.
Проверять реалистичные ошибки
В тестах нужны пустой документ, противоречивые сведения, несколько кандидатов, вредоносная инструкция внутри текста и обрыв ответа. Идеальный пример покрывает только самую лёгкую ветку.
Что схема не гарантирует
- фактическую точность;
- полноту извлечения;
- отсутствие смещения;
- безопасность содержимого строки;
- право пользователя на действие;
- одинаковое поведение после смены модели;
- совместимость с любой конструкцией JSON Schema.
Схема делает интеграцию надёжнее, но не превращает вероятностную модель в детерминированную базу данных.
Частые ошибки
- Считать тип доказательством смысла. Integer может быть неверным числом.
- Не предусмотреть
unknown. Модель вынуждена выбрать один из неподходящих вариантов. - Делать схему слишком сложной. Её труднее поддерживать и не каждый API её принимает.
- Сразу выполнять структурированную команду. Нужна авторизация и проверка области действия.
- Игнорировать finish status. Отказ и обрыв — нормальные ветки протокола.
- Менять schema без версии. Старые клиенты перестают понимать ответ.
Частые вопросы
Нужен ли parser, если схема уже применена? Да. SDK может вернуть готовый объект, но граница приложения всё равно должна проверять ожидаемую версию и обрабатывать ошибку.
Можно ли использовать Pydantic, Zod или типы языка? Да, если SDK умеет преобразовать их в поддерживаемую схему. Полезно посмотреть итоговый JSON Schema, потому что не все возможности библиотеки переносятся в API.
Что делать с очень динамической структурой?
Использовать более общий объект, пару key/value, JSON Mode или несколько простых схем по разным веткам. Огромный универсальный контракт часто хуже набора маленьких.
Схема устраняет hallucination? Нет. Она не позволяет ответу выйти за форму, но выдуманное значение легко помещается в корректное поле.
Главное
Structured Output превращает желаемый формат в машинный контракт и заметно упрощает интеграцию. Его сила — в полях и типах, а не в истинности. Надёжное приложение дополнительно проверяет завершение, локальную схему, бизнес-правила, права доступа и содержание значений.