Структурированный вывод (JSON mode)
| Задача | получать от LLM машиночитаемые данные вместо свободного текста |
|---|---|
| Формат схем | JSON Schema |
| Уровни гарантий | промпт → JSON mode → строгая схема (constrained decoding) |
| Библиотеки | Pydantic, Instructor, Outlines, XGrammar, llguidance |
| Поддержка в API | OpenAI, Anthropic, Google, vLLM, llama.cpp |
Структурированный вывод (англ. structured output) — режим работы языковой модели, при котором ответ представляет собой данные заданной формы, чаще всего JSON по схеме, а не свободный текст. Это нужно везде, где ответ модели читает программа: извлечение полей из документов, классификация, заполнение форм, вызов функций в агентах. Без специальных мер модель время от времени добавляет пояснения, пропускает поля, путает типы или выдаёт синтаксически сломанный JSON — и каждый такой случай ломает конвейер.
Уровни гарантий
| Подход | Как | Гарантия |
|---|---|---|
| Просьба в промпте | «Ответь только JSON вида …» с примером | никакой; ошибки в процентах случаев |
| JSON mode | API гарантирует синтаксически корректный JSON | синтаксис, но не схема |
| Строгая схема | API или движок ограничивает генерацию по JSON Schema | синтаксис и структура |
| Валидация и повтор | проверить ответ, при ошибке отправить её модели | зависит от числа попыток |
Хорошая практика — сочетать строгую схему на уровне генерации и валидацию на стороне приложения: схема гарантирует форму, но не правильность значений.
Ограниченное декодирование
Модель генерирует текст по одному токену, каждый раз выбирая из распределения вероятностей по словарю. Ограниченное декодирование (constrained decoding) на каждом шаге обнуляет вероятность токенов, недопустимых по грамматике: после {"age": разрешены только цифры, после закрытия последнего обязательного поля — только }. Схема JSON или регулярное выражение компилируется в конечный автомат или грамматику, и проверка выполняется быстро.[4]
На этом принципе работают режим strict в API OpenAI, структурированный вывод Anthropic, движки Outlines, XGrammar и llguidance, встроенные в инференс-серверы, и грамматики GBNF в llama.cpp.[1][2][5][7] Поддерживается не вся JSON Schema: обычно доступны объекты, массивы, перечисления, обязательные поля и вложенность, но часть ограничений (форматы, сложные условия) не применяется.[3]
Пример
Схему удобно описывать классами Pydantic и получать сразу типизированный объект (см. Dataclasses и Pydantic):
from pydantic import BaseModel, Field
from openai import OpenAI
class Vacancy(BaseModel):
title: str
salary_from: int | None = Field(description="в рублях, без НДФЛ")
remote: bool
stack: list[str]
client = OpenAI()
resp = client.responses.parse(
model="gpt-5-mini",
input=[{"role": "user", "content": f"Извлеки поля из вакансии:\n{text}"}],
text_format=Vacancy,
)
vacancy: Vacancy = resp.output_parsed
Библиотека Instructor даёт такой же интерфейс для разных провайдеров и локальных моделей, добавляя автоматический повтор с текстом ошибки валидации.[6]
Связь с вызовом инструментов
Вызов инструментов технически — тот же структурированный вывод: модель выдаёт имя функции и аргументы по JSON Schema. Поэтому один из способов получить структуру — объявить «инструмент» с нужной схемой и потребовать его вызова. Современные API предлагают для этого отдельный режим.
Практические советы
- Порядок полей важен. Модель генерирует слева направо: поле с рассуждением (
reasoning) перед итоговым ответом улучшает точность, после — бесполезно. - Описания полей в схеме работают как часть промпта: единицы измерения, формат дат, что делать при отсутствии значения.
- Оставлять выход. Для извлечения данных нужен способ сказать «не найдено» (
null, отдельное перечисление), иначе модель выдумает значение — см. Галлюцинации LLM. - Не перегружать схему. Глубоко вложенные структуры с десятками полей лучше разбить на несколько вызовов.
- Жёсткие ограничения могут вредить. Если формат мешает модели «думать», качество падает; иногда лучше дать свободный ответ, а затем извлечь из него структуру вторым вызовом.
- Проверять смысл, а не только форму: правильный по схеме JSON может содержать неверные данные. Качество измеряют на размеченном наборе — см. Оценка LLM (evals).