TL;DR
Когда просишь LLM извлечь данные из документа и говоришь "напиши тип родов" — модель пишет что хочет. Строгое ограничение пространства вывода на уровне схемы ("тип родов — одно из: NVD, C/S, TOP") вместе с явными правилами нормализации ("дата всегда в формате ДД/ММ/ГГГГ, NAD = норма, ? = неизвестно") даёт более чем 60% улучшение точности на одних и тех же данных — без смены модели.
Главная находка: модель не угадывает правильный формат — она генерирует по паттерну. Без явного списка допустимых значений она "изобретает" свои варианты: кто пишет "кесарево", кто "C/S", кто "К/С" — всё разное, всё несопоставимо. Проблема не в том, что модель не понимает задачу. Проблема в том, что ты не закрыл все дыры в инструкции. Каждая неоднозначность — это ошибка на выходе.
Метод двухуровневый. Первый уровень — схема: перечисли допустимые значения для каждого поля прямо в шаблоне вывода. Второй уровень — правила нормализации: явно пропиши как обрабатывать сокращения, форматы дат, нестандартные обозначения. Всё это идёт в один промпт.
Схема метода
ШАГ 1: Базовый промпт с инструкцией → [что извлечь, зачем, общий контекст]
ШАГ 2: Правила нормализации → [явный список: как обробатывать форматы,
сокращения, нестандартные обозначения]
ШАГ 3: Шаблон вывода с дискретизацией → [JSON/список, где для каждого поля
уже прописаны допустимые значения]
Всё три уровня — в одном промпте. Один запрос.
Пример применения
Задача: Фаундер SaaS-стартапа проводит 20+ интервью с клиентами в неделю. Записи — голос → автотранскрипт → каша из текста. Нужно извлекать структурированные данные по каждому интервью: боль, сегмент, готовность платить, стадия продукта у клиента.
Промпт:
Ты извлекаешь структурированные данные из транскрипта customer development интервью.
ПРАВИЛА НОРМАЛИЗАЦИИ:
- Если сегмент не назван явно — выводи "не определён"
- Готовность платить: только числа в рублях или "не обсуждалась"
- Если клиент использует общие слова ("дорого", "нормально") без цифр — пиши "не обсуждалась"
- Боль: одно предложение, максимум 15 слов, без цитат
- Стадия: только из допустимых значений ниже
- Если поле неясно — пиши "нет данных", не придумывай
ШАБЛОН ВЫВОДА (заполни строго по полям):
{
"сегмент": "[B2B / B2C / не определён]",
"размер_компании": "[1-10 / 11-50 / 51-200 / 200+ / не определён]",
"основная_боль": "[одно предложение]",
"стадия_клиента": "[ищет решение / тестирует альтернативы /
есть текущее решение / не определён]",
"готовность_платить": "[сумма в рублях / не обсуждалась]",
"следующий_шаг": "[договорились о демо / отправить материалы /
нет следующего шага / не определён]"
}
ТРАНСКРИПТ:
{вставь текст транскрипта}
Результат: Модель выдаст заполненный JSON строго по шаблону. Поля с явными допустимыми значениями будут стандартизированы — все 20 интервью окажутся сопоставимы между собой. Поля без данных получат "нет данных", а не выдуманный текст. Никакой вариативности в формулировках — одинаковые категории во всех записях.
Почему это работает
LLM — это паттерн-матчер. Она генерирует следующий токен, исходя из контекста. Когда ты пишешь "опиши тип доставки" — контекст открытый, и модель выбирает любой подходящий паттерн из тренировочных данных. Одна формулировка, другая, третья — все "правильные" с точки зрения языка, но несопоставимые между собой.
Когда ты пишешь "тип доставки — одно из: [самовывоз, курьер, почта]" — контекст закрытый. Модель видит список, понимает что нужно выбрать из него, и генерирует по этому паттерну. Вариативность исчезает не потому что модель "стала умнее" — просто ты убрал свободу интерпретации там, где она вредит.
Рычаги управления: - Количество правил нормализации → добавляй по одному за раз при каждой новой ошибке — легче отлаживать - Жёсткость списков → для критичных полей давай строгий список, для свободных полей (описания, комментарии) — оставь открытым - "Нет данных" как класс → всегда добавляй явно — иначе модель будет придумывать вместо того чтобы признавать неопределённость - Формат вывода → JSON удобен для копирования в таблицы, список с дефисами — для чтения глазами
Шаблон промпта
Ты извлекаешь структурированные данные из {тип_документа}.
ПРАВИЛА НОРМАЛИЗАЦИИ:
- {правило_1: как обрабатывать нестандартные случаи}
- {правило_2: как форматировать конкретный тип данных}
- {правило_3: что делать с неявными или отсутствующими данными}
- Если поле неясно — пиши "нет данных", не придумывай
ШАБЛОН ВЫВОДА:
{
"{поле_1}": "[{вариант_А} / {вариант_Б} / {вариант_В} / не определён]",
"{поле_2}": "[{вариант_А} / {вариант_Б} / не определён]",
"{поле_3}": "[свободный текст, максимум {N} слов]",
"{поле_4}": "[число / не обсуждалось]"
}
{тип_документа_заглавными}:
{вставь текст или описание документа}
Плейсхолдеры:
- {тип_документа} — транскрипт, письмо, договор, отчёт, фото формы
- {правило_X} — конкретное "если...то..." для твоего контекста
- {поле_X} — название поля, которое хочешь извлечь
- {вариант_Х} — допустимые значения через слэш
- {N} — лимит слов для свободных полей
🚀 Быстрый старт — вставь в чат:
Вот шаблон для структурированного извлечения данных из документов.
Адаптируй под мою задачу: {опиши свою задачу}.
Задавай вопросы чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит какие поля нужно извлекать, какие допустимые значения для каждого и что делать с пропусками — потому что без этого не сможет создать рабочую дискретизацию.
Ограничения
⚠️ Только фронтирные модели: GPT-4o и выше, Claude Sonnet/Opus, Gemini 2.5+. Более старые и бюджетные модели часто возвращают список допустимых значений вместо выбора из него — не понимают что нужно выбрать, а не перечислить.
⚠️ Свободный текст не дискретизируется: Метод радикально помогает для полей с конечным числом вариантов. Для свободного описания (комментарии, пояснения) улучшение минимальное — там другая природа ошибок.
⚠️ 60% улучшение — на сложных, нестандартных данных: На чистых, однозначных текстах эффект меньше. Чем больше в исходнике сокращений, форматов и неоднозначностей — тем сильнее работают правила нормализации.
⚠️ Нужна постановка правил из опыта: Первые итерации промпта будут несовершенны. Правила нормализации пишутся по реальным ошибкам — смотришь где модель ошиблась, добавляешь правило, повторяешь.
Как исследовали
Команда из ЮАР взяла реальные рукописные медицинские формы беременных — 49 документов плюс 4 синтетических. Формы заполнялись разными сотрудниками клиник, часто в спешке: даты написаны вертикально, "NAD" вместо пустой строки, цифры "1/2/3" вместо слов "употребляет/бывший/не употребляет". Такие документы — nightmare даже для человека.
Протестировали 17 моделей с двумя вариантами промпта: базовым (только инструкция) и оптимизированным (инструкция + 16 правил нормализации + шаблон с допустимыми значениями). Результат удивил: оптимизация дала 60%+ улучшение по precision/recall — но почти не повлияла на взвешенные метрики (2-5%). Это говорит о том, что основной эффект — в редких и сложных полях, а не в частых и простых.
Ещё неожиданная находка: размер модели ≠ качество для OCR-задач. Gemini Flash (меньше) регулярно побеждал Gemini Pro (больше) несмотря на меньший бюджет токенов для рассуждений. GPT 5.4 обошёл значительно более дорогой GPT 5.2 Pro. Это намекает: для структурированного извлечения важнее обученность на нужных паттернах, а не общая мощность рассуждений.
Адаптации и экстраполяции
🔧 Техника: Итеративное добавление правил → контроль качества вывода
Вместо того чтобы писать все правила сразу — запусти промпт на 3-5 примерах, посмотри где ошибки, добавь правило под каждую ошибку. Тот же принцип что в исследовании: авторы добавили 16 правил именно по реальным паттернам ошибок, не придумали их заранее.
🔧 Техника: "Нет данных" как обязательный класс → снижение галлюцинаций
Исследование фиксировало галлюцинации (модель придумывает поля которых нет). Решение простое: добавь в каждый пункт шаблона явный вариант "нет данных / не определён / не обсуждалось". Модель перестаёт придумывать — у неё появляется легитимный выход из неопределённости.
🔧 Экстраполяция: Принцип на задачи классификации текста
Та же логика работает когда просишь модель категоризировать — отзывы, заявки, письма. Вместо "определи тональность" → "тональность — одно из: [позитивная / негативная / нейтральная / смешанная]". Вместо "определи срочность" → "срочность — одно из: [критичная — нужен ответ сегодня / высокая — в течение дня / средняя — в течение недели / низкая]".
Ресурсы
Статья: "From Handwriting to Structured Data: Benchmarking AI Digitisation of Handwritten Forms"
Авторы: Sitwala Mundia, Nicholas Pather, Joshua Fouché, Karl-Günter Technau, Alex Welte, Thokozile Malaba, Ushma Mehta, Bruce A. Bassett
Организации: University of the Witwatersrand (ЮАР), Grai Labs, University of Cape Town, University of KwaZulu-Natal
Проект: UBOMI BUHLE Pregnancy Exposure Registry — ubomibuhle.org.za
