IFRS Pipeline — архитектура и модели

Пайплайн извлечения показателей МСФО-отчётности из PDF. Источник: архив _код_пайплайна_260825.7z (138 записей), разбор 2026-08-26.
103 файла кода
27 тикеров за батч
61 показатель (БС/ОПУ/ОДДС)
66 промптов agent_*.py
14 моделей
1 LLM (fallback)

Что это

Пайплайн читает PDF МСФО-отчётности, детектирует и извлекает значения показателей (баланс, P&L, поток) и выгружает результат в Excel. Ключевая идея — LLM здесь fallback, а не основной движок: почти всё делает структурный пайплайн + эмбеддинговый матчер.
Поток верхнего уровня:
PDF → подготовка страниц (OCR/Docling/структура) → ifrs-embed-v13 матчер + IFRS-граф → детекция метрик (косинус, без LLM) → пер-показательное извлечение (fast-path → DeepSeek-fallback) → постобработка → extracted.jsonticker.xlsx

Схема пайплайна

Сверху вниз — порядок исполнения. Цветные чипы — модели на каждом шаге.
Легенда: OCR (CPU) Docling (GPU) Структура / layout Эмбеддинг-матчер LLM (API) Языковая модель (n-грамм)
0

Запуск батча — batch_run.py

4 воркера · stagger-старт
  1. 27 тикеров, 4 параллельных subprocess-воркера, старты разнесены по времени (анти-Blackwell-deadlock)
  2. Двухфазный режим: фаза 1 — GPU-эмбеддинги «безопасных» показателей одним процессом → prefill phase1.json; фаза 2 — оставшийся LLM параллельно, эмбеддинги на CPU
  3. Результат в output_<ticker>/, повторные прогоны пропускаются (флаг --force)
1/4

Подготовка документа — prepare_document

CPU + GPU · постранично
  1. Роутинг страниц: какие страницы скан, с битым или неполным текстовым слоем
  2. OCR (только маршрутизированные страницы) — pdf_text_restore.py_ocr.pdf + words.json
    Tesseract (бандл) PP-LCNet_x1_0_doc_ori PP-OCRv5_mobile_det eslav_PP-OCRv5_mobile_rec cyrillic_PP-OCRv5_mobile_rec db_resnet50.onnx (docTR) char-LM rescore/arbiter
  3. Docling (GPU): layout + структура таблиц, OCR='precomputed' → docling-sidecar (ячейки row/col/rowspan/colspan)
    docling-layout-heron TableFormer v1/v2
  4. PP-StructureV3 (subprocess, venv_paddle) → _struct.json: ячейки + grid (row/col) для tabnorm
    PP-StructureV3
  5. Layout-заголовкиpdf_layout_headings.py → границы нот (note_index)
    LayoutDetection (RT-DETR)
  6. Сборка: страницы, типы страниц (правила, без LLM), ноты, аннотации
2/4

Эмбеддинговый матчер + IFRS-граф

GPU при фаза-1 · CPU при фаза-2
  1. Сборка и кодирование узлов IFRS-графа (агрегаты, компоненты, алиасы) — тем же матчером
  2. Матчер = гибрид двух эмбеддеров
    ifrs-embed-v13-clean-final (XLM-RoBERTa, 768d) BAAI/bge-m3 (гибрид α=0.15)
3/4

Детекция метрик — БЕЗ LLM

эмбеддинги · косинус
  1. detect_metrics_in_pdf: косинус-совпадение строк таблиц с целевыми метриками
  2. Связка заголовок → таблица (геометрия + эмбеддинги)
  3. tabnorm-стор: нормализация НЕстандартных таблиц (rollforward/матрицы) → единый пул кандидатов с контекстом «секция + метка-строки + метка-колонки»
4/4

Извлечение показателя — 61 агент × БС/ОПУ/ОДДС

LLM — только fallback
  1. Пре-шаги: выбор страниц (по типу блока), периода, единицы — детерминированно, LLM лишь при неудаче
  2. FAST-PATH (без LLM): структурные finder'ы + эмбеддинг-матчинг + guards (секция, inverse-total, label, scale)
  3. Если fast-path вернул null → LLM: промпт агента из prompts/individual/agent_*.pycall_deepseek
    deepseek/deepseek-v4-flash (OpenRouter → novita) majority-vote k=3 (приоритетные страницы) null-audit critical-review
  4. IFRS-граф: отсутствующие агрегаты считаются из компонентов (COMB=Σкомпонент)
  5. Постобработка: знаки (strict/role/force-negative), magnitude-проверки, периоды, кросс-категорийные алиасы, ROU/обесценение-резолверы, strip_duplicate_values, кросс-отчётная сверка, _reconcile_indicators

Выход

per ticker
  1. output_<ticker>/extracted.jsonextracted_to_excel.py<ticker>.xlsx
  2. Агрегация по базе: aggregate_deliverable.py / extract_deliverable_all.py

Модели проекта

Полный реестр: где исполняется, за что отвечает, в каком файле задействована.
МодельСтадияРольИсполнениеФайл
Tesseract (бандл)1 — OCR Надёжные bbox слов, однобуквенные слова, латиница и знаки препинания CPUpdf_text_restore.py
PP-LCNet_x1_0_doc_ori1 — OCR Определение поворота страницы CPUpdf_text_restore.py
PP-OCRv5_mobile_det1 — OCR Детект строк текста CPUpdf_text_restore.py
eslav_PP-OCRv5_mobile_rec1 — OCR Распознавание слов, содержащих цифры (специалист) CPUpdf_text_restore.py
cyrillic_PP-OCRv5_mobile_rec1 — OCR Распознавание кириллических слов CPUpdf_text_restore.py
db_resnet50.onnx (порт docTR)1 — OCR Детект слов (onnxruntime) CPUdoctr_det.py
char-LM (corpus_charlm.pkl.gz + corpus_lexicon.json)1 — OCR Rescore/арбитр: правка OCR-ошибок n-граммной языковой моделью по корпусу отчётности CPUrec_rescore / rec_arbiter
docling-layout-heron (HF)1 — Docling Разметка страницы (блоки) GPUpdf_docling_extract.py
TableFormer v1/v2 (HF)1 — Docling Структура таблиц: ячейки, row/col, rowspan/colspan. Единственный CUDA-потребитель GPUpdf_docling_extract.py
PP-StructureV31 — структура Ячейки таблиц с row/col-grid → sidecar для tabnorm-стора CPUpdf_structure_extract.py
LayoutDetection (PaddleOCR 3.x, на базе RT-DETR)1 — layout Заголовки → границы нот (note_index), hi-res рендер @880px CPUpdf_layout_headings.py
ifrs-embed-v13-clean-final (XLM-RoBERTa, 768d, fine-tuned)2–3 — матчер Косинус-матчинг строк ↔ показатели; кодирование IFRS-графа GPU / CPUextract_pdf_pipeline.py
BAAI/bge-m32 — матчер Гибрид с ifrs-embed (α=0.15) GPU / CPUextract_pdf_pipeline.py
deepseek/deepseek-v4-flash (OpenRouter, закреплён на novita)4 — LLM Пер-показательное извлечение: 61 агент, majority-vote k=3, null-audit, critical-review. qwen3-235b-a22b-2507 отклонён 2026-06-04 APIcall_deepseek

Ключевые принципы

Почему архитектура такая — главные решения.
  1. LLM — fallback, а не движок. Сначала fast-path (структурные finder'ы + guards без LLM); DeepSeek вызывается только когда fast-path вернул null. У каждого из 61 показателя — свой агент со своим промптом (prompts/individual/agent_*.py).
  2. Эмбеддинги — «мозг» без LLM: и детекция метрик (стадия 3), и косинус-поиск строк в таблицах, и кодирование IFRS-графа — через ifrs-embed-v13 + bge-m3.
  3. Стор как фундамент: все НЕстандартные таблицы (rollforward, матрицы) нормализуются в единый queryable-стор; извлечение показателя = запрос t.value(section,row,column,period), а не ручные finder'ы.
  4. GPU — только docling (TableFormer). Весь OCR/layout — CPU (onnxruntime), поэтому 4 параллельных воркера не конфликтуют за GPU; stagger-старты предотвращают Blackwell-deadlock.
  5. Батч двухфазный: фаза 1 — GPU-эмбеддинги одним процессом (prefill), фаза 2 — LLM параллельно на CPU-эмбеддингах.
  6. Знак и агрегаты — правила, не LLM: знак строки из роли/тега (strict/role/force-negative), агрегаты из графа (COMB=Σкомпонент), финальная сверка тождеств (_reconcile_indicators).

Батч-режим и окружение

Как это крутится в проде.
Запуск и фазы
batch_run.py гоняет 27 тикеров по 4 параллельных subprocess-воркера. Фаза 1: GPU-эмбеддинги «безопасных» показателей одним процессом → phase1.json (prefill). Фаза 2: оставшийся LLM параллельно, эмбеддинги на CPU. Логи и результат — в output_<ticker>/; повторные прогоны пропускаются, --force перезапускает; --no-llm = полный пайплайн без per-показательных LLM-вызовов.
OCR-окружение (venv_paddle)
PaddlePaddle GPU 3.3.1 только из индекса cu129 — ветка CUDA важнее версии: cu126 на Blackwell (RTX 5070 Ti, CC 12.0) даёт 0 распознанных слов. Установка в два шага: pip install paddlepaddle-gpu==3.3.1 --index-url https://www.paddlepaddle.org.cn/packages/stable/cu129/, затем pip install --no-deps -r requirements_venv_paddle.txt. Ограничение numpy<2 больше не нужно.
Офлайн-бандлы
Docling качает layout (docling-layout-heron) + TableFormer с HuggingFace при первом запуске — на закрытом сервере банка нужен забандленный HF-кеш hf_cache/hub/… и флаг офлайн. Веса eslav/cyrillic rec и HF-кеш docling в архиве отсутствуют — есть только doctr_bundled/db_resnet50.onnx, corpus-LM и ifrs-embed-v13-clean-finalmodel_output/).
Известные тонкости
Детект тонких нот-заголовков зависит от горизонтальной ширины растра (≥800px), а не длинной стороны — общий робастный таргет 880px, hi-res рендер @2.0 + Lanczos. Ограничение batch_size кропов при распознавании: 1 шт. — 3.2 мс/строку, 64 шт. — 18.2 (пакет тянется за самым широким кропом).

Карта ключевых файлов

Навигация по коду из архива _код_пайплайна_260825/код/.
extract_pdf_pipeline.pyосновной пайплайн: prepare_document, детекция, пер-показательное извлечение, постобработка (1.2 МБ)ядро
batch_run.pyмассовый прогон 27 тикеров, двухфазный GPU/LLM-режим, stagger-стартызапуск
pdf_text_restore.pyOCR-восстановление: роутинг страниц, Tesseract + PaddleOCR, char-LM арбитрOCR
pdf_docling_extract.pydocling: layout + структура таблиц, изоляция по страницам/чанкам (RAM-плато)структура
pdf_layout_headings.pyLayoutDetection → границы нот (note_index)layout
pdf_text_restore/pdf_ocr_restore/doctr_det, rec_arbiter, rec_rescore, pdf_structure_extract (PP-StructureV3 sidecar)OCR-модули
agents.pyтипы страниц (expected_page_type), постобработка strip_cross_category_contamination / strip_duplicate_valuesправила
prompts/individual/agent_*.py66 пер-показательных промптов для DeepSeekLLM
tabnorm/store.build_store — нормализация НЕстандартных таблиц; extract — слой извлечения поверх сторастор
model_output/ifrs-embed-v13-clean-final/fine-tuned эмбеддер (XLM-RoBERTa, 768d)матчер
extracted_to_excel.py / aggregate_deliverable.pyвыход: Excel по тикеру, агрегация по базевыход