
Как проверить JSON-ответ LLM на соответствие схеме и смыслу
Если LLM возвращает JSON для API, очереди задач или базы данных, недостаточно увидеть фигурные скобки и успешно вызвать `json.loads`. Парсер подтверждает только синтаксис. Он не проверяет, что поля имеют нужные типы, значение входит в допустимый диапазон, а выбранный статус согласуется с остальными данными.
Надёжная проверка строится слоями: декодировать JSON, проверить структуру по схеме, затем выполнить прикладные проверки. Встроенный режим структурированного вывода у модели может уменьшить число ошибок формата, но не заменяет проверку на стороне приложения. Ниже — компактный способ выстроить такой контроль на Python.
Проверка JSON-ответа LLM начинается с разделения ошибок
Условный ответ для службы поддержки:
json
{
«priority»: «urgent»,
«summary»: «Пользователь не может войти»,
«needs_human»: false
}
В нём могут скрываться разные классы проблем:
Ошибка разбора: строка не является корректным JSON.
Ошибка структуры: отсутствует обязательное поле или добавлено незнакомое.
3. Ошибка типа или диапазона: например, приоритет задан числом вместо строки.
4. Смысловое противоречие: обращение помечено как срочное, но передано в автоматическую обработку, хотя внутреннее правило требует участия оператора.
Это не взаимозаменяемые проверки. JSON Schema описывает форму данных и часть ограничений на значения. Правила, зависящие от бизнес-контекста, обычно нужно реализовать отдельно.
Зафиксируйте контракт до вызова модели
Начните с минимального объекта, который приложение действительно готово принять. Не включайте поля «на будущее»: каждое необязательное поле увеличивает число вариантов и усложняет обработку.
Например, контракт может требовать:
- `priority` — одно из значений `low`, `normal`, `urgent`;
- `summary` — непустая строка не длиннее 300 символов;
- `needs_human` — логическое значение;
- никаких дополнительных ключей.
JSON Schema для такого контракта:
json
{
«$schema»: «https://json-schema.org/draft/2020-12/schema»,
«type»: «object»,
«properties»: {
«priority»: {
«type»: «string»,
«enum»: [«low», «normal», «urgent»]
},
«summary»: {
«type»: «string»,
«minLength»: 1,
«maxLength»: 300
},
«needs_human»: {
«type»: «boolean»
}
},
«required»: [«priority», «summary», «needs_human»],
«additionalProperties»: false
}
Ограничения `required` и `additionalProperties` важны для интеграций: первое не даёт молча принять неполный ответ, второе помогает обнаружить неожиданное поле, которое приложение не умеет обрабатывать. Спецификация JSON Schema 2020-12 описывает такие ключевые слова, но сама по себе не определяет все прикладные правила.
Как проверить JSON-ответ LLM на Python
Разделите разбор строки и проверку схемы. Так в логах можно отличить повреждённый JSON от корректного объекта с неверными полями.
python
import json
from jsonschema import Draft202012Validator
schema = {
«$schema»: «https://json-schema.org/draft/2020-12/schema»,
«type»: «object»,
«properties»: {
«priority»: {
«type»: «string»,
«enum»: [«low», «normal», «urgent»],
},
«summary»: {
«type»: «string»,
«minLength»: 1,
«maxLength»: 300,
},
«needs_human»: {«type»: «boolean»},
},
«required»: [«priority», «summary», «needs_human»],
«additionalProperties»: False,
}
validator = Draft202012Validator(schema)
def parse_and_validate(raw: str) -> dict:
data = json.loads(raw) # JSONDecodeError при синтаксической ошибке
errors = sorted(validator.iter_errors(data), key=lambda e: list(e.path))
if errors:
details = [
f»{list(error.path)}: {error.message}»
for error in errors
]
raise ValueError(«Нарушена схема: » + «; «.join(details))
return data
Библиотека `jsonschema` позволяет собирать ошибки через `iter_errors`, а не останавливаться на первом нарушении. Это полезно при отладке промпта и контрактов, но не означает, что ошибочные данные следует частично использовать.
В промышленном коде добавьте верхний предел размера ответа и обработку исключений на границе вызова модели. Не передавайте исходную строку дальше только потому, что ошибка произошла «редко».
Структурированный вывод модели — ещё один слой, не замена валидатору
Некоторые API предлагают режимы структурированного вывода, где разработчик передаёт схему или описание допустимой формы. Документация OpenAI, например, описывает Structured Outputs и различает соблюдение заданной схемы и более свободный режим JSON.
Это снижает вероятность структурных отклонений, но у приложения остаются собственные задачи:
- проверить, что ответ действительно получен, а не завершился отказом или ошибкой;
- убедиться, что используется ожидаемая версия контракта;
- проверить бизнес-условия, которые схема не выражает;
- обработать сетевые сбои, усечение ответа и изменения поведения API.
Формулировка «модель вернула JSON» не равна гарантии, что приложение получило пригодный для выполнения объект. Проверяйте результат после получения, даже если формат ограничивался на стороне API.
Добавьте семантические проверки после схемы
Схема может подтвердить, что `priority` — строка из трёх разрешённых вариантов. Но она не знает, что делать с конкретной категорией обращения. Прикладные правила следует выразить отдельным кодом:
python
def validate_business_rules(data: dict) -> None:
if data[«priority»] == «urgent» and not data[«needs_human»]:
raise ValueError(
«Срочное обращение должно быть передано оператору»
)
if len(data[«summary»].split()) < 3:
raise ValueError(«Краткое описание слишком малоинформативно»)
Такие проверки не доказывают истинность содержания. Они лишь отсекают известные недопустимые состояния. Например, проверка длины не гарантирует, что краткое описание точно отражает исходное сообщение.
Для критичных решений полезно разделять машинное извлечение и действие: модель предлагает поля, а приложение применяет детерминированные правила допуска. Если последствия ошибки значимы, добавьте ручную проверку или независимый источник данных, а не пытайтесь решить проблему дополнительной инструкцией в промпте.
Протестируйте валидатор на ошибочных и пограничных примерах
Проверка одного удачного ответа показывает лишь один случай. Соберите набор входов с ожидаемым результатом:
| Случай | Ожидаемое поведение |
|---|---|
| Все поля корректны | Принять |
| Нет `priority` | Отклонить по схеме |
| `needs_human` задано строкой `»false»` | Отклонить по типу |
| Добавлено поле `debug` | Отклонить при `additionalProperties: false` |
| Пустое `summary` | Отклонить по `minLength` |
| Срочность без передачи оператору | Отклонить бизнес-правилом |
| Ответ обрезан посреди строки | Отклонить при разборе JSON |
Зафиксируйте эти случаи как обычные автоматические тесты. При обновлении схемы или модели прогоняйте один и тот же набор, чтобы увидеть, изменилось ли число ошибок. Для оценки поведения модели отдельно храните реальные тестовые запросы и эталонные ожидания: валидатор не измеряет качество извлечения фактов.
Продумайте повторы, отказы и журналирование
После ошибки не запускайте бесконечный цикл «попросить модель исправить JSON». Ограничьте число повторов, учитывайте стоимость и задержку, а после исчерпания лимита переводите задачу в безопасный отказ или очередь ручной проверки.
В журнале полезно хранить тип сбоя, версию схемы, версию модели или API и идентификатор тестового случая. Не записывайте без необходимости персональные данные и полный текст обращения: диагностические логи сами могут стать источником утечки.
Если результат передаётся в другой сервис, повторно проверяйте его на границе этого сервиса либо передавайте только типизированный объект из доверенного слоя приложения. Для потоков JSONL дополнительно проверяйте каждую строку отдельно: по формату JSON Lines одна запись занимает одну строку, поэтому ошибка одной записи не должна незаметно менять разбор соседних.
Что проверить перед включением в production
Перед подключением генерации к реальному потоку данных проверьте три вещи: валидатор отклоняет ошибочные типы и лишние поля; бизнес-правила покрыты тестами; отказ модели или превышение лимита повторов не приводит к автоматическому действию.
Дальше прогоните фиксированный набор примеров на текущей версии модели и сохраните долю структурных и семантических ошибок отдельно. Схема отвечает на вопрос «можно ли принять такую форму данных», а не «правдив ли ответ». Для последнего нужны собственные тесты на содержание, сравнение с исходными данными и подходящий уровень человеческого контроля.










