Формат ответа — это интерфейс, а не пожелание
Почему «ответь в JSON» ненадёжно, что дают схемы и структурированный вывод, и как обращаться с полем, которого модель не знает.
Классический способ потерять полдня: попросить модель ответить в JSON, написать разбор ответа, выкатить — и обнаружить в проде, что примерно один ответ из тридцати приходит с пояснением перед фигурной скобкой.
«Вот запрошенный JSON:» — и дальше валидный объект. Формально модель выполнила просьбу. Фактически ваш разбор упал.
Просьба и контракт
Разница принципиальная и стоит того, чтобы её проговорить.
Просьба — это текст в промпте: «ответь в формате JSON». Модель учитывает её вероятностно, наравне с остальным контекстом. Обычно выполняет. Иногда нет.
Контракт — это схема, переданная провайдеру отдельным параметром. Тогда ограничение применяется на уровне генерации: сэмплер физически не может выбрать токен, ломающий структуру. Ответ либо соответствует схеме, либо запрос падает с ошибкой — но не приходит «почти правильным».
Разница между ними — это разница между «обычно работает» и «работает». Если у провайдера есть структурированный вывод, его надо использовать, и это тот редкий случай, где я не вижу аргументов против.
Что схема не гарантирует
Важная оговорка, о которую спотыкаются после первой радости.
Схема гарантирует форму, но не содержание. Поле amount будет
числом, а не строкой. Оно не будет правильным числом. Валидная структура
с выдуманными значениями — это
по-прежнему выдуманные значения, просто
теперь они проходят разбор без исключения и уезжают дальше по конвейеру.
Отсюда следствие, которое легко упустить: структурированный вывод убирает класс ошибок, который был громким, и оставляет класс, который тихий. Проверку содержания придётся делать отдельно, и она никуда не делась.
Поле, которого модель не знает
Самая частая ошибка проектирования схем — не оставить выход.
Если в схеме есть обязательное поле дата_договора, а в документе даты
нет, модель обязана что-то туда положить. Она положит правдоподобное.
Не потому что «врёт», а потому что схема не оставила ей другого варианта:
пропустить поле нельзя, значит надо заполнить.
Лечится это на уровне схемы, а не промпта. Каждое поле, которого может
не оказаться во входных данных, должно допускать null — и инструкция
должна прямо говорить, что null предпочтительнее догадки. Это одна
из немногих формулировок в промпте, которая реально меняет поведение.
Ещё полезнее добавить рядом поле уверенности или цитату-основание: «откуда взято». Тогда проверяющий видит не только значение, но и то, на чём оно держится, — и проверка перестаёт требовать перечитывания исходника целиком.
Стандарт результата для текста
Для свободного текста схемы нет, но принцип тот же: всё, что вы не задали, будет заполнено средним по обучающей выборке. Именно это среднее вас потом и раздражает — вводные абзацы, оговорки про важность темы, симметричные «с одной стороны, с другой стороны», финальный абзац с выводами.
Работающая формулировка описывает не тональность, а измеримые свойства: объём, наличие примеров, что делать нельзя. «Две страницы, каждый пункт с примером из моего текста, без вводных абзацев, без заключения» — это стандарт, который можно проверить. «Напиши хорошо и по делу» — это не стандарт.
Условие несогласия
Если ответ читает человек и сразу правит, жёсткий контракт не нужен — он только добавит трения. Схемы окупаются там, где результат уходит в код: в базу, в интеграцию, в следующий шаг конвейера.
Граница проходит по вопросу «кто следующий читатель». Человек простит лишний абзац перед скобкой; разбор — нет.
Чего я делать не стал бы
Не стал бы чинить формат регулярными выражениями поверх ответа. Это работает ровно до следующей формы отклонения и превращается в снежный ком из заплаток.
Не стал бы делать схему с двадцатью полями за один вызов. Чем больше полей, тем выше шанс, что модель заполнит слабое поле догадкой, и тем дороже разбор. Два вызова с простыми схемами обычно надёжнее и, с учётом ретраев, не дороже.
И не стал бы просить модель «вернуть только JSON, без пояснений» вместо схемы, если схема доступна. Это просьба, которую можно нарушить.