API и разработка

JSON-ответы нейросети: валидный объект ещё не значит верные данные

Как извлекать структурированные данные из текста: JSON-режим, проверка схемы и арифметики, пустые поля, конфликтующие документы и обработка ошибок.

Редакция KvantoraТематический выпуск: Около 5 минут чтения
Обложка статьи «JSON-ответы нейросети: валидный объект ещё не значит верные данные»

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

Начните с известного образца

Представьте синтетическую заявку: «Заказ K-42. Две коробки по 350 рублей. Доставка не указана». Ожидаются order_id K-42, quantity 2, unit_price_rub 350, goods_total_rub 700 и delivery_rub null. Эталон запишите до вызова модели. Не просите её придумать стоимость доставки и не подставляйте ноль вместо отсутствующего значения: ноль и неизвестно означают разные вещи.

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

Здесь разбираем, как спроектировать поведение приложения и выбрать проверки. Для воспроизводимого примера в Kvantora ниже есть отдельная короткая практическая инструкция в связанных материалах.

Формат выбирают по фактическому контракту

У разных API есть разные формы структурированного вывода. Официальная документация OpenAI описывает Structured Outputs с поддерживаемым подмножеством JSON Schema. В контракте Kvantora для поддержанной текстовой операции указан response_format с типом json_object; это не обещание произвольной JSON Schema. Не переносите режим одного поставщика на другой по похожему имени параметра.

Даже когда сервер обещает синтаксически корректный JSON, приложение должно проверить его содержимое. Используйте обычный парсер и валидатор схемы, который вы контролируете. Список допустимых полей, типы и условия должны жить в коде продукта. Подсказка помогает модели, но строгую валидацию бизнес-данных проводит программа.

Три уровня проверки

Сначала проверьте парсинг: получился ли JSON-объект нужного вида? Затем структуру: строка ли order_id, целое ли quantity, допускается ли null в delivery_rub? Наконец, смысл: совпадает ли goods_total_rub с произведением количества и цены, есть ли такой номер во входе? Каждый уровень может пройти при провале следующего.

Не исправляйте ошибки незаметно. Если quantity пришло строкой «два», можно направить результат на повторный разбор или человеку, но журнал должен сохранить исходное значение и причину отказа. Если модель вернула 700 при входной цене 300, валидный JSON не делает ответ истинным. Показывайте оператору исходную строку рядом с извлечёнными полями.

Схема меняется вместе с продуктом

Когда в карточку заказа добавляют новое поле, старые ответы не стоит молча трактовать по новой схеме. Храните версию схемы рядом с результатом и указывайте её в запросе модели. Если приложение прочитало устаревший JSON, оно должно либо мигрировать данные по явному правилу, либо отправить их на повторную проверку. Иначе новая обязательная графа может получить правдоподобное значение из воздуха.

Полезно отдельно тестировать совместимость парсера: неизвестное поле, отсутствующее обязательное, null вместо числа, число вместо строки и два ключа с похожим названием. Эти случаи легко смоделировать без вызова модели. Тест самой модели тогда отвечает за извлечение смысла, а обычные программные тесты: за устойчивость вашего контракта данных.

Код проверки известного примера

Фрагмент ниже намеренно проверяет и типы, и факты синтетического образца. В реальном продукте сравнение с захардкоженным K-42 заменит валидатор, который читает исходный документ и допустимые поля. Смысл примера: не позволять ответу модели пройти только потому, что JSON.parse не упал.

Проводите валидацию после полного ответа. В потоковом режиме отдельные фрагменты ещё не образуют объект; попытка парсить каждый chunk создаст ложные ошибки. Если ответ оборвался, сначала восстановите состояние запроса, затем решайте, можно ли повторять обработку.

JavaScript
const v = JSON.parse(answer);
const valid = v.order_id === 'K-42' && v.quantity === 2
  && v.unit_price_rub === 350 && v.goods_total_rub === 700
  && v.delivery_rub === null;
if (!valid) throw new Error('Нужна проверка исходного документа');

Пустое поле лучше уверенной выдумки

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

При конфликте двух сумм автоматизация должна остановиться и обозначить конфликт. Не выбирайте «самую правдоподобную» цифру без правила. В рабочем процессе такой отказ экономит больше времени, чем тихое неверное заполнение базы и последующее расследование.

Проверяйте и неявные зависимости между полями. Дата окончания не может предшествовать дате начала, количество не должно быть отрицательным, а код валюты должен соответствовать сумме в документе. Эти правила не описываются одним JSON.parse. Запишите их как обычные программные проверки и тестируйте на синтетических ошибочных объектах без платного вызова модели.

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

Как сравнить модели

Считайте отдельно долю JSON, прошедших парсер, долю объектов, прошедших схему, и долю полностью верных документов. Первый показатель обычно выше остальных и сам по себе мало говорит о пользе. Для каждого кандидата сохраняйте ID, параметры, дату, классы ошибок и подтверждённый расход. Сравнивайте на одном и том же наборе входов.

Если новая модель стала чаще возвращать правильную структуру, но хуже различать отсутствующее поле и ноль, это регрессия для бизнес-задачи. Решение о запуске принимайте по цене опасной ошибки и возможности ручной проверки. Форма JSON нужна машине; достоверность всё ещё нужна человеку и продукту.

Источники