OCR + LLM-агент: автоввод счетов и накладных в 1С — АйТи Фреш
· 17 мин чтения

OCR + LLM-агент для 1С: как мы автоматизировали ввод счетов, накладных и договоров

OCR + LLM-агент для 1С: как мы автоматизировали ввод счетов, накладных и договоров

Полгода назад к нам пришёл клиент с простой, казалось бы, болью: бухгалтер тратит от 3 до 5 минут на каждый входящий счёт, накладную или договор — просто перебивает реквизиты в 1С руками. Казалось бы, задача для нейросети. Первая идея — скормить всё одной vision-модели — разбилась о реальность: у каждого контрагента свои макеты счетов, ТОРГ-12 и УПД — это жёсткие таблицы, а договор — 20 страниц текста, а не картинка с парой строк. В этой статье расскажем, как мы построили гибридный пайплайн OCR+LLM: где проходит граница между «просто дать LLM картинку» и «сначала прогнать через OCR-движок», и как всё это подключается к 1С через OData и HTTP-сервисы.

Зачем гибрид, а не «одна vision-модель на всё»

Первое, что приходит в голову — взять vision-модель уровня Claude Sonnet 5, скормить ей скан целиком и попросить вернуть JSON с реквизитами. На одностраничном счёте это реально работает: в большинстве случаев корректный результат с первого раза. Но стоит добавить в поток три реальных типа документов — счета, накладные ТОРГ-12/УПД и договоры — и экономика с надёжностью сразу расходятся в разные стороны.

Три причины, по которым мы отказались от чистого vision-LLM-подхода.

Поэтому мы строили не «AI, который читает документы», а маршрутизатор. Для каждого конкретного файла система сама решает: добавить OCR-слой перед LLM, отправить скан напрямую в vision-LLM — или, если это длинный договор в PDF, вообще не сканировать, а просто вытащить текстовый слой.

Архитектура: четыре стадии пайплайна

Пайплайн живёт как отдельный Python-сервис. Он забирает вложения из почтового ящика бухгалтерии или из папки обмена с 1С, а на выходе кладёт готовый документ в базу через OData или HTTP-сервис. Внутри — четыре стадии.

  1. Нормализация входа. PDF с текстовым слоем проверяется через pdftotext -layout (poppler-utils) — если текст извлекается и его объём разумный (не «мусорные» 3 символа на страницу от сканированного PDF без OCR-слоя), документ идёт в текстовый режим без всякого OCR. Если текстового слоя нет или PDF — это обёртка над сканом, страницы рендерятся в PNG через pdftoppm -r 300 -png — обязательно не ниже 300 dpi, потому что при 150 dpi точность Tesseract на мелком кегле счетов падает ощутимо.
  2. Классификация макета. Первая (уменьшенная, ~768px по длинной стороне) страница отправляется дешёвой модели — Claude Haiku 4.5 — с задачей определить тип документа (счёт / ТОРГ-12 / УПД / договор / прочее) и грубую оценку качества скана.
  3. Маршрутизация в слой распознавания. В зависимости от типа и качества документ уходит либо в OCR-слой (Tesseract 5 или Yandex Vision OCR) с последующим текстовым запросом к LLM, либо напрямую в vision-LLM с полным изображением страницы.
  4. Извлечение, валидация, запись в 1С. LLM возвращает строго типизированный JSON, значения сверяются со справочниками 1С (контрагенты, номенклатура) через OData, документ с высокой уверенностью проводится автоматически, документ с низкой — уходит в очередь ручной проверки бухгалтеру, и только после подтверждения записывается.

Здесь важно понимать: маршрутизация — это не настройка один раз на уровне контрагента. Это решение для каждого конкретного документа. У одного и того же поставщика типовой счёт может пройти вообще без OCR — LLM просто читает картинку целиком. А плохо отсканированная накладная от него же — уже через полный OCR-слой с ручной проверкой таблицы.

Считаете вручную счета и накладные? Посчитаем, сколько часов бухгалтерии это экономит

Мы разворачиваем такой пайплайн под конкретный набор макетов документов клиента — не универсальный шаблон, а настройку под то, как выглядят именно ваши счета, накладные и договоры, и под конкретную конфигурацию 1С. Опишите, сколько документов в месяц и какие типы преобладают — прикинем экономику и покажем, с чего начать.

Классификация макета: дешёвая модель как диспетчер

Классификатор — не отдельная ML-модель. Это запрос к Claude Haiku 4.5 со строгой JSON-схемой через tool use. На вход подаётся уменьшенное изображение первой страницы — нам нужна общая структура, а не текст, поэтому экономим токены. На выходе — компактный объект, который диспетчер маршрутизации читает дальше.

{ "model": "claude-haiku-4-5-20251001", "max_tokens": 300, "tools": [{ "name": "classify_document", "input_schema": { "type": "object", "properties": { "doc_type": {"type": "string", "enum": ["invoice", "torg12", "upd", "contract", "other"]}, "page_count_estimate": {"type": "integer"}, "scan_quality": {"type": "string", "enum": ["digital_text", "clean_scan", "poor_scan", "handwritten_marks"]}, "has_dense_table": {"type": "boolean"} }, "required": ["doc_type", "scan_quality", "has_dense_table"] } }], "tool_choice": {"type": "tool", "name": "classify_document"} }

Правило маршрутизации у нас простое и жёстко зашито в коде, а не отдано на откуп модели: doc_type=invoice и scan_quality не хуже clean_scan — сразу в vision-LLM без OCR; doc_type in (torg12, upd) или has_dense_table=true — всегда через OCR-слой независимо от качества скана; doc_type=contract — сначала повторная попытка извлечь текстовый слой PDF, и только если её нет — OCR постранично с последующей склейкой текста. poor_scan и handwritten_marks в любом случае поднимают порог уверенности, который потребуется на выходе, чтобы документ провёлся автоматически.

OCR-слой: Tesseract 5 против Yandex Vision OCR

Если маршрутизатор решил, что нужен OCR, встаёт следующий вопрос: локальный движок или облачный? Мы держим оба варианта и переключаемся в зависимости от типа документа и требований к конфиденциальности. У части клиентов данные о поставщиках нельзя отправлять во внешнее облако — это не паранойя, это условия договора.

КритерийTesseract 5 (LSTM)Yandex Vision OCRПрямой vision-LLM (Claude)
Где работаетЛокально, без сетиОблако (Yandex Cloud)Облако (Anthropic API)
Лучше всего дляЧистые сканы 300 dpi+, типовые табличные формыТабличные формы (режим распознавания таблиц), PDF без предварительного рендераСвободные макеты счетов, нестандартная вёрстка
Метрика уверенностиConfidence 0-100 по слову (TSV-вывод)Confidence по слову/блоку в ответеНет нативной — только самооценка модели в JSON
ОграниченияТребует ручной preprocessing (deskew, бинаризация Otsu) при плохом сканеЛимит файла 10 МБ / до 200 страниц; многостраничные — через асинхронный методСторона изображения до 8000 px; при батче свыше 20 изображений размер каждого урезается
СтоимостьБесплатно (только CPU-время)Постраничная тарификация в Yandex CloudТокены на страницу-изображение

Для Tesseract мы всегда явно задаём движок LSTM: tesseract page.png out --oem 1 --psm 6 -l rus+eng -c tessedit_create_tsv=1. Флаг -l rus+eng обязателен даже для русскоязычных документов, потому что суммы, артикулы и часть служебных пометок в счетах у клиентов часто набраны латиницей. --psm 6 подходит для однородного блока текста; для табличных накладных с чёткими колонками мы переключаемся на --psm 4, который лучше держит колоночную структуру. По TSV-выводу считаем среднюю confidence по документу и отдельно по каждому распознанному полю; порог для «доверяем без перепроверки» мы установили на практике на уровне 75 — ниже него поле уходит на повторный OCR с более агрессивной бинаризацией или, если это не помогает, вырезается как кроп и отправляется на уточнение в vision-LLM.

Yandex Vision OCR мы подключаем там, где нужна табличная структура из коробки. В запросе выбирается модель распознавания таблиц, и ответ уже содержит строки и столбцы — не плоский текст, который потом нужно восстанавливать регулярками. Для многостраничных сканов используем асинхронный метод: синхронный рассчитан на одну страницу за вызов. Авторизация — IAM-токен от сервисного аккаунта, обновляется раз в час, либо API-ключ. В продакшен-пайплайне мы используем сервисный аккаунт с ролью на использование Vision OCR.

LLM-слой: структурированное извлечение полей

После OCR — или вместо него, если счёт чистый — в дело вступает LLM. Уже не для распознавания символов, а для смыслового разбора: понять, что «Итого к оплате» и «Всего с НДС» — одно и то же поле, отличить ИНН продавца от ИНН покупателя, привязать строку таблицы к позиции номенклатуры.

Модель выбирается под сложность конкретного документа — не одна на все случаи.

Извлечение всегда идёт через строгую JSON-схему с обязательными полями — через tool use, не через свободный текстовый ответ. Иначе парсинг ответа сам превращается в источник ошибок, и мы это проверили на практике. Пример схемы для счёта и накладной — ниже.

{ "name": "extract_invoice", "input_schema": { "type": "object", "properties": { "doc_number": {"type": "string"}, "doc_date": {"type": "string", "description": "YYYY-MM-DD"}, "seller_inn": {"type": "string"}, "seller_kpp": {"type": "string"}, "buyer_inn": {"type": "string"}, "currency": {"type": "string"}, "total_with_vat": {"type": "number"}, "vat_amount": {"type": "number"}, "line_items": { "type": "array", "items": { "type": "object", "properties": { "name": {"type": "string"}, "unit": {"type": "string"}, "qty": {"type": "number"}, "price": {"type": "number"}, "amount": {"type": "number"}, "field_confidence": {"type": "number", "description": "0-1, самооценка модели"} }, "required": ["name", "qty", "price", "amount"] } } }, "required": ["doc_number", "doc_date", "seller_inn", "total_with_vat", "line_items"] } }

Ключевое правило промпта, которое мы формулируем явно: модель должна переносить напечатанные значения, а не пересчитывать их. LLM склонна «помогать» — если сумма НДС в документе округлена нестандартно, модель может незаметно подставить «правильно посчитанное» число вместо того, что реально напечатано в счёте. Мы прямо пишем в системном промпте: «извлекай значения как они напечатаны в документе, не выполняй пересчёт и округление; если поле нечитаемо — верни null, не угадывай; если видишь арифметическое расхождение — зафиксируй его в отдельном поле discrepancy_note».

Три макета: что меняется в промпте и в маршруте

Разница между типами документов — не только визуальная. Она напрямую влияет на то, что именно OCR-слой должен передать LLM.

Тип документаОсобенность макетаСтратегия распознаванияКуда в 1С
Счёт на оплатуСвободная вёрстка, у каждого контрагента своя; итоги иногда на второй страницеОбычно напрямую в vision-LLM (Sonnet 5/Haiku 4.5) без OCR — макет слишком разный для регулярных правилСчёт на оплату (внутренний документ или основание для Поступления)
ТОРГ-12 / УПДЖёсткая унифицированная форма, плотная таблица позицийOCR с табличной моделью (Yandex Vision или Tesseract --psm 4) → LLM только нормализует текст и мапит на номенклатуру 1СПоступление товаров и услуг (Document_ПоступлениеТоваровУслуг) с табличной частью «Товары»
ДоговорМногостраничный текст, юридические формулировки, редко таблицыИзвлечение текстового слоя PDF напрямую (без OCR, если возможно) → LLM (Opus) вычленяет ключевые условия из полного текстаСправочник «Договоры» + карточка контрагента (реквизиты, срок действия, сумма)

С ТОРГ-12 и УПД есть отдельная головная боль: нужно сопоставить позиции из таблицы с номенклатурой в 1С. OCR может вернуть «Кабель ВВГнг 3х2.5» с опечаткой — на плохом скане «ВВГнг» легко распознаётся как «ВВГиг». Точный поиск по справочнику тут не поможет. Мы делаем нечёткий поиск через расстояние Левенштейна с порогом: обычно достаточно не более 2 правок на строку короче 30 символов. Если совпадение неоднозначно — строка помечается для ручного выбора бухгалтером. Новую позицию справочника автоматически мы не создаём никогда.

Confidence-пороги и человек в контуре

Ни один слой пайплайна не имеет права провести документ в 1С «вслепую». У нас три независимых источника уверенности — и решение принимается по их пересечению, не по одному числу.

УсловиеДействие
OCR-confidence поля < 75 и нет vision-LLM подтвержденияКроп поля отправляется отдельным запросом в vision-LLM как перепроверка
Сумма строк таблицы ≠ итоговая сумма документаДокумент не проводится, уходит в очередь ручной проверки с пометкой расхождения
ИНН контрагента не найден в справочнике 1СЧерновик документа создаётся, но привязка контрагента — «требует подтверждения», новая карточка не создаётся автоматически
Все проверки пройдены, confidence ≥ порогаДокумент проводится автоматически, бухгалтер получает уведомление постфактум

Очередь ручной проверки — это либо простой веб-интерфейс, либо телеграм-бот. Оператор видит скан документа с подсвеченным полем рядом с распознанным значением и жмёт «принять» или «исправить». На первых неделях внедрения в проверку уходит примерно 15–20% документов — в основном это плохие сканы и накладные с рукописными правками. Дальше доля падает: накапливается словарь известных контрагентов и типовых позиций номенклатуры для нечёткого поиска, и система начинает узнавать то, что раньше ставило её в тупик.

Как это попадает в 1С: OData и HTTP-сервисы

Готовый JSON от LLM — это ещё не документ в базе. Мы используем стандартный интерфейс OData (публикуется в конфигураторе, путь вида /odata/standard.odata/), но с одной важной оговоркой: «глубокая вставка» (deep insert) шапки документа вместе с табличной частью через один POST-запрос в OData 1С работает нестабильно и версионно-зависимо, а диагностика ошибок при отказе крайне скудная.

Именно поэтому для документов с табличными частями — например, «Поступление товаров и услуг» — мы разбиваем запись на два шага:

  1. POST в Document_ПоступлениеТоваровУслуг — создаём шапку документа (контрагент, дата, склад, договор), получаем Ref_Key;
  2. Последовательные POST в подчинённый набор Document_ПоступлениеТоваровУслуг_Товары с указанием Ref_Key родителя и LineNumber для каждой строки таблицы.

У части клиентов двухшаговая запись через OData давала расхождения в нумерации строк или не проходила проверку заполнения при проведении. В таких случаях мы отказываемся от OData и пишем в 1С отдельный HTTP-сервис. Почему это надёжнее? Вся бизнес-логика — заполнение табличной части, проверка учётной политики, расчёт НДС по ставке из документа — остаётся на стороне 1С и вызывается одним атомарным запросом. Ничего не разъезжается между шагами.

POST /hs/invoice_import/create HTTP/1.1 Content-Type: application/json { "doc_type": "upd", "contractor_inn": "7712345678", "doc_number": "УПД-4521", "doc_date": "2026-07-02", "warehouse": "Основной склад", "lines": [ {"nomenclature_match": "Кабель ВВГнг 3х2.5", "qty": 200, "unit": "м", "price": 48.5} ], "source_confidence": 0.94 }

Перед созданием документа агент всегда сначала обращается в OData на чтение — GET .../Catalog_Контрагенты?$filter=ИНН eq '7712345678' — чтобы найти существующего контрагента по ИНН, а не полагаться на текстовое совпадение наименования (оно у одного и того же контрагента в разных документах пишется по-разному: с кавычками, с ООО впереди или сзади, сокращённо). Отдельно перед записью проверяем дубль по связке номер+дата+контрагент, чтобы повторная присылка того же скана не задвоила поступление и НДС к вычету. Если контрагент не найден — документ создаётся, но помечается флагом на подтверждение, создание новой карточки в справочник остаётся за бухгалтером.

Экономика и грабли, на которые мы уже наступили

На внедрённых нами пайплайнах картина примерно такая: бухгалтер тратил 3–5 минут на ручной ввод одного документа, теперь — около 30 секунд на подтверждение уже заполненного черновика. Для автоматически проведённых документов подтверждение вообще не нужно, только выборочный постфактум-контроль. Но честно предупреждаем: это цифры по конкретным внедрениям, не универсальный норматив. Доля ручной проверки и итоговая экономия времени сильно зависят от того, насколько качественные сканы присылают контрагенты конкретного клиента.

Грабли, которые стоит знать заранее:

Если бы мы строили этот пайплайн с нуля — сразу заложили бы раздельные confidence-метрики по каждому полю, а не одну общую на весь документ. Мы наступили на эти грабли сами: считали единую уверенность на весь счёт, и из-за одного нечитаемого поля в очередь ручной проверки улетал документ целиком — даже когда остальные 90% полей были распознаны верно. Лишняя работа для оператора, которой можно было избежать.

Частые вопросы

Можно ли обойтись без OCR-движков и отдать всё Claude?
Для однотипных чистых счетов — да, это рабочий и самый простой вариант. Но на плотных таблицах ТОРГ-12/УПД и на многостраничных договорах гибрид с OCR-слоем и текстовым извлечением из PDF выходит дешевле и стабильнее, потому что каждая страница-изображение в vision-LLM стоит фиксированный объём токенов независимо от объёма текста на ней.
Как выбрать между Tesseract и Yandex Vision OCR?
Tesseract — бесплатный локальный вариант, хорош на чистых сканах от 300 dpi и там, где данные нельзя передавать во внешнее облако. Yandex Vision OCR выигрывает на табличных формах за счёт встроенного режима распознавания таблиц и меньше требует ручной предобработки изображения, но это платный внешний сервис с постраничной тарификацией и лимитом на размер файла.
Что делать, если LLM неправильно посчитала НДС или итоговую сумму?
Промпт должен явно запрещать модели пересчитывать значения — задача LLM только перенести напечатанные цифры, а не проверять арифметику документа. Расхождение между суммой строк и итоговой суммой — это отдельная проверка на этапе валидации, а не повод довериться пересчитанному моделью числу.
Нужно ли переписывать структуру справочников в 1С под этот пайплайн?
Нет, пайплайн работает поверх существующей структуры — обращается к тем же справочникам Контрагенты и Номенклатура через OData на чтение и создаёт стандартные документы (Поступление товаров и услуг и другие) тем же способом, каким их создал бы бухгалтер вручную. Изменения нужны только в части публикации OData/HTTP-сервисов, если они ещё не включены на информационной базе.
Как пайплайн реагирует на новых контрагентов, которых ещё нет в 1С?
Автоматически новую карточку контрагента система не создаёт — при отсутствии совпадения по ИНН документ создаётся как черновик с пометкой «контрагент требует подтверждения», и решение о создании новой карточки остаётся за бухгалтером, чтобы избежать дублей от опечаток OCR.
📄
Скачайте подробный разбор в PDF Кейсы, статистика, типовые ошибки и чек-лист самопроверки — 11 страниц
Скачать PDF

Подпишитесь на разборы ITfresh

Раз в неделю — практичные материалы по ИТ для бизнеса. Без спама, только польза.