TL;DR
Когда нужно извлечь конкретные поля из документа — счёт, договор, коммерческое предложение — стандартный запрос "вытащи данные" работает плохо. Модель угадывает что важно, путает похожие поля, пропускает или галлюцинирует значения. Исследование показывает: достаточно один раз показать модели размеченный пример с явными маркерами — и точность взлетает с уровня "терпимо" до уровня "можно доверять".
Главная находка: разница между нулевым запросом и хорошим few-shot — более 19 процентных пунктов точности. При этом настройки модели (температура, top-k, top-p — параметры, которые влияют на "случайность" ответа) дают лишь доли процента разницы. Промпт важнее настроек в десятки раз. Вторая важная находка: аннотированный пример (где явно помечено, какой текст соответствует какому полю) бьёт неаннотированный — модели не нужно догадываться о соответствии, ей показывают напрямую.
Метод работает в два элемента: сначала вы даёте модели схему полей (что именно извлечь и чем одно поле отличается от другого), потом показываете размеченный образец (фрагмент реального документа с пометками, где какое поле). Документ подаётся в структурированном виде — с сохранением таблиц, заголовков, форматирования.
Схема метода
Всё выполняется в одном промпте:
БЛОК 1: Роль → "Ты — ассистент по извлечению данных. Выводи результат в JSON"
БЛОК 2: Схема полей → список полей с описанием каждого
(чем одно поле отличается от другого — явно)
БЛОК 3: Размеченный пример → фрагмент документа с маркерами
#поле1 текст из документа #поле1
#поле2 текст из документа #поле2
БЛОК 4: Сам документ → вставляешь в Markdown-формате
(с таблицами, заголовками, структурой)
Вывод: JSON со всеми полями
Пример применения
Задача: Получил коммерческое предложение от подрядчика на разработку сайта. Нужно быстро сравнить 5 разных КП — вытащить из каждого ключевые условия в единую таблицу: цена, сроки, гарантия, состав работ, условия оплаты.
Промпт:
Ты — ассистент по извлечению данных из коммерческих предложений.
Выведи результат строго в формате JSON.
## Поля для извлечения
- "компания": название компании-исполнителя
- "цена_итого": итоговая сумма за весь проект (в рублях)
- "срок_выполнения": срок реализации проекта (в днях или неделях как указано)
- "гарантия": гарантийный период после сдачи (если указан)
- "состав_работ": перечень работ, кратко (список)
- "условия_оплаты": схема оплаты — аванс, поэтапно, постоплата
- "контакт": имя менеджера или email для связи
Важно: "цена_итого" — это финальная цифра всего проекта, не стоимость отдельных модулей.
## Пример разметки
Ниже пример документа, где текст, соответствующий каждому полю, обёрнут в маркеры:
---
Коммерческое предложение от #компания Студия Сайтов «Веб-Мастера» #компания
Стоимость разработки: #цена_итого 320 000 рублей #цена_итого
#состав_работ Дизайн главной, разработка 10 страниц, интеграция с 1С, SEO-оптимизация #состав_работ
Срок реализации: #срок_выполнения 45 рабочих дней #срок_выполнения
Оплата: #условия_оплаты 50% аванс, 50% по сдаче #условия_оплаты
Гарантия: #гарантия 6 месяцев на функциональность #гарантия
Менеджер: #контакт Алексей, aleksey@veb-mastera.ru #контакт
---
## Документ для извлечения
{вставь текст КП сюда — желательно сохранив таблицы и структуру}
Результат:
Модель вернёт JSON с заполненными полями. Если поле в документе не найдено — поставит null. Повторяешь для каждого из 5 КП, потом просишь модель свести JSON-объекты в сравнительную таблицу.
Почему это работает
Проблема "угадывания". Когда пишешь "вытащи данные из документа" — модель сама решает, что важно. Документ может содержать 3 разных суммы: сумма за модуль, сумма без НДС, итоговая сумма. Без явного указания модель выберет случайную из трёх.
Схема полей убирает неоднозначность. Когда ты прямо пишешь "поле A — это сумма за весь проект, НЕ стоимость отдельных модулей" — модель получает инструкцию однозначно. Это не угадывание, а чёткое задание.
Разметка показывает, не объясняет. Вместо словесного описания "текст поля находится после двоеточия в разделе..." — ты просто показываешь: вот текст, вот маркер, вот соответствие. Модель видит паттерн и переносит его на новый документ. Один хороший размеченный пример = десятки страниц объяснений о структуре документа.
Рычаги управления: - Детализация описаний полей — чем точнее описание "чем это поле отличается от похожего", тем меньше ошибок при похожих значениях - Количество примеров — добавь 2-3 документа разной структуры, если одно предприятие делает документы в разных форматах - Структура документа — сохраняй таблицы и заголовки, не вставляй документ сплошным текстом, это снижает точность
Шаблон промпта
Ты — ассистент по извлечению данных из {тип_документа}.
Выводи результат строго в формате JSON.
## Поля для извлечения
{для каждого поля:}
- "{название_поля}": {что это такое, чем отличается от похожих полей}
## Размеченный пример
---
{фрагмент реального документа, где каждое значение обёрнуто в маркеры:}
#название_поля значение из текста #название_поля
---
## Документ для извлечения
{вставь документ здесь — сохрани таблицы, заголовки, форматирование}
Что подставлять:
- {тип_документа} — счёт, договор, КП, акт, резюме
- {название_поля} — короткое имя: "цена", "срок", "исполнитель"
- {что это такое...} — особенно важно, если в документе несколько похожих полей
- В примере — лучше взять реальный фрагмент из похожего документа, а не придумывать
🚀 Быстрый старт — вставь в чат:
Вот шаблон для извлечения данных из документов. Адаптируй под мою задачу: {твоя задача}.
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит какой тип документа, какие поля нужно извлечь и попросит пример документа или фрагмент — потому что без примера маркеры в шаблоне будут пустыми, а именно они делают извлечение точным.
Ограничения
⚠️ Сложная структура документа: Если таблицы в документе многоуровневые или данные разбросаны по нескольким страницам в нелогичном порядке — точность падает. Структура документа-источника — главный определитель сложности, не размер или тип.
⚠️ Это не для простых документов: Если документ небольшой и нужно 2-3 поля — достаточно прямого запроса. Метод окупается при 10+ полях или при регулярной обработке однотипных документов.
⚠️ Формат подачи важен: Если вставлять документ сплошным текстом без форматирования — теряются связи между элементами. PDF → скопируй с сохранением структуры, или используй инструменты конвертации в Markdown.
⚠️ Галлюцинации при отсутствующих полях: Если поле в документе не указано, модель иногда придумывает значение вместо
null. Проверяй результат по критичным полям.
Как исследовали
Исследователи взяли 2 400 испанских счетов за электричество — синтетически сгенерированных, но по реальным шаблонам Iberdrola, Endesa, Naturgy. Шесть разных шаблонов с одинаковым содержимым, но совершенно разной структурой: у одних данные в таблицах, у других — в колонках, у третьих — вразброс. Задача — извлечь до 107 полей из каждого документа и сравнить с эталоном.
Ключевое решение: всё сравнение построено на промптах, а не на обучении модели. Шесть стратегий — от "просто попроси" (zero-shot) до "покажи три размеченных примера из разных шаблонов" (cross-validation). Параллельно прогнали 19 конфигураций параметров генерации. Итог оказался однозначным: разница между лучшей и худшей настройкой параметров — меньше 1%. Разница между zero-shot и лучшим few-shot — 19+ процентных пунктов. Это как сравнить влияние шрифта резюме и содержания резюме при найме.
Интересная деталь: аннотированные примеры (с явными маркерами #поле текст #поле) оказались лучше неаннотированных — где модели просто показывали пару "документ → JSON" без пометок. Это значит, что явное указание "вот этот конкретный фрагмент = вот это поле" работает лучше, чем "догадайся из примеров". Ещё одна находка: примеры, взятые из структурно разных шаблонов, дают лучшую обобщаемость — модель не переучивается на один тип форматирования.
Адаптации и экстраполяции
🔧 Техника: Итеративное извлечение по категориям → точность на сложных документах
Вместо выгрузки всех полей сразу — разбей на блоки. Сначала "извлеки только реквизиты сторон", потом "извлеки финансовые условия", потом "извлеки сроки и ответственность". Каждый запрос — меньше полей, меньше шанс перепутать. Исследование называет это Iterative strategy: она даёт лучший контроль за счёт большего числа запросов.
Шаг 1 из 3: извлеки только реквизиты сторон из договора:
- "заказчик_название", "заказчик_инн", "исполнитель_название", "исполнитель_инн"
[размеченный пример для этих полей]
[документ]
🔧 Техника: Шаблон для регулярной обработки → однажды размеченный пример работает многократно
Если ты регулярно работаешь с одним типом документов (акты одного подрядчика, счета одного поставщика) — один раз сделай качественный размеченный пример из реального документа этого поставщика. Сохрани его в заметки. Дальше подставляй только новые документы — схема остаётся. Это и есть то, что исследователи называют "template-specific extraction": если пример взят из того же формата, что и обрабатываемый документ, точность максимальная.
Ресурсы
Information Extraction from Electricity Invoices with General-Purpose Large Language Models — Javier Gómez, Javier Sánchez, Universidad de Las Palmas de Gran Canaria (ULPGC), Centro de Tecnologías de la Imagen (CTIM)
Датасет IDSEM (Invoices Database of the Spanish Electricity Market): публично доступен, 75 000 счетов, 6 шаблонов
Опирается на: Gemini 1.5 Pro (Google DeepMind), Mistral-small; работы по Markdown-форматированию для LLM [Borchmann et al.], LayoutLM, chain-of-thought prompting
