Все статьи
Статья
29 июля 2026 г.7 мин

Классификация и извлечение полей: JSON-режим

response_format и JSON Schema превращают модель в надёжный парсер. Как получать структурированные данные без регулярок и без сюрпризов.

Сергей Панов


Классификация и извлечение полей: JSON-режим

Извлечь из письма сумму, дату и контрагента; разложить тикеты по категориям; вытащить поля из счёта — классические задачи, где модель заменяет хрупкие регулярки. Ключ к надёжности — заставить её отвечать строго структурированным JSON.

response_format вместо «верни JSON»

Просьба «ответь в JSON» в тексте промпта работает через раз: модель то обернёт в ```json, то добавит комментарий сверху. Используйте параметр:

resp = client.chat.completions.create(
model="qwen-pro",
messages=[
{"role": "system", "content": "Извлеки поля из счёта. Верни JSON."},
{"role": "user", "content": invoice_text},
],
response_format={"type": "json_object"},
temperature=0,
)
data = json.loads(resp.choices[0].message.content)

С json_object модель гарантированно вернёт валидный JSON без обёрток. temperature=0 убирает вариативность — для извлечения это то, что нужно.

Строгая схема

Если модель и провайдер поддерживают JSON Schema — фиксируйте структуру полностью. Тогда не придётся проверять наличие каждого поля вручную:

schema = {
"type": "object",
"properties": {
"supplier": {"type": "string"},
"amount": {"type": "number"},
"currency": {"type": "string", "enum": ["RUB", "USD", "EUR"]},
"date": {"type": "string", "description": "ISO 8601"},
},
"required": ["supplier", "amount", "currency", "date"],
"additionalProperties": False,
}

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

Классификация: закройте варианты

Для разметки тикетов не оставляйте модели свободу — дайте закрытый список меток:

system = """Классифицируй обращение в одну из категорий:
billing | technical | account | other.
Если не уверен — other. Верни JSON: {"category": "...", "confidence": 0.0-1.0}."""

Поле confidence даёт порог для ручной проверки: всё, что ниже 0.7, отправляйте человеку.

Выбор модели

  • Простое извлечение и классификация — Flash-модели справляются и стоят копейки. Гоняйте батчами.
  • Сложные документы (многостраничные договоры, вложенная структура) — модель посильнее и с большим контекстом.
  • Vision нужен, когда данные в скане или картинке, а не в тексте — тогда мультимодальная модель (об этом отдельно).

Грабли

  • Числа как строки. Модель вернёт "amount": "1 000,50". Схема с "type": "number" заставит её нормализовать, но проверяйте: русский формат с пробелом и запятой она иногда путает. Просите точку и без разделителей разрядов.
  • Галлюцинация полей. Если поля нет в документе — модель может его выдумать. Инструктируйте: «отсутствующее поле — null, не угадывай».
  • null против пустой строки. Договоритесь заранее и пропишите в промпте, иначе получите вперемешку.
  • Ретраи. Даже с json_object изредка прилетает битый JSON. Оберните json.loads в try и сделайте один ретрай — дешевле, чем усложнять парсер.

Соберите тестовый набор из 30–50 реальных документов с эталонными полями и гоняйте по нему при смене модели или промпта. Извлечение — это тот случай, где метрику точности легко посчитать и легко улучшать.