
Структурированный вывод LLM — это не «красивый JSON в ответе», а контракт между моделью и кодом. Если агент создает задачу в Jira, извлекает поля из письма, обновляет CRM или запускает следующий инструмент, свободный текст становится источником сбоев: пропущенное поле, лишняя строка перед JSON, неверный тип даты, несуществующая категория.
За последние годы провайдеры добавили несколько способов снизить этот риск: JSON mode, схемы на базе JSON Schema, tool calling и управляемую генерацию. Но эти режимы решают разные задачи. Ошибка начинается там, где разработчик просит модель «верни валидный JSON» и считает это полноценной гарантией для продакшена.
Структурированный вывод LLM: когда он действительно нужен
Жесткий формат нужен не каждому запросу. Если пользователь просит объяснить ошибку в коде или написать письмо, обычный текст лучше: модель может уточнять, аргументировать и не упираться в искусственные поля. Но если результат сразу парсится машиной, формат становится частью безопасности и надежности.
Типичные случаи:
- извлечение данных: имя клиента, сумма, срок, номер заказа, уровень срочности;
- классификация: категория обращения, риск, намерение пользователя, тип документа;
- агентные действия: выбор инструмента, аргументы вызова, причина отказа;
- оценка качества: рубрика, баллы, замечания, флаг на ручную проверку;
- RAG-пайплайны: ссылки на использованные документы, цитаты, уровень уверенности.
Главное правило: если downstream-код не может безопасно продолжить работу без конкретного поля, это поле должно быть в схеме и дополнительно проверяться валидатором. Промпт сам по себе не заменяет контракт.
JSON mode, JSON Schema и tool calling — не одно и то же
В документации OpenAI отдельно описаны Structured Outputs, а в анонсе 2024 года компания объясняла разницу между простым JSON mode и строгим соответствием схеме. JSON mode обычно означает: модель должна вернуть синтаксически корректный JSON. Это не означает, что в ответе будут все нужные поля, правильные enum-значения и ожидаемые типы.
| Подход | Что проверяет | Где полезен | Главный риск |
|---|---|---|---|
| Промпт «верни JSON» | Ничего формально | Прототипы, ручной разбор | Текст вокруг JSON, пропущенные поля, нестабильные ключи |
| JSON mode | Синтаксис JSON | Логи, простые ответы, быстрые интеграции | Валидный JSON может не соответствовать вашей структуре |
| JSON Schema / Structured Outputs | Форму объекта, типы, обязательные поля, enum | Автоматизация, извлечение данных, оценки | Поддерживается не весь JSON Schema; нужна валидация на своей стороне |
| Tool calling | Аргументы вызова инструмента | Агенты, функции, внешние API | Модель может выбрать не тот инструмент или вызвать его не вовремя |
Anthropic в документации по tool use описывает инструменты через JSON-схему входных параметров. Google в Gemini API использует термин structured output и поддерживает управляемую генерацию с response schema. У всех подходов общий смысл: сузить пространство допустимых ответов. Но детали реализации, ограничения схемы и поведение при отказе отличаются.
Начинайте не с промпта, а с маленькой схемы
Самая частая ошибка — пытаться описать весь бизнес-процесс одной огромной схемой. Чем больше вложенность, свободных строк и необязательных полей, тем труднее тестировать результат. Для первого рабочего контура лучше выбрать один узкий объект.
Пример: агент разбирает входящее письмо в поддержку и должен решить, создавать ли тикет.
{
"type": "object",
"additionalProperties": false,
"required": ["should_create_ticket", "category", "priority", "summary", "customer_email"],
"properties": {
"should_create_ticket": {
"type": "boolean"
},
"category": {
"type": "string",
"enum": ["billing", "bug", "account", "security", "other"]
},
"priority": {
"type": "string",
"enum": ["low", "normal", "high", "urgent"]
},
"summary": {
"type": "string",
"maxLength": 240
},
"customer_email": {
"type": "string"
}
}
}
Здесь есть несколько практичных ограничений:
additionalProperties: falseзапрещает модели добавлять поля вродеconfidence_explanation, если код их не ждет;enumне дает появиться «critical», если в системе есть толькоurgent;maxLengthзащищает интерфейс и базу от длинного пересказа письма;- обязательные поля заставляют явно решать, что делать с неполными данными.
Спецификация JSON Schema шире, чем поддержка конкретного LLM-провайдера. Перед внедрением нужно сверить, какие ключевые слова схемы реально принимаются API: вложенные объекты, массивы, nullable-поля, регулярные выражения, ограничения длины и числовые диапазоны могут поддерживаться не одинаково.
Рабочий контур: модель, парсер, валидатор, бизнес-правила
Даже если провайдер обещает структурированный ответ, серверное приложение не должно слепо доверять результату. Надежный контур выглядит так:
- отправить модели инструкцию и схему;
- получить структурированный объект или аргументы tool call;
- проверить объект локальным валидатором;
- применить бизнес-правила, которые не стоит отдавать модели;
- при ошибке вернуть запрос на исправление, отправить на ручную проверку или безопасно остановить действие.
Для Python удобен Pydantic: он позволяет держать контракт рядом с кодом и генерировать JSON Schema из модели. Документация Pydantic отдельно описывает генерацию JSON Schema, что полезно для синхронизации схемы между API и локальной валидацией.
from typing import Literal
from pydantic import BaseModel, EmailStr, Field, ValidationError
class TicketDecision(BaseModel):
should_create_ticket: bool
Источники
