Skip to Content

Конвейер пакетной обработки

Описание

Конвейер пакетной обработки — это JSON-конфигурация, которая определяет, как будут обрабатываться документы в пакете. Конвейер описывает последовательность шагов: классификацию страниц, распознавание полей, NLP-извлечение данных, постобработку и валидацию результатов.

Обработка идёт в две фазы.

Основной конвейер (steps) работает постранично. Это дерево шагов: корневой шаг — классификация — определяет тип каждой страницы, а дочерние шаги обрабатывают страницу в зависимости от результата классификации. Шаг основного конвейера всегда видит одну страницу, а не документ целиком.

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

Фаза 1 — основной конвейер, по странице Классификация (SmartOCR или NLP) ├── Класс «Счёт-фактура» → Распознавание (SmartOCR) → Постобработка ├── Класс «Паспорт» → Распознавание (SmartOCR) └── Класс «Договор» → Извлечение NLP ↓ страницы объединяются в документы по documentDefinitions Фаза 2 — конвейеры документов, по документу целиком Определение «Счёт-фактура» → Извлечение NLP по всем страницам документа Определение «Договор» → Постобработка объединённых полей

Переход между фазами виден в результате: processing.docsStructureReadyAt отмечает момент, когда структура документов зафиксирована, processing.readyAt — окончание всей обработки. Если ни у одного определения нет своего конвейера, пакет завершается сразу после первой фазы.

Структура конвейера

{ "documentDefinitions": [ ... ], "notClassifiedDefinition": { ... }, "steps": [ ... ] }
ПолеОписание
stepsКорневые шаги основного конвейера (обычно один шаг классификации). Не может быть пустым
documentDefinitionsПравила группировки страниц в документы и конвейер каждого документа (подробнее)
notClassifiedDefinitionНастройки и конвейер для нераспознанных страниц

Идентификаторы шагов уникальны в пределах своего конвейера: шаг основного конвейера и шаг конвейера документа могут называться одинаково, а два шага одного конвейера — нет.

Шаги конвейера

Каждый шаг (PackagePipelineStep) содержит:

ПолеОписаниеОбязательное
idИдентификатор шага (3–50 символов). Не может повторяться в пределах своего конвейераДа
routingKeyКлюч маршрутизации — указывает, какой агент обработает шагКроме шага постобработки
metaТип шага: компонент и его подтипДа
classConditionТочное имя класса — шаг выполнится только для страниц этого классаНет
classConditionRegexРегулярное выражение для фильтрации по классуНет
complexConditionСоставное условие выполнения шага с булевой алгеброй (подробнее)Нет
classifyПараметры классификации (для шагов классификации)Для классификации
recognitionОбработка ответа агента: какие поля перезаписывать и с какой уверенностью приниматьДля распознавания и NLP
rulesПравила обработки уже распознанных полей (подробнее)Для шага постобработки
nlpПараметры NLP (для шагов NLP)Для NLP
nextStepsДочерние шаги, выполняемые после текущегоНет
⚠️

Правила обработки полей (postProcessing, validations, removeHeaderRows, removeColumns, cellPostProcessing, cellValidations, jsonFieldExtractors) задаются только у шага постобработки, в блоке rules. У шага, который обращается к агенту, их указывать нельзя — конвейер не пройдёт проверку.

Мета-информация (meta)

{ "meta": { "component": "SmartOCR", "smartOcrType": "Classification" } }
ПолеЗначения
component"SmartOCR", "NLP" или "PostProcessing"
smartOcrType"Classification" или "Recognition" (только для SmartOCR)
nlpType"Extraction" или "Classification" (только для NLP)
⚠️

Для SmartOCR-шагов указывайте smartOcrType и не указывайте nlpType. Для NLP-шагов верно обратное. Для шага постобработки не указывайте ни того, ни другого: агент не вызывается.

Классификация

Классификация — первый шаг конвейера. Определяет тип каждой страницы документа.

Классификация через Умный OCR

{ "id": "classify", "routingKey": "ocr-clas-a1b2", "meta": { "component": "SmartOCR", "smartOcrType": "Classification" }, "classify": { "fallback": "Unknown", "postProcessing": [], "classesConfidenceThreshold": { "Invoice": 0.8, "Passport": 0.7 } }, "nextSteps": [ ... ] }
ПолеОписание
classify.fallbackКласс, присваиваемый странице, если модель не смогла определить тип
classify.classesConfidenceThresholdМинимальная уверенность для каждого класса (0–1). Если уверенность ниже порога, страница получает класс fallback
classify.postProcessingПравила постобработки результатов классификации

Классификация через NLP

Языковая модель используется для классификации вместо Умного OCR. В nlp.schema перечисляются допустимые классы.

{ "id": "classify-nlp", "routingKey": "nlp-clas-a3k2", "meta": { "component": "NLP", "nlpType": "Classification" }, "classify": { "fallback": "Unknown" }, "nlp": { "addImage": true, "schema": ["Паспорт", "Договор", "Другое"], "promptBuilder": { "addUnifiedText": false, "addAnswer": false, "addRawAnswer": false }, "temperature": 0.0, "minP": 0.1, "responseLength": 1024 }, "nextSteps": [ ... ] }
ℹ️

NLP-агенты классификации разворачиваются с предварительно настроенными промптами. В конвейере достаточно указать schema — список допустимых классов. Не указывайте prependPrompt для классификации. Это вызовет конфликт со встроенным промптом агента.

Условия выполнения шага

Дочерние шаги обрабатывают только страницы, удовлетворяющие заданным условиям. Есть два способа задать условия: простые поля classCondition / classConditionRegex или составное условие complexCondition.

Маршрутизация по классу (classCondition) #class-routing

Простейший способ фильтрации — по имени класса:

ПолеОписаниеПример
classConditionТочное совпадение имени класса (с учётом регистра)"Invoice"
classConditionRegexРегулярное выражение (синтаксис C# Regex)"^Invoice.*"

Если оба поля указаны, применяется логика И — оба условия должны совпасть. Если ни одно не указано, шаг выполняется для всех документов.

Пример — распознавание запускается только для страниц, классифицированных как Invoice:

{ "id": "recognize-invoice", "routingKey": "ocr-rec-struct-c3d4", "meta": { "component": "SmartOCR", "smartOcrType": "Recognition" }, "classCondition": "Invoice", "nextSteps": [] }

Составное условие (complexCondition) #complex-condition

Для сложных сценариев маршрутизации используйте complexCondition — составное условие с поддержкой булевой алгебры (И / ИЛИ / НЕ). Составное условие позволяет комбинировать несколько критериев: класс документа, уверенность классификации, количество и наличие распознанных полей, условия на значения полей и ячеек.

complexCondition можно использовать вместо или вместе с classCondition / classConditionRegex. Если указаны оба варианта, шаг выполнится только при совпадении всех условий.

Структура complexCondition

{ "complexCondition": { "classCondition": "Invoice", "classConditionRegex": null, "confidence": { "min": 0.8, "max": null }, "fieldCount": { "min": 3, "max": null }, "hasField": { "fieldKey": "INN", "fieldKeyRegex": null, "valueState": "Filled" }, "fieldConditions": [ ... ], "cellConditions": [ ... ], "and": [ ... ], "or": [ ... ], "negate": false } }

Все поля внутри complexCondition необязательные. Собственные проверки условия и вложенные условия and объединяются логикой И — все должны быть выполнены. Список or — это альтернатива собственным проверкам: условие выполнено, если выполнены они либо хотя бы одно условие из списка. Поле negate инвертирует итоговый результат.

Поля составного условия

ПолеТипОписание
classConditionstringТочное совпадение имени класса
classConditionRegexstringРегулярное выражение для класса
confidenceobjectДиапазон уверенности классификации
fieldCountobjectДиапазон количества распознанных полей
hasFieldobjectПроверка наличия поля по ключу
fieldConditionsarrayУсловия на поля (аналогичны FieldConditions распознавания). Объединяются через ИЛИ
cellConditionsarrayУсловия на ячейки таблиц. Объединяются через ИЛИ
andarray<complexCondition>Вложенные составные условия, объединённые через И
orarray<complexCondition>Альтернативы собственным проверкам условия (подробнее)
negatebooleanИнвертировать итоговый результат (true = НЕ)

Диапазон уверенности (confidence)

Фильтрация по уверенности классификации страницы (значение classConfidence от 0 до 1):

{ "confidence": { "min": 0.8, "max": 0.95 } }
ПолеТипОписание
mindecimalМинимальная уверенность (включительно)
maxdecimalМаксимальная уверенность (включительно)

Оба поля необязательные — можно задать только нижнюю или только верхнюю границу.

Количество полей (fieldCount)

Фильтрация по количеству распознанных полей на странице:

{ "fieldCount": { "min": 3, "max": 10 } }
ПолеТипОписание
minintМинимальное количество полей
maxintМаксимальное количество полей

Наличие поля (hasField)

Проверяет, что среди распознанных полей есть поле с заданным ключом и что его значение находится в нужном состоянии:

{ "hasField": { "fieldKey": "INN", "valueState": "Filled" } }
ПолеТипОписание
fieldKeystringТочное имя поля
fieldKeyRegexstringРегулярное выражение для имени поля (альтернатива fieldKey)
valueStatestringКаким должно быть значение поля. По умолчанию "Filled"

Укажите fieldKey или fieldKeyRegex (не оба одновременно).

Состояния значения:

ЗначениеУсловие выполнено, когда
"Filled"у поля есть непустой текст либо непустая таблица
"Empty"поле распознано, но значения в нём нет — ни текста, ни ячеек
"Any"поле есть, значение любое
ℹ️

У табличного поля текста не бывает — значение лежит в ячейках, поэтому проверить таблицу условием на текст нельзя. "Filled" учитывает и текст, и ячейки, так что для таблицы подходит именно оно.

Если поле с таким именем встречается на странице несколько раз, "Filled" выполняется, когда непусто хотя бы одно значение, а "Empty" — только когда пусты все. Состояние "Empty" требует, чтобы поле существовало: если его нет вовсе, условие не выполнится. Чтобы охватить и этот случай, используйте инверсию: { "negate": true, "hasField": { "fieldKey": "INN", "valueState": "Filled" } }.

Логика ИЛИ через массивы fieldConditions / cellConditions

Массивы fieldConditions и cellConditions используют ту же структуру, что и условия в параметрах распознавания. Внутри массива элементы объединяются через ИЛИ — достаточно совпадения хотя бы одного.

Вложенные условия (and) и инверсия (negate)

Массив and содержит вложенные complexCondition, объединённые через И. Каждый вложенный элемент может содержать собственные условия, включая and, or и negate, что позволяет строить произвольные булевы выражения.

Альтернативы (or) #or-conditions

Массив or содержит вложенные complexCondition и работает как альтернатива собственным проверкам условия: условие выполнено, если выполнены его собственные проверки либо хотя бы одно условие из списка. Если кроме or в условии ничего не задано, решает только список.

{ "complexCondition": { "or": [ { "classCondition": "Invoice" }, { "classCondition": "Receipt" } ] } }

Шаг выполняется, если класс — Invoice или Receipt.

{ "complexCondition": { "hasField": { "fieldKey": "Таблица", "valueState": "Filled" }, "or": [{ "classCondition": "Invoice" }] } }

Здесь достаточно любого из двух: либо таблица непустая, либо класс — Invoice.

Чтобы получить «собственные проверки И (то либо это)», вложите список альтернатив в условие and:

{ "complexCondition": { "hasField": { "fieldKey": "Таблица", "valueState": "Filled" }, "and": [ { "or": [ { "hasField": { "fieldKey": "Основание", "valueState": "Empty" } }, { "fieldConditions": [{ "fieldKey": "Основание", "textRegex": "(?i)(акт|сч[её]т|заказ)" }] } ] } ] } }

Шаг выполнится, если таблица непустая и при этом основание либо не распозналось, либо ссылается на другой документ.

Примеры

Шаг для счетов с высокой уверенностью и наличием ИНН:

{ "id": "process-confident-invoice", "routingKey": "ocr-rec-struct-c3d4", "meta": { "component": "SmartOCR", "smartOcrType": "Recognition" }, "complexCondition": { "classCondition": "Invoice", "confidence": { "min": 0.85 }, "hasField": { "fieldKey": "INN", "valueState": "Filled" } }, "nextSteps": [] }

Шаг для документов с 5+ полями, но НЕ для класса «Unknown»:

{ "complexCondition": { "fieldCount": { "min": 5 }, "and": [ { "classCondition": "Unknown", "negate": true } ] } }

Распознавание (SmartOCR Recognition)

Извлекает поля из страницы с помощью структурированного OCR.

{ "id": "recognize-invoice", "routingKey": "ocr-rec-struct-c3d4", "meta": { "component": "SmartOCR", "smartOcrType": "Recognition" }, "classCondition": "Invoice", "recognition": { "overrideClassIfEmpty": null, "overrideFields": [], "fieldsConfidenceThreshold": { "INN": 0.5, "TotalAmount": 0.4 } }, "nextSteps": [] }

Блок recognition описывает только то, как принять ответ агента:

ПолеОписание
fieldsConfidenceThresholdМинимальная уверенность для каждого поля (0–1). Поля ниже порога отбрасываются
overrideClassIfEmptyПрисвоить указанный класс, если страница не была классифицирована
overrideFieldsСписок полей, которые при повторном распознавании заменяют существующие значения

Правила обработки самих значений — правка текста, валидация, работа с таблицами — задаются отдельным шагом постобработки, который подвешивается дочерним к шагу распознавания.

Извлечение через NLP #nlp-extraction

Извлекает поля из документа с помощью языковой модели. В schema указываются имена извлекаемых полей.

{ "id": "extract-contract", "routingKey": "nlp-extr-4bo9", "meta": { "component": "NLP", "nlpType": "Extraction" }, "classCondition": "Contract", "nlp": { "addImage": true, "schema": ["Номер договора", "Дата", "Контрагент", "Сумма"], "promptBuilder": { "addUnifiedText": false, "addAnswer": false, "addRawAnswer": false }, "temperature": 0.0, "minP": 0.1, "responseLength": 1024 }, "nextSteps": [] }

Параметры NLP

ПолеОписаниеПо умолчанию
addImageПередать изображение страницы модели-
schemaИмена полей для извлечения-
temperatureСлучайность ответа (0–2). Для извлечения рекомендуется 0.00.0
minPМинимальная вероятность токена (0–1)0.1
responseLengthМаксимальное количество токенов в ответе1024

Управление промптом (promptBuilder)

promptBuilder управляет данными, которые передаются в промпт языковой модели:

ПолеОписание
addUnifiedTextВключить текст, распознанный на предыдущих шагах (OCR)
addAnswerВключить ответы предыдущих NLP-шагов
addRawAnswerВключить сырые ответы предыдущих NLP-шагов
prependPromptТекст, добавляемый перед данными
appendPromptТекст, добавляемый после данных
fieldsByConditionsВключить конкретные распознанные поля, отобранные по условиям
tableColumnsВключить отдельные столбцы распознанных таблиц (подробнее)

Столбцы таблицы в промпте (tableColumns) #table-columns

Передавать модели таблицу целиком дорого и обычно не нужно: как правило, интересен один столбец. tableColumns кладёт в промпт значения выбранного столбца списком.

{ "promptBuilder": { "prependPrompt": "Ниже наименования товаров из таблицы документа.", "tableColumns": [ { "conditions": { "fieldKey": "Таблица" }, "columnHeaderRegex": "Наименование товара", "promptKey": "наименования", "skipEmptyCells": true } ] } }
ПолеОписание
conditionsУсловия отбора табличных полей (fieldKey, fieldKeyRegex и др.)
columnIndexНомер столбца, с нуля. Нумерация — после применения правил удаления столбцов
columnHeaderТочное совпадение текста ячейки в строке 0 (заголовка)
columnHeaderRegexРегулярное выражение по тексту заголовка
promptKeyКлюч, под которым столбец попадёт в промпт. По умолчанию ИмяПоля[НомерСтолбца]
skipEmptyCellsПропускать пустые ячейки. По умолчанию true

Укажите columnIndex, columnHeader или columnHeaderRegex — хотя бы одно. Если задан номер, он имеет приоритет над заголовком.

⚠️

Заголовок ищется в строке 0 таблицы, поэтому выбор по заголовку работает, только пока шапка не срезана правилами удаления строк. В конвейере документа шапки обычно уже нет — там остаётся columnIndex.

В конвейере документа значения столбца собираются по всем страницам документа в порядке страниц — так же, как строки таблицы объединяются в итоговом результате.

ℹ️

NLP-агенты разворачиваются с предварительно настроенными промптами. По умолчанию достаточно указать schema и источник данных (addImage или addUnifiedText). Используйте prependPrompt / appendPrompt только когда нужно дополнить стандартное поведение.

⚠️

Для NLP-шага обязательно должен быть указан хотя бы один источник данных: addImage, prependPrompt, addAnswer, addRawAnswer, addUnifiedText, fieldsByConditions или appendPrompt.

Цепочка OCR → NLP

OCR распознаёт структурированные поля, затем NLP уточняет или извлекает дополнительные данные на основе OCR-результатов:

{ "id": "recognize-invoice", "routingKey": "ocr-rec-struct-u3v4", "meta": { "component": "SmartOCR", "smartOcrType": "Recognition" }, "classCondition": "Invoice", "nextSteps": [ { "id": "refine-invoice-nlp", "routingKey": "nlp-extr-w5x6", "meta": { "component": "NLP", "nlpType": "Extraction" }, "recognition": { "overrideFields": ["PaymentTerms", "Notes"] }, "nlp": { "addImage": false, "promptBuilder": { "addUnifiedText": true, "addAnswer": false, "addRawAnswer": false }, "schema": ["PaymentTerms", "Notes", "DeliveryAddress"], "temperature": 0.0, "minP": 0.1, "responseLength": 1024 } } ] }

В данном примере:

  • NLP-шаг не имеет classCondition — он выполняется для всех документов, дошедших до него через родительский шаг
  • addUnifiedText: true передаёт OCR-текст в промпт модели
  • recognition.overrideFields указывает, какие существующие поля NLP-шаг имеет право перезаписать

Шаг постобработки #post-processing-step

Шаг постобработки не обращается к агенту — он применяет правила к уже распознанным полям. Поэтому у него нет routingKey, classify, recognition и nlp: только rules.

{ "id": "postprocess-invoice", "meta": { "component": "PostProcessing" }, "rules": { "jsonFieldExtractors": [ ... ], "postProcessing": [ ... ], "validations": [ ... ], "removeHeaderRows": [ ... ], "removeRows": [ ... ], "removeColumns": [ ... ], "cellPostProcessing": [ ... ], "cellValidations": [ ... ] }, "nextSteps": [] }

Должно быть задано хотя бы одно правило. Правила применяются в фиксированном порядке, независимо от порядка в JSON:

  1. jsonFieldExtractors — разбор json-полей в отдельные поля
  2. postProcessing — правка значений полей
  3. validations — проверка полей
  4. removeHeaderRows — удаление заголовочных строк таблиц
  5. removeRows — удаление строк таблиц
  6. removeColumns — удаление столбцов таблиц
  7. cellPostProcessing — правка ячеек
  8. cellValidations — проверка ячеек

Порядок важен: например, удаление столбцов меняет их нумерацию, и cellPostProcessing работает уже с новыми номерами.

Где выполняется шаг, зависит от того, в каком конвейере он стоит:

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

Условия выполнения (classCondition, classConditionRegex, complexCondition) работают так же, как у остальных шагов.

ℹ️

Шаг постобработки исполняется сервером сразу, без очереди к агенту, поэтому в stepsInWork он не появляется. Выполненный шаг отмечается в stepIds страницы и документа.

Разбор json-полей (jsonFieldExtractors) #json-field-extractors

Некоторые модели возвращают в одном поле json целиком. Извлекатель достаёт из него значение по JSONPath и кладёт в отдельное поле:

{ "rules": { "jsonFieldExtractors": [ { "sourceFieldKey": "Продавец_инн_кпп", "jsonPath": "$.Продавец_инн", "targetFieldKey": "поставщик_инн" } ] } }
ПолеОписание
sourceFieldKeyИмя поля, в котором лежит json
jsonPathПуть к значению внутри json
targetFieldKeyИмя поля, в которое попадёт значение

Если извлечь значение не удалось — поля нет, в нём не json или по пути пусто, — целевое поле всё равно создаётся пустым. По нему видно, что извлечение не сработало, и это состояние можно поймать условием "valueState": "Empty".

Правка значений полей (postProcessing) #post-processing

Правка меняет значения полей после распознавания. Задаётся в rules.postProcessing шага постобработки. Отдельно существует classify.postProcessing — правка результатов классификации до выбора победившего класса.

{ "rules": { "postProcessing": [ { "conditions": { "fieldKey": "INN" }, "modifyRules": { "text": { "regexPattern": "[^0-9]", "replacement": "" } } }, { "conditions": { "fieldKeyRegex": "Date.*", "minConfidence": 0.3 }, "modifyRules": { "text": { "regexPattern": "(\\d{2})\\.(\\d{2})\\.(\\d{4})", "replacement": "$3-$2-$1" } } } ] } }

Условия отбора полей (conditions)

Все условия опциональны. Если указано несколько, применяется логика И.

ПолеОписание
fieldKeyТочное совпадение имени поля
fieldKeyRegexРегулярное выражение для имени поля
textТочное совпадение текста
textRegexРегулярное выражение для текста
minConfidenceМинимальная уверенность (0–1)
maxConfidenceМаксимальная уверенность (0–1)
jsonPathConditionУсловие по JSONPath: { "jsonPath": "$.field", "valueRegex": "\\d+" }

Правила модификации (modifyRules)

Ключ правила — "field" (имя поля) или "text" (текст поля). Значение — объект с regexPattern и replacement.

Примеры:

  • Удалить все нецифровые символы из ИНН: "regexPattern": "[^0-9]", "replacement": ""
  • Переформатировать дату из ДД.ММ.ГГГГ в ГГГГ-ММ-ДД: "regexPattern": "(\\d{2})\\.(\\d{2})\\.(\\d{4})", "replacement": "$3-$2-$1"

Валидация (validations) #validations

Валидация проверяет извлечённые поля и устанавливает флаг isValid. Задаётся в rules.validations шага постобработки.

{ "rules": { "validations": [ { "conditions": { "fieldKey": "INN" }, "validations": [{ "textRegex": "^\\d{10}$|^\\d{12}$" }] }, { "conditions": { "fieldKey": "TotalAmount" }, "validations": [ { "textRegex": "^[\\d\\s,.]+$" }, { "minConfidence": 0.4 } ] } ] } }
  • conditions — отбирает поля для валидации (те же условия, что и в постобработке)
  • validations[] — массив правил. Все правила должны пройти, чтобы поле получило isValid = true
  • Доступные правила: textRegex, text, minConfidence, maxConfidence, fieldKey, fieldKeyRegex, jsonPathCondition

Результат: поле получает isValid = true, если все правила выполнены, false — если хотя бы одно не выполнено. Поля, не попавшие под conditions, сохраняют isValid = null.

ℹ️

Постобработка выполняется до валидации. Валидация проверяет уже модифицированные значения.

Удаление заголовочных строк (removeHeaderRows) #remove-header-rows

Удаляет повторяющиеся заголовочные строки из табличных полей. Типичный сценарий — многостраничная таблица, где на каждой странице повторяется шапка таблицы.

Удаление выполняется каскадно сверху вниз: строки проверяются начиная с первой (row 0). Если строка соответствует одному из правил — она удаляется, и проверяется следующая. Каскад прекращается на первой строке, которая не совпала ни с одним правилом.

{ "rules": { "removeHeaderRows": [ { "conditions": { "fieldKey": "invoice_table" }, "rules": [ { "rowIndex": 0 }, { "cellMatchers": [ { "textRegex": "^(Наименование|Название)$", "minCount": 1 }, { "textRegex": "^Кол-?во$", "exactCount": 1 } ] } ] } ] } }

Конфигурация

Каждый элемент массива removeHeaderRows содержит:

ПолеОписание
conditionsУсловия отбора табличных полей (fieldKey, fieldKeyRegex и др.)
rulesМассив правил определения заголовочных строк. Правила связаны через ИЛИ — строка считается заголовочной, если совпало хотя бы одно правило

Правила (HeaderRowRule)

Правило работает в одном из двух режимов:

ПолеОписание
rowIndexПринудительно удалить строку с указанным индексом (0 — первая строка). Если задан, cellMatchers игнорируются
cellMatchersМассив матчеров ячеек. Все матчеры должны совпасть (логика И)

Матчеры ячеек (CellMatcher)

Проверяют, содержит ли строка ячейки, соответствующие заданным условиям:

ПолеОписание
textRegexРегулярное выражение для текста ячейки
minCountМинимальное количество ячеек в строке, соответствующих textRegex
exactCountТочное количество ячеек в строке, соответствующих textRegex

Указывается либо minCount, либо exactCount (не оба). Если не указано ни одного, матчер требует хотя бы одно совпадение.

ℹ️

После удаления строк индексы оставшихся ячеек автоматически пересчитываются, а rawTable перестраивается.

Удаление строк (removeRows) #remove-rows

Удаляет из таблицы любые строки, подходящие под правила. Правила те же, что у removeHeaderRows (rowIndex либо cellMatchers), но каскада сверху вниз нет: проверяется каждая строка таблицы.

{ "rules": { "removeRows": [ { "conditions": { "fieldKey": "invoice_table" }, "rules": [ { "rowIndex": -1 }, { "cellMatchers": [{ "textRegex": "^Итого", "minCount": 1 }] } ] } ] } }

Отрицательный rowIndex отсчитывается от конца таблицы: -1 — последняя строка, -2 — предпоследняя. Это удобно для строки «Итого», номер которой заранее неизвестен.

Удаление столбцов (removeColumns) #remove-columns

Удаляет столбцы из табличных полей по индексу или по тексту заголовка (ячейка в строке 0).

{ "rules": { "removeColumns": [ { "conditions": { "fieldKey": "invoice_table" }, "rules": [ { "columnIndex": 0 }, { "headerTextRegex": "^(Примечание|Комментарий)$" } ] } ] } }

Конфигурация

Каждый элемент массива removeColumns содержит:

ПолеОписание
conditionsУсловия отбора табличных полей (fieldKey, fieldKeyRegex и др.)
rulesМассив правил определения столбцов для удаления. Правила связаны через ИЛИ

Правила (ColumnRule)

ПолеОписание
columnIndexИндекс столбца для удаления (0 — первый столбец)
headerTextRegexРегулярное выражение для текста ячейки в строке 0 (заголовка)

Указывается либо columnIndex, либо headerTextRegex.

ℹ️

После удаления столбцов индексы оставшихся ячеек автоматически пересчитываются, а rawTable перестраивается.

Постобработка ячеек таблиц (cellPostProcessing) #cell-post-processing

Модифицирует текст ячеек в табличных полях. Использует двухуровневую фильтрацию: сначала отбирает поля (conditions), затем ячейки внутри них (cellConditions).

{ "rules": { "cellPostProcessing": [ { "conditions": { "fieldKey": "invoice_table" }, "cellConditions": { "columnHeader": "Сумма", "textRegex": "^\\s*\\d" }, "modifyRule": { "regexPattern": "\\s", "replacement": "" } } ] } }

Условия отбора ячеек (cellConditions)

ПолеОписание
rowIndexИндекс строки
columnIndexИндекс столбца
columnHeaderТочное совпадение текста ячейки в строке 0 того же столбца
columnHeaderRegexРегулярное выражение для заголовка столбца
textRegexРегулярное выражение для текста ячейки
minConfidence / maxConfidenceДиапазон уверенности ячейки (0–1)

Валидация ячеек таблиц (cellValidations) #cell-validations

Аналогична cellPostProcessing, но вместо модификации устанавливает cell.isValid.

{ "rules": { "cellValidations": [ { "conditions": { "fieldKey": "invoice_table" }, "cellConditions": { "columnHeaderRegex": "(Сумма|Итого)" }, "rules": [{ "textRegex": "^\\d+[.,]\\d{2}$", "minConfidence": 0.7 }] } ] } }
ℹ️

Порядок обработки табличных полей: удаление заголовочных строк → удаление строк → удаление столбцов → постобработка ячеек → валидация ячеек. Каждый последующий шаг работает с результатами предыдущего.

Определения документов (documentDefinitions) #document-definitions

Определяют, как классифицированные страницы группируются в логические документы.

{ "documentDefinitions": [ { "id": "invoice", "title": "Invoice", "titleClasses": ["Invoice", "Invoice-alt"], "tailClasses": ["Invoice-page"], "isPdfGenerated": true, "steps": [] }, { "id": "passport", "title": "Passport", "titleClasses": ["Passport"], "isPdfGenerated": false } ] }
ПолеОписание
idИдентификатор определения. Нужен, если у определения есть свой конвейер; NotClassified зарезервирован
titleНазвание документа (обязательное)
titleClassesКлассы, обозначающие титульную страницу документа (обязательное, не пустое)
tailClassesКлассы страниц-продолжений. Если не заданы, в документ попадают любые страницы до следующей титульной
isPdfGeneratedГенерировать PDF из страниц документа после обработки. По умолчанию: false
stepsКонвейер документа — шаги, которые выполнятся после объединения страниц (подробнее)

Когда система встречает страницу с классом из titleClasses, она начинает новый документ. Последующие страницы (до следующей титульной) включаются в тот же документ.

⚠️

Класс, который присвоила классификация, должен встречаться среди titleClasses — иначе страницу не с чем связать, документ не соберётся, и страница уйдёт в notClassified.

Конвейер документа #document-pipeline

Шаги в steps определения выполняются во второй фазе — когда страницы уже собраны в документы. Устроены они так же, как шаги основного конвейера (условия, nextSteps, те же типы), но работают с документом целиком:

  • NLP-шагу передаются все страницы документа сразу, а поля в промпт собираются со всех страниц;
  • шаг постобработки применяет правила к объединённым полям документа;
  • условия проверяются по документу: класс берётся с титульной страницы, поля — со всех.
{ "id": "upd", "title": "УПД", "titleClasses": ["УПД_Первая_Страница"], "tailClasses": ["УПД_Продолжение"], "isPdfGenerated": true, "steps": [ { "id": "extract-osnovanie", "routingKey": "nlp-extr-4bo9", "meta": { "component": "NLP", "nlpType": "Extraction" }, "complexCondition": { "negate": true, "hasField": { "fieldKey": "основание", "valueState": "Filled" } }, "recognition": { "overrideFields": ["основание"] }, "nlp": { "addImage": false, "schema": ["основание"], "promptBuilder": { "prependPrompt": "Ниже наименования из таблицы документа.", "appendPrompt": "Верни только JSON вида {\"основание\": \"<значение>\"}.", "tableColumns": [ { "conditions": { "fieldKey": "Таблица" }, "columnIndex": 0, "promptKey": "наименования" } ] }, "temperature": 0.0, "minP": 0.1, "responseLength": 512 } } ] }

Шаг из этого примера ищет основание в таблице документа и только тогда, когда на страницах его найти не удалось.

Шаги конвейера документа отмечаются в результате с префиксом определения: upd::extract-osnovanie. Из-за префикса идентификаторы шагов достаточно держать уникальными внутри своего конвейера.

ℹ️

Если ни у одного определения нет своего конвейера, вторая фаза не выполняется и пакет завершается сразу после основного конвейера.

Неклассифицированные страницы

{ "notClassifiedDefinition": { "isPdfGenerated": true, "steps": [] } }

Страницы, не попавшие ни в один класс, группируются в отдельный объект NotClassified. У него тоже может быть свой конвейер — шаги выполнятся для всех нераспознанных страниц разом, а отмечаются с префиксом NotClassified::.

Интерпретация результатов #results

После обработки пакет содержит структуру processing со следующими данными.

Ход обработки отмечают четыре поля:

ПолеОписание
startedAtОбработка запущена
docsStructureReadyAtОсновной конвейер отработал, страницы объединены в документы — начинается вторая фаза
readyAtОбработка завершена полностью
cancelledAtОбработка отменена

У каждого из них есть парный признак: isStarted, isDocsStructureReady, isReady, isCancelled. Состав документов после docsStructureReadyAt уже не меняется, поэтому идентификаторы документов можно запоминать с этого момента.

Документ (PackageDocumentDto)

ПолеОписание
idИдентификатор документа
titleНазвание документа (из documentDefinitions)
definitionIdИдентификатор определения, по которому собран документ
classConfidenceУверенность классификации
pagesМассив страниц документа
itemsРаспознанные данные документа (объединение постраничных данных)
isPdfCreatedPDF готов к скачиванию
warningsПредупреждения при обработке
stepIdsИдентификаторы шагов, обработавших документ. Шаг конвейера документа записывается как <id определения>::<id шага>

Поле документа (PackageDocumentDtoItem)

ПолеОписание
fieldИмя поля
confidenceУверенность распознавания (0–1)
coordinatesКоординаты области на изображении (minX, minY, maxX, maxY)
textРаспознанный текст (для скалярных полей)
cellsЯчейки таблицы (для табличных полей)
rawTableАвтоматически генерируемое представление: rawTable[столбец][строка] = текст
isValidРезультат валидации: null — не валидировалось, true — пройдена, false — не пройдена

Поле является либо скалярным (text заполнен, cells отсутствует), либо табличным (cells заполнен, text отсутствует, rawTable генерируется автоматически).

Ячейка таблицы (PackageDocumentDtoItemCell)

ПолеОписание
rowИндекс строки
columnИндекс столбца
textТекст ячейки
confidenceУверенность распознавания
isValidРезультат валидации ячейки

Страница документа

Каждая страница (PackageDocumentPageDto) содержит:

ПолеОписание
classКласс, присвоенный при классификации
classConfidenceУверенность классификации
indexInPackageПорядковый номер страницы в пакете
fileИнформация о файле страницы (для скачивания изображения)
rotationAngleУгол наклона изображения, определённый агентом (подробнее)
stepIdsИдентификаторы шагов, обработавших страницу (с префиксом определения — для шагов конвейера документа)
warningsПредупреждения по странице

Постраничные результаты распознавания наружу не отдаются: они нужны серверу, чтобы собрать items документа, и в ответе остаётся только объединённый результат.

ℹ️

Поля документа (items) — это объединение полей всех его страниц, а не содержимое одной страницы. Поле, перечисленное в overrideFields, берётся с последней страницы, где оно непустое, остальные — с первой непустой. Строки таблицы, продолжающейся на нескольких страницах, дописываются друг за другом.

Угол наклона страницы #rotation-angle

Агент Умного OCR определяет наклон изображения и возвращает его вместе с результатом. В пакетной обработке это значение только сохраняется в поле rotationAngle страницы — изображение не поворачивается и координаты полей не пересчитываются.

  • Записывается значение последнего шага, который его вернул, включая 0 — наклон не обнаружен.
  • Поле отсутствует, пока ни один шаг не вернул информацию о трансформации изображения: например, если страницу обрабатывал только NLP.

Ошибки обработки

Массив errors содержит информацию об ошибках:

ПолеОписание
stepIdИдентификатор шага, на котором произошла ошибка
fileIdsИдентификаторы файлов, затронутых ошибкой
errorMsgОписание ошибки

Ключи маршрутизации

Ключ маршрутизации (routingKey) связывает шаг конвейера с конкретным агентом. Формат: {тип}-{случайный_суффикс}.

Тип шагаПрефиксПример
OCR-классификацияocr-clas-ocr-clas-275i
OCR-распознаваниеocr-rec-struct-ocr-rec-struct-71ui
NLP-извлечениеnlp-extr-nlp-extr-4bo9
NLP-классификацияnlp-clas-nlp-clas-a3k2

Ключ маршрутизации создаётся и копируется в Портале при настройке навыка или проекта.

Полный пример конвейера

Конвейер классифицирует документы через SmartOCR, распознаёт счета-фактуры и приводит их поля в порядок шагом постобработки, извлекает данные из договоров через NLP, а в конвейере документа добирает сумму счёта из таблицы, если на страницах она не нашлась:

{ "documentDefinitions": [ { "id": "invoice", "title": "Invoice", "titleClasses": ["Invoice"], "isPdfGenerated": true, "steps": [ { "id": "extract-total", "routingKey": "nlp-extr-g7h8", "meta": { "component": "NLP", "nlpType": "Extraction" }, "complexCondition": { "negate": true, "hasField": { "fieldKey": "TotalAmount", "valueState": "Filled" } }, "recognition": { "overrideFields": ["TotalAmount"] }, "nlp": { "addImage": false, "schema": ["TotalAmount"], "promptBuilder": { "prependPrompt": "Ниже суммы строк из таблицы счёта.", "appendPrompt": "Верни только JSON вида {\"TotalAmount\": \"<итоговая сумма>\"}.", "tableColumns": [ { "conditions": { "fieldKey": "invoice_table" }, "columnHeaderRegex": "Сумма", "promptKey": "суммы" } ] }, "temperature": 0.0, "minP": 0.1, "responseLength": 256 } } ] }, { "id": "contract", "title": "Contract", "titleClasses": ["Contract"], "isPdfGenerated": true } ], "notClassifiedDefinition": { "isPdfGenerated": false }, "steps": [ { "id": "classify", "routingKey": "ocr-clas-a1b2", "meta": { "component": "SmartOCR", "smartOcrType": "Classification" }, "classify": { "fallback": "Unknown", "classesConfidenceThreshold": { "Invoice": 0.8, "Contract": 0.7 } }, "nextSteps": [ { "id": "recognize-invoice", "routingKey": "ocr-rec-struct-c3d4", "meta": { "component": "SmartOCR", "smartOcrType": "Recognition" }, "classCondition": "Invoice", "recognition": { "fieldsConfidenceThreshold": { "INN": 0.5 } }, "nextSteps": [ { "id": "postprocess-invoice", "meta": { "component": "PostProcessing" }, "rules": { "postProcessing": [ { "conditions": { "fieldKey": "INN" }, "modifyRules": { "text": { "regexPattern": "[^0-9]", "replacement": "" } } } ], "validations": [ { "conditions": { "fieldKey": "INN" }, "validations": [{ "textRegex": "^\\d{10}$|^\\d{12}$" }] }, { "conditions": { "fieldKey": "TotalAmount" }, "validations": [ { "textRegex": "^[\\d\\s,.]+$" }, { "minConfidence": 0.4 } ] } ], "removeHeaderRows": [ { "conditions": { "fieldKey": "invoice_table" }, "rules": [{ "rowIndex": 0 }] } ] } } ] }, { "id": "extract-contract", "routingKey": "nlp-extr-e5f6", "meta": { "component": "NLP", "nlpType": "Extraction" }, "classCondition": "Contract", "nlp": { "addImage": true, "schema": ["Номер договора", "Дата", "Контрагент", "Сумма"], "promptBuilder": { "addUnifiedText": false, "addAnswer": false, "addRawAnswer": false }, "temperature": 0.0, "minP": 0.1, "responseLength": 1024 } } ] } ] }

В результате шаги основного конвейера отметятся на страницах как classify, recognize-invoice, postprocess-invoice, а шаг конвейера документа — как invoice::extract-total.

Last updated on