На счёте десять полей, и девять из них распознаны верно. Номер документа совпал, дата на месте, ИНН поставщика прочитан без единой ошибки, наименование получилось даже аккуратнее, чем в оригинале. А в банковском счёте вместо восьмёрки оказалась тройка. Для человека, который смотрит на результат по диагонали, всё выглядит прекрасно: девяносто процентов успеха. Для бухгалтерии это платёж не туда или, в лучшем случае, возврат с неприятным письмом контрагенту.
Поэтому задача OCR для входящих счетов формулируется не как «распознать всё», а как «понять, какому из десяти полей нельзя верить без глаза человека, и показать ему именно это поле». Всё остальное — следствие этой формулировки. Разберём, как построить такой процесс на практике и почему он устроен не как одна большая нейросеть, а как цепочка небольших, проверяемых шагов.
Три разные задачи под одним словом «распознавание»
Когда говорят «OCR счетов», обычно смешивают три слоя, у которых разные причины ошибок и разные способы лечения.
- Чтение изображения. Из пикселей получаются символы и их координаты на странице. Здесь ошибается «глаз»: тройка вместо восьмёрки, слипшиеся буквы, потерянная запятая.
- Понимание структуры. Среди всех строк текста нужно найти, какая из них номер счёта, какая ИНН, а какая является строкой таблицы с позицией и суммой. Здесь ошибается «смысл»: ИНН покупателя принят за ИНН поставщика, итог взят из колонки НДС.
- Бизнес-проверки. Значения должны сойтись между собой и с тем, что известно учётной системе: сумма строк с итогом, контрольные разряды идентификаторов, наличие контрагента в справочнике.
Если склеить эти слои в один чёрный ящик, вы получите на выходе значение без объяснения, откуда оно взялось и почему ему можно доверять. Если держать их раздельно, у каждого поля появляется биография: где было прочитано, как отнесено к реквизиту и какие проверки прошло. Именно эта биография потом и решает, нужно ли беспокоить оператора.
Для первых двух слоёв удобно брать инструмент, который разбирает документ в структурированное представление, а не просто выдаёт поток текста. Например, проект 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 ошибся в одном месте, редко ошибается ровно так, чтобы всё осталось согласованным.
Вот проверки, которые практически ничего не стоят:
- Строка: количество × цена = сумма. Расхождение в копейки может быть округлением, и допуск стоит задать явно, а не молча принимать.
- Сумма всех строк = итог без НДС. Если таблица разобрана с пропущенной строкой, здесь это вскроется сразу.
- Итог без НДС + НДС = итог к оплате. Ставки и правила округления зависят от документа, поэтому формулу и допуск определяйте по своим поставщикам.
- Сумма прописью = сумма цифрами. Если на счёте есть прописью, это бесплатный независимый второй источник.
- ИНН: контрольные разряды. Опечатка в одной цифре почти всегда ломает контрольную сумму, и идентификатор не пройдёт проверку.
- Банковские реквизиты: связка БИК, корреспондентского и расчётного счёта проверяется по алгоритмам, а БИК ещё и по справочнику.
Вернёмся к нашей тройке вместо восьмёрки. Если она оказалась в расчётном счёте, формальная проверка по БИК и ключу счёта с большой вероятностью её поймает. Если в сумме — разойдётся арифметика. Если ошибка в номере счёта-фактуры, формальных проверок не будет, но можно сравнить номер с тем, что указан в имени файла или теме письма, и с уже существующими документами этого поставщика: дубль номера или выпадение из последовательности сами по себе сигнал.
Теперь важный нюанс, о котором часто забывают. Совпадение проверки — не доказательство верности, а лишь отсутствие улик. Две ошибки могут компенсировать друг друга, а некоторые поля вообще не имеют внутренней проверки: название контрагента, назначение платежа, дата. Поэтому правило эскалации должно быть консервативным и зависеть от поля. Критичные для платежа поля (счёт, БИК, итог) пусть идут в автоматический проход только при пройденных формальных проверках и совпадении двух независимых источников. Менее критичные, например наименование, могут идти с одной проверкой по справочнику. Эти пороги — ваше решение о риске, которое лучше записать в конфигурацию, а не оставлять в голове.
Таблица позиций — самое хрупкое место
Если реквизиты шапки обычно лежат отдельными подписанными блоками, то таблица позиций — территория неоднозначности: объединённые ячейки, переносы строк внутри наименования, колонки без заголовков на второй странице. Именно поэтому полезно использовать инструмент, который восстанавливает структуру таблицы, а не только распознаёт текст ячеек (в примере Docling это включается отдельным параметром вместе с полностраничным OCR). Но даже восстановленная структура — гипотеза, и арифметика итогов здесь работает как её проверка: если суммы по колонке сходятся с итогом, структура, скорее всего, разобрана верно. Если нет, это повод показать оператору всю таблицу целиком, а не одну ячейку, потому что ошибка может быть в границах строк, а не в цифре.
Карточка проверки: что видит человек
Теперь самое главное для людей. Оператор не должен вводить счёт заново и не должен искать спорное место глазами на всей странице. Его рабочая единица — карточка одного сомнения. В ней три обязательных элемента:
- изображение фрагмента из исходной страницы, вырезанное по координатам поля с небольшим полем вокруг, чтобы было видно контекст (подпись «р/с» рядом с номером, строку таблицы целиком);
- извлечённое значение в редактируемом поле, чтобы исправить символ, а не набирать всё заново;
- причина сомнения из закрытого перечня, сформулированная по-человечески.
Пример карточки в текстовом виде:
Поле: расчётный счёт поставщика
Фрагмент: [вырезка страницы 1, область с р/с и БИК]
Извлечено: 40702810300000012343
Причина: ключ счёта не сходится с БИК; вероятная замена одной цифры
Действие: подтвердить / исправить / отметить «не читается»Обратите внимание, чего здесь нет: нет процента уверенности и нет ничего про устройство движка. Есть то, что оператору нужно для решения. Хорошая карточка делает работу за секунды: человек сверяет картинку с цифрами, видит расхождение и правит один символ. Если проверка после правки проходит, карточка закрывается сама, если нет, поле остаётся спорным, и система не позволит «подтвердить» заведомо неверное значение без явной отметки.
Как не заставить человека проверять всё подряд
Две ошибки в дизайне очереди встречаются чаще всего. Первая: показывать оператору весь документ с подсвеченными полями. Тогда он неизбежно начнёт перепроверять и те, что подсвечены зелёным, и всякая экономия пропадёт. Вторая: выдавать каждое сомнение отдельным заданием, и один счёт с тремя спорными полями попадает к трём людям или трижды открывается одним. Лучше группировать карточки по документу: человек берёт счёт, видит стопку из одной-трёх карточек, закрывает их и отпускает документ дальше.
Уместна и обратная связь. Если оператор исправил значение, фиксируйте, что именно было исправлено и по какой причине сомнения. Через месяц по такой статистике видно, какие проверки ловят настоящие ошибки, а какие создают ложные тревоги, и пороги можно пересмотреть. Если вы строите поиск и ответы над распознанными документами и хотите так же системно оценивать качество, пригодится материал о том, как оценивать качество RAG-ответов без ручной проверки: логика выборочной оценки там во многом родственна.
Что хранить, чтобы решение можно было объяснить
Для каждого счёта в хранилище стоит оставлять не только итоговые значения, но и следы пути:
- исходный файл в неизменном виде и его контрольную сумму;
- версию конвейера и выбранный режим распознавания для каждой страницы;
- для каждого поля: значение, страницу, координаты, источник, список проверок и их результат;
- для каждой правки оператора: прежнее значение, новое значение, причину сомнения, время и кто подтвердил.
Это не бюрократия ради бюрократии. Когда контрагент пришлёт претензию, а аудитор спросит, почему в учёте именно эта сумма, ответ собирается из записей, а не из памяти. Исходные файлы и вырезки занимают место, поэтому планируйте диск с запасом, а копирование и срок хранения определяйте самостоятельно, не рассчитывая на то, что они включены в тариф по умолчанию.
Серверная часть при этом скромная: сервис приёма файлов, воркер распознавания, небольшая база для записей о полях и веб-интерфейс очереди. Для старта достаточно одного VPS с root-доступом, где всё это работает как несколько процессов. Если вы разворачиваете автоматизацию на готовых инструментах, например связываете приём почты, распознавание и очередь через n8n, среди предложений каталога MAATRIX он есть с установкой, но точное наличие образа и ОС стоит проверить в форме заказа. Граница между «достаточно» и «пора разносить» определяется замером на ваших же счетах, а не размером тарифа.
От спорного поля к проводке
Соберём путь одного счёта целиком. Файл пришёл на почту и сохранён без изменений. Конвейер проверил, есть ли текстовый слой, и выбрал режим: для двух страниц достаточно текста, для третьей, где печать закрыла часть реквизитов, потребовался полный проход. Структура страницы разобрана, поля отнесены к реквизитам, у каждого записаны место и источник.
Дальше сработали проверки согласованности. ИНН и дата прошли. Сумма строк совпала с итогом, итог с НДС сошёлся с суммой к оплате. А вот расчётный счёт не прошёл проверку ключа. Система не пытается угадывать и не отправляет документ дальше: она формирует одну карточку с вырезкой нижней части страницы, считанным значением и причиной. Оператор открывает карточку, видит на картинке, что последняя цифра стоит иначе, чем в извлечённом значении, вносит исправление и подтверждает. Проверка ключа после правки проходит.
Только теперь документ считается подтверждённым и передаётся в учётную систему как запись, где у каждого значения есть происхождение. Ничего не вводилось заново, девять полей из десяти даже не потребовали внимания человека, а десятое исправлено осознанно, с записью о том, кто, когда и по какой причине. Если через полгода кто-то спросит, откуда в платёжке именно этот счёт, ответ найдётся в карточке: вот фрагмент страницы, вот то, что прочитала машина, вот то, что поправил человек, и вот проверка, которая подтвердила окончательное значение.
В этом и состоит смысл всей затеи. Машина берёт на себя рутину и честно отдаёт человеку то, в чём не уверена, а человек видит ровно столько, сколько нужно для решения. Не девяносто процентов успеха, а десять полей, у каждого из которых понятна история.