Как LLM анализируют тендерную документацию: разбор пайплайна
- AI
- LLM
- Обработка документов
- Тендеры
Зачем вообще нужен такой пайплайн
Средняя компания, участвующая в тендерах, тратит десятки часов в неделю на чтение документации. Часть лотов не стоит времени — но понимаешь это только после того, как уже потратил час на разбор. Экономия на этом простом шаге — классический сценарий для LLM-пайплайна обработки документов.
В этой статье разберём архитектуру реального пайплайна, который мы реализовали в демо-проекте по анализу тендеров. Это не маркетинговый обзор, а инженерный разбор: как устроены модули, почему приняты именно такие решения, где случаются ошибки и как мы их ловим.
Шаг 1. Приём и нормализация документов
Тендерная документация приходит в виде архивов: PDF (часто со сканами), DOCX, XLSX, изредка HTML. Первая задача — привести всё это в единый текстовый поток с сохранением структуры.
Что мы делаем:
- PDF с текстовым слоем парсим через
pdfplumber. Он аккуратно сохраняет таблицы и разрывы абзацев, чего часто не хватает более простым библиотекам. - PDF без текстового слоя (сканы) прогоняем через Tesseract OCR с русскоязычной моделью. На этом этапе точность теряется — примерно 94–96% на качественных сканах и 80–85% на плохих.
- DOCX парсим через
python-docx. XLSX черезopenpyxl, отдельно сохраняя каждый лист. - Все документы склеиваются в один поток с пометкой источника: «из файла X, страница Y». Это критично для последующего цитирования — чтобы в сводке можно было сослаться на точное место.
Типичные ошибки. OCR может перепутать цифры на плохом скане — мы отдельно валидируем все числовые поля регулярками и, если проверка падает, помечаем поле как требующее ручной проверки.
Шаг 2. Извлечение ключевых полей с помощью LLM
Главный вопрос: как из текста на 200 страниц вытащить структурированные данные — требования к участникам, ТЗ, сроки, критерии оценки, размер обеспечения?
Можно было бы попытаться написать регулярные выражения под каждое поле, но на практике это работает только на очень стандартизованных документах. Реальные тендеры пишутся по-разному, термины различаются, структура плавает. LLM справляется с этим гораздо лучше.
Как устроен промпт:
- Для каждого ключевого поля — свой промпт с чёткой постановкой:
«Найди в документе требования к опыту участника. Верни JSON с полями
years_of_experience,required_certifications,similar_projects. Если данных нет, верни null.» - В промпт передаётся релевантный фрагмент документа (а не весь текст целиком). Для этого предварительно делаем разметку разделов и передаём только «Раздел 1. Требования к участникам» + соседние страницы.
- На выходе LLM возвращает JSON. Мы валидируем его через Pydantic (или Zod, если бэкенд на TypeScript) — если структура не соответствует ожидаемой, повторяем запрос с уточнением.
Почему не один большой промпт на все поля сразу. Мы так пробовали, и качество заметно падает. LLM начинает забывать одни поля ради других, или генерирует неконсистентный вывод. Разделение на специализированные промпты стабильнее и дешевле (меньше токенов на каждый вызов).
Шаг 3. Валидация извлечённых данных
LLM даёт первую версию. Дальше — проверка.
Что валидируем:
- Типы и форматы. Даты — в формат даты, суммы — в число, лицензии — в список строк.
- Диапазоны. Если модель вернула «срок подачи: 1 января 1900 года» — это явно галлюцинация, и мы её ловим.
- Консистентность. Даты подачи должны быть раньше даты открытия конвертов. Размер обеспечения — это процент от начальной цены контракта, и он должен попадать в разумный диапазон.
Если валидация падает, поле помечается как «требует ручной проверки» и выводится отдельным блоком в интерфейсе — менеджер видит, что именно нужно перепроверить.
Шаг 4. Классификация релевантности
После извлечения полей нам нужно понять: стоит ли компании вообще участвовать в этом тендере? Это уже не задача обработки документов, а задача классификации с бизнес-правилами.
Что мы учитываем:
- Профиль клиента. Коды ОКПД, регионы работы, размер контрактов, которые компания обычно выигрывает, специализация.
- Извлечённые поля. Сумма контракта, требования к опыту, сертификаты, обеспечение заявки.
- Исторические данные. Если компания уже участвовала в похожих тендерах и проигрывала по конкретным критериям, следующие аналогичные лоты снижают оценку.
На выходе — категория лота: «стоит подавать» / «возможно» / «не стоит», плюс объяснение: почему именно такая оценка. Объяснение — это ключевой элемент доверия. Менеджер видит «не стоит: требуется лицензия ФСБ, у компании её нет» и сразу понимает, согласен он с оценкой или нет.
Шаг 5. Генерация сводки
Последний шаг — короткий отчёт на 1 страницу, который менеджер может прочитать за 2 минуты. Это делает LLM на основе уже извлечённых и валидированных данных.
Сводка содержит: суть тендера одним абзацем, ключевые требования, критичные риски, общую оценку релевантности и рекомендацию. Мы специально не просим LLM делать самостоятельный анализ — она работает только с готовыми структурированными данными, чтобы не галлюцинировать.
Что в итоге
Получается не «умная модель, которая читает тендеры», а честно устроенная инженерная система: парсинг → извлечение → валидация → классификация → сводка. Каждый шаг можно отлаживать, оценивать и улучшать отдельно. На нашем тестовом корпусе из 10 000+ документов такой пайплайн показывает точность извлечения ключевых полей около 92% и F1 классификации релевантности около 0.85.
Это примерно в 50–70 раз быстрее ручного анализа на один документ, и, что ещё важнее, качество стабильно: нет эффекта «после обеда менеджер устал и пропустил критичное требование».
Если у вас есть похожая задача по обработке документов — договоров, заявок, отчётов — запишитесь на AI-аудит. Разберём вашу ситуацию и скажем, подходит ли вам такой подход.