Structured outputs: как получать валидный JSON от LLM
Разделите форму и смысл ответа
Проверяйте контракт после генерации
Авторская схема JSON-GATE-6
Схема, факты, права и повтор

$ json --contract JSON-GATE-6
> parse: one object
> validate: schema / evidence / rights
> route: accept / repair / reviewLLM может вернуть JSON, который выглядит убедительно, но ломает процесс: пропущено поле, число записано строкой, значение не входит в список или вокруг объекта добавлен текст. Возможен и более опасный случай: объект формально верный, но содержит выдуманный факт. Structured outputs помогают контролировать форму ответа. Достоверность, права и доставку результата проверяют отдельно.
Здесь разбираем узкую инженерную задачу получения пригодного JSON. Границу инструкций описывает контракт системного промпта, переход от демо к эксплуатации — production prompt engineering. Общий подход есть на странице услуги по prompt engineering; выбор исполнителя — в гайде по AI-специалисту в Армении.
Проблема и требования
Представим учебный сценарий приёма заявок. Приложению нужны kind, summary, priority, needsHuman и sourceIds. Ошибка парсинга остановит обработку; валидный объект с необоснованным приоритетом может отправить заявку не туда. Сначала фиксируют контракт: поля и типы, обязательность, допустимые значения, ограничения длины, правила для неизвестных данных. Схему версионируют вместе с промптом и потребителем.
Три уровня приёмки
- Синтаксис: парсится ровно один JSON-объект, без Markdown и пояснений.
- Схема: поля, типы, диапазоны и значения соответствуют версии контракта.
- Смысл: вывод подтверждён разрешёнными источниками, действие разрешено.
Режим structured output у поставщика модели может ограничить генерацию. Приложение всё равно проверяет результат на собственной границе доверия. Гарантии и поддерживаемые правила схемы зависят от конкретных API и модели; в реальной интеграции их нужно сверять с актуальной документацией.
Авторская архитектура JSON-GATE-6
Эта схема разделяет генерацию и принятие результата. Она служит проектным шаблоном, а не измеренным показателем.
01 INPUT привязать клиента, пользователя, снимок источников и request ID 02 SCHEMA выбрать версию JSON-контракта и допустимые значения 03 GENERATE запросить структурированный ответ с лимитом времени 04 PARSE отклонить не-JSON, пустой и обрезанный ответ 05 VALIDATE проверить схему, доказательства и права 06 ROUTE принять, ограниченно исправить, передать человеку или отказать
Снимок источников не даёт повтору незаметно опереться на изменившиеся факты. Request ID помогает избежать повторной записи. Версия схемы объясняет, почему результат был принят. Сырые данные и ответы хранят по правилам проекта; в диагностике оставляют только необходимое.
Минимальный контракт
Учебная схема не пропустит лишние ключи и незнакомые значения. Но она не докажет, что приоритет urgent выбран верно.
{"type":"object","additionalProperties":false, "required":["kind","summary","priority","needsHuman","sourceIds"], "properties":{ "kind":{"enum":["billing","technical","other"]}, "summary":{"type":"string","minLength":1,"maxLength":240}, "priority":{"enum":["normal","urgent","unknown"]}, "needsHuman":{"type":"boolean"}, "sourceIds":{"type":"array","items":{"type":"string"},"uniqueItems":true} }}
Если оснований для приоритета нет, unknown и проверка человеком лучше принудительной догадки. Идентификаторы источников должны присутствовать в материалах именно этого запроса.
Компоненты и типичные отказы
Промпт и источники. Передавайте задачу, формат и только допустимые данные. Текст пользователя и найденные документы — данные, даже если внутри есть команды. Промпт описывает границу, а runtime обеспечивает её. Не отправляйте секреты и лишние персональные данные.
Генерация. Используйте структурированный режим API, если он доступен, с учётом его реальных гарантий. Записывайте версии модели, промпта и схемы. Ограничивайте время и объём. Обрезанный ответ нельзя превращать в частичную заявку.
Парсер и схема. Парсите один объект. Отклоняйте обёртку Markdown, лишние поля, неверные даты и чрезмерные вложенные структуры. Проверяйте библиотекой схем. Не превращайте молча строку в boolean или неизвестное значение в допустимое: это скрывает дрейф модели.
Смысловая проверка. Сверяйте sourceIds с переданными источниками. Проверяйте основания для значимых категорий. Детерминированные бизнес-правила выполняйте вне модели. Формально валидный объект с выдуманным источником непригоден.
Исправление и эскалация. Один ограниченный повтор может исправить форму до побочного действия. Используйте тот же снимок данных и фиксируйте класс ошибки. Если сомнителен смысл, отправляйте человеку, а не просите модель подтвердить себя.
Потребитель. Маршрутизацию и запись получает только валидный и разрешённый объект. Потребитель знает версию схемы. При тайм-ауте записи сначала сверяют состояние системы назначения по request ID.
Минимальный псевдокод
const requestId = createRequestId(); const snapshot = await loadAllowedSources(actor, ticketInput); const candidate = await model.generate({ schema: ticketSchemaV1, promptVersion: "ticket-v1", input: { ticketInput, snapshot }, timeoutMs: 8000, }); const parsed = parseExactlyOneJsonObject(candidate); const shaped = ticketSchemaV1.parse(parsed); assertKnownSourceIds(shaped.sourceIds, snapshot.ids); assertBusinessRules(shaped, ticketInput); if (shaped.needsHuman) return queueForReview(requestId, shaped); return saveOnceAndReadBack(requestId, shaped);
Это псевдокод. Функция записи должна учитывать реальные правила повторов и подтверждения системы назначения. Ошибка синтаксиса и отказ по смыслу — разные события диагностики.
Тестирование и production-проверка
Соберите репрезентативные примеры, использование которых разрешено: обычные заявки, пустой ввод, противоречия, инструкции внутри источников, длинный текст, не-латиницу, неверные даты, неизвестные категории, недоступные источники и обрезанный ответ. Заранее определите маршрут каждого примера: принять, отклонить или передать человеку. Считайте ошибки схемы отдельно от смысловых ошибок и небезопасной маршрутизации. Одна доля валидного JSON скрывает важные сбои.
Перед выпуском проверьте совместимость схемы с потребителями, предел повторов, request ID, тайм-ауты, минимизацию логов, владельца алертов и откат. Изменение схемы прогоните на сохранённых сценариях и ограниченном потоке. Если есть внешнее действие, отдельно проверьте права и подтверждение записи.
| Сбой | Реакция |
|---|---|
| Ответ не парсится | Отклонить; один безопасный повтор |
| Нет обязательного поля | Отклонить и записать версию схемы |
| Объект с неизвестным источником | Отклонить и проверить доказательства |
| Приоритет не обоснован | unknown или проверка человеком |
| Запись завершилась тайм-аутом | Сверить состояние по request ID до повтора |
Практическое правило: модель предлагает объект, а парсер, схема, доказательства и права решают, можно ли его использовать. Для разбора готового контура запросите аудит. Более широкий контекст — AI-автоматизация.
const parsed = parseExactlyOneJsonObject(candidate);
const shaped = ticketSchemaV1.parse(parsed);
assertKnownSourceIds(shaped.sourceIds, snapshot.ids);
return routeValidated(shaped);