На счёте десять полей, и девять из них распознаны верно. Номер документа совпал, дата на месте, ИНН поставщика прочитан без единой ошибки, наименование получилось даже аккуратнее, чем в оригинале. А в банковском счёте вместо восьмёрки оказалась тройка. Для человека, который смотрит на результат по диагонали, всё выглядит прекрасно: девяносто процентов успеха. Для бухгалтерии это платёж не туда или, в лучшем случае, возврат с неприятным письмом контрагенту.

Поэтому задача OCR для входящих счетов формулируется не как «распознать всё», а как «понять, какому из десяти полей нельзя верить без глаза человека, и показать ему именно это поле». Всё остальное — следствие этой формулировки. Разберём, как построить такой процесс на практике и почему он устроен не как одна большая нейросеть, а как цепочка небольших, проверяемых шагов.

Три разные задачи под одним словом «распознавание»

Когда говорят «OCR счетов», обычно смешивают три слоя, у которых разные причины ошибок и разные способы лечения.

  1. Чтение изображения. Из пикселей получаются символы и их координаты на странице. Здесь ошибается «глаз»: тройка вместо восьмёрки, слипшиеся буквы, потерянная запятая.
  2. Понимание структуры. Среди всех строк текста нужно найти, какая из них номер счёта, какая ИНН, а какая является строкой таблицы с позицией и суммой. Здесь ошибается «смысл»: ИНН покупателя принят за ИНН поставщика, итог взят из колонки НДС.
  3. Бизнес-проверки. Значения должны сойтись между собой и с тем, что известно учётной системе: сумма строк с итогом, контрольные разряды идентификаторов, наличие контрагента в справочнике.

Если склеить эти слои в один чёрный ящик, вы получите на выходе значение без объяснения, откуда оно взялось и почему ему можно доверять. Если держать их раздельно, у каждого поля появляется биография: где было прочитано, как отнесено к реквизиту и какие проверки прошло. Именно эта биография потом и решает, нужно ли беспокоить оператора.

Для первых двух слоёв удобно брать инструмент, который разбирает документ в структурированное представление, а не просто выдаёт поток текста. Например, проект Docling поддерживает разбор документов, структурированное представление, OCR и структуру таблиц, а также локальное выполнение. Последнее для счетов существенно: в них есть реквизиты и суммы, и многие предпочитают не отправлять такие страницы во внешние сервисы. Но выбор конкретной библиотеки вторичен. Важнее принцип: на выходе должен быть не плоский текст, а набор фрагментов с положением на странице и типом (абзац, ячейка таблицы, подпись).

Скан скану рознь: что решается до запуска OCR

Самая частая причина «странных» ошибок распознавания лежит не в OCR-движке, а в картинке. В документации Tesseract по улучшению качества прямо перечислены факторы, от которых зависит результат: разрешение, фон, шум, перекос и сегментация страницы. Для входящих счетов это превращается в вполне земные ситуации.

  • Счёт пришёл как PDF, сформированный из учётной программы. В нём уже есть текстовый слой, и вместо распознавания его можно просто прочитать. OCR здесь не нужен вовсе, а ошибка в «тройке» невозможна по определению.
  • Счёт пришёл как PDF-обёртка вокруг фотографии. Текстового слоя нет, есть тень от пальца и лёгкий наклон.
  • Счёт пришёл с мультифункционального аппарата: серый фон, полосы от стекла, штамп поверх реквизитов.
  • Счёт — смесь: основная часть текстовая, а печать с банковскими данными вставлена картинкой.

Первое решение маршрутизации очень простое и сэкономит много времени: для каждой страницы выяснить, есть ли пригодный текстовый слой. Если да, брать его и сохранять координаты. Если нет или слой подозрительно пуст, отправлять страницу на распознавание. Смешанные страницы заслуживают особого внимания: полезно сравнить текстовый слой с результатом OCR по области, где лежит картинка, и не считать страницу «цифровой» только потому, что на ней есть хоть какой-то текст.

Перед OCR: что можно поправить дёшево

Предобработка не должна быть магией. Выпрямить перекос, убрать явный шум, привести разрешение к разумному для мелких цифр, проверить ориентацию страницы. Мелкий шрифт банковских реквизитов в нижней части счёта страдает первым, поэтому разрешение стоит оценивать именно по нему, а не по заголовку. Любое преобразование картинки желательно сохранять как отдельную версию: оператору в итоге нужно показывать фрагмент оригинала, а не результат ваших фильтров, иначе вы рискуете показать человеку «исправленную» картинку, где сомнительная цифра уже стала чёткой, но другой.

Полностраничный режим или гибрид: что выбирать для счетов

У OCR-конвейеров обычно есть выбор: распознавать только те области, где нет текстового слоя, или прогонять всю страницу целиком. В примере Docling показано, как включить полностраничный OCR вместе с извлечением структуры таблиц. Там же отмечено, что выбранный backend требует соответствующих зависимостей, а полный режим часто медленнее гибридного.

Для счетов этот выбор не вопрос вкуса, а вопрос того, где вы готовы платить временем.

Когда какой режим уместен для входящих счетов
СитуацияРежимПочему
Чистый PDF из учётной системыТекстовый слой без OCR или гибридНет смысла вносить ошибки распознавания туда, где текст уже есть
Фотография или скан целикомПолностраничный OCRТекстового слоя нет, вся страница идёт на распознавание
PDF с текстом и картинкой-печатьюГибрид, а при расхождении — полный режим для спорной страницыБыстрый путь для основной части, точечная дорогая проверка там, где данные вызывают подозрение

Разумная стратегия — начинать с дешёвого режима и повышать «цену» только для страниц, где проверки согласованности (о них ниже) не сошлись. Тогда медленный полностраничный проход получают единицы документов, а не вся очередь. Это собственная рекомендация, а не требование какого-то инструмента: сам по себе режим ничего не знает о ваших счетах, и порог эскалации вы подбираете на своих документах.

Здесь же уместно сказать о ресурсах. Распознавание страниц — задача процессорная и по памяти нетребовательная для одиночного потока, но при пакетной загрузке очередь счетов в конце месяца легко упрётся в число ядер. Для небольшого потока хватит скромной конфигурации, а при росте объёмов полезно разделить приём файлов, OCR и интерфейс проверки по разным процессам и масштабировать только узкое место. На VPS это решается выбором тарифа с нужным числом vCPU и объёмом RAM; ресурсы тарифа не равны обещанию конкретной пропускной способности, поэтому скорость обработки одной страницы стоит измерить на своих документах и только потом выбирать план. Диск NVMe пригодится для хранения исходных файлов и фрагментов страниц, о которых речь пойдёт дальше.

Поле без адреса — просто догадка

Центральная идея всей схемы: ни одно извлечённое значение не должно существовать само по себе. Рядом с ним всегда лежит указание, откуда оно взято. Минимальная запись о поле может выглядеть так:

{
  "field": "supplier_inn",
  "value": "7701234567",
  "page": 1,
  "bbox": [412, 188, 590, 210],
  "source": "ocr",
  "checks": ["inn_checksum: passed", "in_registry: passed"]
}

Координаты прямоугольника позволяют вырезать из оригинальной страницы ровно тот кусочек, из которого прочитано число. Источник («текстовый слой» или «OCR») показывает, насколько это чтение подвержено ошибкам глаза. Список проверок — это история того, что с полем уже сделали. Эта запись и есть то, что в конце сделает решение оператора объяснимым: через полгода по ней можно восстановить, почему сумма в учёте именно такая.

Есть соблазн заменить всё это единой цифрой «уверенность 97%». Не стоит. Во-первых, разные движки считают её по-разному, и сравнивать эти числа между собой нельзя. Во-вторых, внутренняя оценка символа ничего не говорит о том, правильно ли значение отнесено к полю: ИНН покупателя, прочитанный на 99%, всё равно окажется не тем ИНН. В-третьих, оператору такая цифра ничего не объясняет. Фраза «сумма строк не равна итогу на 40 рублей» объясняет всё и сразу. Поэтому вместо универсального процента мы опираемся на конкретные причины сомнения, каждая из которых выражается словами и проверяется воспроизводимо.

Причина сомнения — это тип, а не оценка

Заведите короткий закрытый перечень причин, по которым поле попадает в очередь:

  • значение не прошло формальную проверку (контрольный разряд, длина, допустимые символы);
  • два источника дали разные значения (текстовый слой и OCR, либо два места на странице);
  • числа не сошлись арифметически;
  • реквизит не найден в справочнике контрагентов или найден, но с другим названием;
  • поле не найдено вовсе;
  • найдено несколько кандидатов, и однозначно выбрать нельзя;
  • фрагмент визуально плохого качества: перекрыт печатью, смазан, обрезан.

Закрытый перечень удобен тем, что по нему можно строить статистику: если половина обращений к операторам приходится на «перекрыто печатью», проблема не в OCR, а в том, как поставщики подписывают счета, и её можно обсуждать с ними, а не тихо тратить время людей.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Сумма строк, итог и НДС: арифметика как дешёвый детектор

Самое полезное свойство счёта с точки зрения проверки — его избыточность. Одни и те же факты записаны несколько раз в разных формах, и если OCR ошибся в одном месте, редко ошибается ровно так, чтобы всё осталось согласованным.

Вот проверки, которые практически ничего не стоят:

  1. Строка: количество × цена = сумма. Расхождение в копейки может быть округлением, и допуск стоит задать явно, а не молча принимать.
  2. Сумма всех строк = итог без НДС. Если таблица разобрана с пропущенной строкой, здесь это вскроется сразу.
  3. Итог без НДС + НДС = итог к оплате. Ставки и правила округления зависят от документа, поэтому формулу и допуск определяйте по своим поставщикам.
  4. Сумма прописью = сумма цифрами. Если на счёте есть прописью, это бесплатный независимый второй источник.
  5. ИНН: контрольные разряды. Опечатка в одной цифре почти всегда ломает контрольную сумму, и идентификатор не пройдёт проверку.
  6. Банковские реквизиты: связка БИК, корреспондентского и расчётного счёта проверяется по алгоритмам, а БИК ещё и по справочнику.

Вернёмся к нашей тройке вместо восьмёрки. Если она оказалась в расчётном счёте, формальная проверка по БИК и ключу счёта с большой вероятностью её поймает. Если в сумме — разойдётся арифметика. Если ошибка в номере счёта-фактуры, формальных проверок не будет, но можно сравнить номер с тем, что указан в имени файла или теме письма, и с уже существующими документами этого поставщика: дубль номера или выпадение из последовательности сами по себе сигнал.

Теперь важный нюанс, о котором часто забывают. Совпадение проверки — не доказательство верности, а лишь отсутствие улик. Две ошибки могут компенсировать друг друга, а некоторые поля вообще не имеют внутренней проверки: название контрагента, назначение платежа, дата. Поэтому правило эскалации должно быть консервативным и зависеть от поля. Критичные для платежа поля (счёт, БИК, итог) пусть идут в автоматический проход только при пройденных формальных проверках и совпадении двух независимых источников. Менее критичные, например наименование, могут идти с одной проверкой по справочнику. Эти пороги — ваше решение о риске, которое лучше записать в конфигурацию, а не оставлять в голове.

Таблица позиций — самое хрупкое место

Если реквизиты шапки обычно лежат отдельными подписанными блоками, то таблица позиций — территория неоднозначности: объединённые ячейки, переносы строк внутри наименования, колонки без заголовков на второй странице. Именно поэтому полезно использовать инструмент, который восстанавливает структуру таблицы, а не только распознаёт текст ячеек (в примере Docling это включается отдельным параметром вместе с полностраничным OCR). Но даже восстановленная структура — гипотеза, и арифметика итогов здесь работает как её проверка: если суммы по колонке сходятся с итогом, структура, скорее всего, разобрана верно. Если нет, это повод показать оператору всю таблицу целиком, а не одну ячейку, потому что ошибка может быть в границах строк, а не в цифре.

Карточка проверки: что видит человек

Теперь самое главное для людей. Оператор не должен вводить счёт заново и не должен искать спорное место глазами на всей странице. Его рабочая единица — карточка одного сомнения. В ней три обязательных элемента:

  • изображение фрагмента из исходной страницы, вырезанное по координатам поля с небольшим полем вокруг, чтобы было видно контекст (подпись «р/с» рядом с номером, строку таблицы целиком);
  • извлечённое значение в редактируемом поле, чтобы исправить символ, а не набирать всё заново;
  • причина сомнения из закрытого перечня, сформулированная по-человечески.

Пример карточки в текстовом виде:

Поле: расчётный счёт поставщика
Фрагмент: [вырезка страницы 1, область с р/с и БИК]
Извлечено: 40702810300000012343
Причина: ключ счёта не сходится с БИК; вероятная замена одной цифры
Действие: подтвердить / исправить / отметить «не читается»

Обратите внимание, чего здесь нет: нет процента уверенности и нет ничего про устройство движка. Есть то, что оператору нужно для решения. Хорошая карточка делает работу за секунды: человек сверяет картинку с цифрами, видит расхождение и правит один символ. Если проверка после правки проходит, карточка закрывается сама, если нет, поле остаётся спорным, и система не позволит «подтвердить» заведомо неверное значение без явной отметки.

Как не заставить человека проверять всё подряд

Две ошибки в дизайне очереди встречаются чаще всего. Первая: показывать оператору весь документ с подсвеченными полями. Тогда он неизбежно начнёт перепроверять и те, что подсвечены зелёным, и всякая экономия пропадёт. Вторая: выдавать каждое сомнение отдельным заданием, и один счёт с тремя спорными полями попадает к трём людям или трижды открывается одним. Лучше группировать карточки по документу: человек берёт счёт, видит стопку из одной-трёх карточек, закрывает их и отпускает документ дальше.

Уместна и обратная связь. Если оператор исправил значение, фиксируйте, что именно было исправлено и по какой причине сомнения. Через месяц по такой статистике видно, какие проверки ловят настоящие ошибки, а какие создают ложные тревоги, и пороги можно пересмотреть. Если вы строите поиск и ответы над распознанными документами и хотите так же системно оценивать качество, пригодится материал о том, как оценивать качество RAG-ответов без ручной проверки: логика выборочной оценки там во многом родственна.

Что хранить, чтобы решение можно было объяснить

Для каждого счёта в хранилище стоит оставлять не только итоговые значения, но и следы пути:

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

Это не бюрократия ради бюрократии. Когда контрагент пришлёт претензию, а аудитор спросит, почему в учёте именно эта сумма, ответ собирается из записей, а не из памяти. Исходные файлы и вырезки занимают место, поэтому планируйте диск с запасом, а копирование и срок хранения определяйте самостоятельно, не рассчитывая на то, что они включены в тариф по умолчанию.

Серверная часть при этом скромная: сервис приёма файлов, воркер распознавания, небольшая база для записей о полях и веб-интерфейс очереди. Для старта достаточно одного VPS с root-доступом, где всё это работает как несколько процессов. Если вы разворачиваете автоматизацию на готовых инструментах, например связываете приём почты, распознавание и очередь через n8n, среди предложений каталога MAATRIX он есть с установкой, но точное наличие образа и ОС стоит проверить в форме заказа. Граница между «достаточно» и «пора разносить» определяется замером на ваших же счетах, а не размером тарифа.

От спорного поля к проводке

Соберём путь одного счёта целиком. Файл пришёл на почту и сохранён без изменений. Конвейер проверил, есть ли текстовый слой, и выбрал режим: для двух страниц достаточно текста, для третьей, где печать закрыла часть реквизитов, потребовался полный проход. Структура страницы разобрана, поля отнесены к реквизитам, у каждого записаны место и источник.

Дальше сработали проверки согласованности. ИНН и дата прошли. Сумма строк совпала с итогом, итог с НДС сошёлся с суммой к оплате. А вот расчётный счёт не прошёл проверку ключа. Система не пытается угадывать и не отправляет документ дальше: она формирует одну карточку с вырезкой нижней части страницы, считанным значением и причиной. Оператор открывает карточку, видит на картинке, что последняя цифра стоит иначе, чем в извлечённом значении, вносит исправление и подтверждает. Проверка ключа после правки проходит.

Только теперь документ считается подтверждённым и передаётся в учётную систему как запись, где у каждого значения есть происхождение. Ничего не вводилось заново, девять полей из десяти даже не потребовали внимания человека, а десятое исправлено осознанно, с записью о том, кто, когда и по какой причине. Если через полгода кто-то спросит, откуда в платёжке именно этот счёт, ответ найдётся в карточке: вот фрагмент страницы, вот то, что прочитала машина, вот то, что поправил человек, и вот проверка, которая подтвердила окончательное значение.

В этом и состоит смысл всей затеи. Машина берёт на себя рутину и честно отдаёт человеку то, в чём не уверена, а человек видит ровно столько, сколько нужно для решения. Не девяносто процентов успеха, а десять полей, у каждого из которых понятна история.