Вы когда-нибудь пытались вытащить ИНН из счёта, который прислал новый контрагент, и понимали, что ваш старый regex сломался, потому что бухгалтер решила поменять местами строчки? Я — да, и не раз. B2B-документы — это дикий запад: счета, накладные, УПД, договоры — каждый со своим форматом, вёрсткой и капризами. Два лагеря борются за право парсить этот хаос: классические правила (регулярки + шаблоны) и LLM, работающие локально через Ollama. Сегодня — личный бой без правил. Я разберу оба подхода на настоящих документах, покажу код, замерю точность и скорость, и разложу по полочкам, кому какой вариант подходит.
Важное предупреждение: Дата — 13 мая 2026 года. Все упомянутые версии инструментов актуальны на этот момент. LLaMA 3 уже прошла проверку боем, Ollama стабильна, Tesseract — в версии 5.4.
Проблема: почему B2B-документы — это ад для парсинга
Представьте: у вас сотня счетов от разных контрагентов. В одном реквизиты в таблице, в другом — в random div'ах PDF, в третьем — вообще скан с печатью. Структура документа не гарантирована. Традиционный подход — написать под каждый шаблон свой regex + Tesseract для OCR — ломается при первом же изменении формата. LLM, с другой стороны, может понять контекст, но склонен к 'галлюцинациям' и требователен к ресурсам.
Наша задача — построить B2B Document Extractor, который умеет вытаскивать три ключевых поля: Номер документа, Дата, Сумма. Тестовый полигон — 10 реальных PDF-счетов разного качества (от идеально свёрстанных до кривых сканов).
Решение: сравнение regex vs LLM (Ollama + LLaMA 3)
Мы не будем выбирать победителя навсегда. Я покажу, как выглядит каждый подход, а в конце дам чек-лист для принятия решения.
1 Подготовка инструментов
Устанавливаем всё необходимое:
# Tesseract + русский язык
sudo apt install tesseract-ocr tesseract-ocr-rus
# Ollama (локальный запуск LLM)
curl -fsSL https://ollama.com/install.sh | sh
# Python библиотеки
pip install pytesseract pdf2image pillow requests
Запускаем Ollama и скачиваем LLaMA 3 (последняя версия на май 2026 — llama3:8b, именно с ней и будем работать):
ollama pull llama3:8b
2 Реализация regex-парсера
А вот и типичный подход 'быстро и грязно'. Сначала конвертируем PDF в изображения, затем используем pytesseract для OCR, после чего — регулярки.
Код парсера на regex:
import pytesseract
from pdf2image import convert_from_path
import re
def extract_fields_regex(pdf_path):
images = convert_from_path(pdf_path, dpi=300)
text = ''
for img in images:
text += pytesseract.image_to_string(img, lang='rus+eng')
# Парсим номер документа
doc_num = re.search(r'(Счет\\s*№|Счет-фактура\\s*№)\\s*([\\w\\-]+)', text, re.IGNORECASE)
doc_num = doc_num.group(2) if doc_num else None
# Парсим дату (ДД.ММ.ГГГГ)
date = re.search(r'(от\\s*)?(\\d{2}\\.\\d{2}\\.\\d{4})', text)
date = date.group(2) if date else None
# Парсим сумму (с точкой или запятой)
amount = re.search(r'Итого:\\s*([\\d\\s]+[\\.,]\\d{2})', text)
if not amount:
amount = re.search(r'Сумма\\s*к\\s*оплате:\\s*([\\d\\s]+[\\.,]\\d{2})', text)
amount = amount.group(1) if amount else None
return {'doc_num': doc_num, 'date': date, 'amount': amount}
Результаты на 10 документах: точность 60%. Проблемы: на сканах с низким DPI (300 маловато), на нестандартных шаблонах (если 'Итого' написано жирно и распозналось как 'Итог0'), на суммах с пробелами между разрядами.
Ошибка новичка: Я намеренно не добавил fallback на другой вариант суммы. В реальном проекте таких веток может быть 10+. Именно это делает поддержку regex-парсера адом.
Типичный пример сбоя: документ, где сумма написана прописью ('Сто двадцать три рубля 45 копеек'). Regex бесполезен. Нужна семантика.
3 Реализация LLM-парсера (Ollama + LLaMA 3)
Теперь подключим локальную LLM. Я использую Ollama с моделью llama3:8b (на май 2026 доступна версия, дообученная на русском). Отправляем OCR-текст в модель и просим структурированный ответ.
import requests
import json
OLLAMA_URL = "http://localhost:11434/api/chat"
def extract_fields_llm(text_ocr):
prompt = f"""Из следующего текста счёта извлеки номер документа, дату и сумму.
Верни ТОЛЬКО JSON без пояснений.
Текст: {text_ocr}"""
payload = {
"model": "llama3:8b",
"messages": [{"role": "user", "content": prompt}],
"stream": False
}
response = requests.post(OLLAMA_URL, json=payload)
result = response.json()['message']['content']
try:
return json.loads(result)
except:
return {"error": "Failed to parse LLM response", "raw": result}
def extract_full_pipeline(pdf_path):
images = convert_from_path(pdf_path, dpi=300)
text = ''
for img in images:
text += pytesseract.image_to_string(img, lang='rus+eng')
return extract_fields_llm(text)
Результаты: точность 90% на тех же 10 документах. LLM справился с суммами прописью, нестандартным размещением даты, опечатками в OCR. Но вот нюанс: два документа он 'придумал' номер счёта, которого не было — классическая галлюцинация.
Если вы сталкивались с тем, что LLM врёт о документах, рекомендую прочитать статью Когда LLM врёт о документах: строим логический детектор вместо очередного промпта. Там разобраны методы борьбы с галлюцинациями.
Сравнительный бенчмарк
Я прогнал оба подхода на одном и том же наборе из 10 PDF-документов. Замерял точность (полное совпадение всех трёх полей) и среднее время обработки одного документа.
| Критерий | Regex-парсер | LLM (Ollama + LLaMA 3) |
|---|---|---|
| Точность (полное совпадение) | 60% | 90% |
| Среднее время обработки | 3.2 сек | 12.8 сек |
| Галлюцинации | 0% (только пропуски) | 20% (2 из 10 допридумали поля) |
| Затраты на инфраструктуру | Минимальные (Tesseract) | GPU: 8GB VRAM, CPU: 16GB RAM |
Очевидно: regex быстрее, но ломается на 40% документов. LLM точнее, но медленнее и может соврать. Вывод: не стоит полагаться только на один подход.
Ошибки и нюансы, которые я вынес на своей шкуре
1. Качество OCR критично для обоих подходов
Если Tesseract плохо распознал текст — ни regex, ни LLM не помогут. Совет: подними DPI до 400-600 для сканов, включи preprocessing (бинаризация, дескью). Лучше сразу получить чистый текст.
2. LLM без валидации — бомба замедленного действия
LLaMA 3 иногда выдумывает данные, особенно если OCR текст битый. Решение — двухслойная валидация. Подробно описал в статье Архитектура двухслойной валидации для анализа документов: как LLM + код находят в 2.5 раза больше рисков. Вкратце: после LLM проверяй дату на формат, сумму — на числовой тип, номер документа — на длину.
3. Не используйте LLM для каждого документа подряд
В больших объёмах (сотни тысяч документов) LLM будет слишком дорог и медленен. Гибридный подход: запускай regex сначала. Если regex нашёл все поля с высокой уверенностью — сдавай результат. Если нет — отправляй на LLM. Это снизит затраты в 5-10 раз. Delegation Filter: когда НЕ использовать LLM в продакшн-пайплайнах — хороший чек-лист для такого выбора.
Когда какой подход побеждает?
- Regex — если документы одного-двух шаблонов, требования к точности не жёсткие (достаточно 80-90%), объём большой, инфраструктура слабая.
- LLM — если шаблонов много и они постоянно меняются, OCR грязный, нужна высокая точность (но готов проверять и валидировать).
- Гибрид — оптимален для реальных B2B-систем. Regex как быстрый фильтр, LLM как fallback.
Кстати, в своём проекте я пошёл дальше: добавил ещё один уровень — логический детектор, который проверяет ответ LLM на согласованность. Например, если номер документа пустой или не подходит под маску — переспрашиваю модель с другим промптом. Это снизило галлюцинации до 2-3%.
Для полноты картины, если вас интересует, как развернуть пайплайн с Unstructured и Llama 3 для парсинга PDF в CSV — посмотрите Извлечение данных из PDF в CSV: лучший в 2025 году пайплайн. Там кстати используются похожие принципы, но акцент на CSV-выгрузку.
Что дальше?
Не пытайтесь создать универсальный 'святой грааль'. B2B-документы всегда будут хаосом. Лучшая стратегия — гибкая архитектура, которая позволяет комбинировать инструменты. Начните с малого: соберите реальные документы, замерьте точность на своих данных, и выберите вариант, который даёт допустимую ошибку при приемлемой скорости. А если LLM всё же врёт — не спешите её штрафовать, лучше встройте валидатор. Кстати, о том, как чистить корпоративные справочники от мусора с помощью Structured Output LLM — почитайте Когда номенклатура превращается в бардак. Может пригодиться.