Алексей Маркин рабочий журнал

ищет по заголовкам и тексту записей и заметок

запись 3 сентября 2026 г. · 4 мин
Все статьи

Формат ответа — это интерфейс, а не пожелание

Почему «ответь в JSON» ненадёжно, что дают схемы и структурированный вывод, и как обращаться с полем, которого модель не знает.

Классический способ потерять полдня: попросить модель ответить в JSON, написать разбор ответа, выкатить — и обнаружить в проде, что примерно один ответ из тридцати приходит с пояснением перед фигурной скобкой.

«Вот запрошенный JSON:» — и дальше валидный объект. Формально модель выполнила просьбу. Фактически ваш разбор упал.

Просьба и контракт

Разница принципиальная и стоит того, чтобы её проговорить.

Просьба — это текст в промпте: «ответь в формате JSON». Модель учитывает её вероятностно, наравне с остальным контекстом. Обычно выполняет. Иногда нет.

Контракт — это схема, переданная провайдеру отдельным параметром. Тогда ограничение применяется на уровне генерации: сэмплер физически не может выбрать токен, ломающий структуру. Ответ либо соответствует схеме, либо запрос падает с ошибкой — но не приходит «почти правильным».

Разница между ними — это разница между «обычно работает» и «работает». Если у провайдера есть структурированный вывод, его надо использовать, и это тот редкий случай, где я не вижу аргументов против.

Что схема не гарантирует

Важная оговорка, о которую спотыкаются после первой радости.

Схема гарантирует форму, но не содержание. Поле amount будет числом, а не строкой. Оно не будет правильным числом. Валидная структура с выдуманными значениями — это по-прежнему выдуманные значения, просто теперь они проходят разбор без исключения и уезжают дальше по конвейеру.

Отсюда следствие, которое легко упустить: структурированный вывод убирает класс ошибок, который был громким, и оставляет класс, который тихий. Проверку содержания придётся делать отдельно, и она никуда не делась.

Поле, которого модель не знает

Самая частая ошибка проектирования схем — не оставить выход.

Если в схеме есть обязательное поле дата_договора, а в документе даты нет, модель обязана что-то туда положить. Она положит правдоподобное. Не потому что «врёт», а потому что схема не оставила ей другого варианта: пропустить поле нельзя, значит надо заполнить.

Лечится это на уровне схемы, а не промпта. Каждое поле, которого может не оказаться во входных данных, должно допускать null — и инструкция должна прямо говорить, что null предпочтительнее догадки. Это одна из немногих формулировок в промпте, которая реально меняет поведение.

Ещё полезнее добавить рядом поле уверенности или цитату-основание: «откуда взято». Тогда проверяющий видит не только значение, но и то, на чём оно держится, — и проверка перестаёт требовать перечитывания исходника целиком.

Стандарт результата для текста

Для свободного текста схемы нет, но принцип тот же: всё, что вы не задали, будет заполнено средним по обучающей выборке. Именно это среднее вас потом и раздражает — вводные абзацы, оговорки про важность темы, симметричные «с одной стороны, с другой стороны», финальный абзац с выводами.

Работающая формулировка описывает не тональность, а измеримые свойства: объём, наличие примеров, что делать нельзя. «Две страницы, каждый пункт с примером из моего текста, без вводных абзацев, без заключения» — это стандарт, который можно проверить. «Напиши хорошо и по делу» — это не стандарт.

Условие несогласия

Если ответ читает человек и сразу правит, жёсткий контракт не нужен — он только добавит трения. Схемы окупаются там, где результат уходит в код: в базу, в интеграцию, в следующий шаг конвейера.

Граница проходит по вопросу «кто следующий читатель». Человек простит лишний абзац перед скобкой; разбор — нет.

Чего я делать не стал бы

Не стал бы чинить формат регулярными выражениями поверх ответа. Это работает ровно до следующей формы отклонения и превращается в снежный ком из заплаток.

Не стал бы делать схему с двадцатью полями за один вызов. Чем больше полей, тем выше шанс, что модель заполнит слабое поле догадкой, и тем дороже разбор. Два вызова с простыми схемами обычно надёжнее и, с учётом ретраев, не дороже.

И не стал бы просить модель «вернуть только JSON, без пояснений» вместо схемы, если схема доступна. Это просьба, которую можно нарушить.