From 37b36047fe95fd8b266e89f76724fc3651e6ba95 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Wed, 7 Oct 2026 00:36:20 +0400 Subject: [PATCH] =?UTF-8?q?docs(openspec):=20=D1=80=D0=B5=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=E2=84=9621=E2=80=9325=20(Task=208:=20valu?= =?UTF-8?q?es=20=D0=BF=D0=BE=20=D0=BA=D0=BE=D0=BB=D0=BE=D0=BD=D0=BA=D0=B0?= =?UTF-8?q?=D0=BC,=20=D0=BB=D1=83=D0=BA=D0=B0=D0=BB=D0=B0=D0=B9=D0=BA?= =?UTF-8?q?=D0=B8=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA=D0=BE=20=D0=B4=D0=BB?= =?UTF-8?q?=D1=8F=20=D0=B4=D0=B0=D1=82,=20=D0=BF=D1=83=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D1=8F=20=D1=80=D0=B5=D0=B4=D0=B0=D0=BA=D1=86=D0=B8=D1=8F,=20fo?= =?UTF-8?q?rm=5Fname)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2 --- openspec/changes/slice-3a-ocr-normalizer/design.md | 7 ++++++- .../specs/image-recognition-gateway/spec.md | 14 +++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/openspec/changes/slice-3a-ocr-normalizer/design.md b/openspec/changes/slice-3a-ocr-normalizer/design.md index 23ffef3..c5739c7 100644 --- a/openspec/changes/slice-3a-ocr-normalizer/design.md +++ b/openspec/changes/slice-3a-ocr-normalizer/design.md @@ -93,7 +93,7 @@ docstring'ами на русском; алгоритмы и регулярки положит владелец, код не меняется. Выбор редакции для документа: по `report_date` шапки → по самой поздней `period_date` колонок → по совпадениям кодов (с предупреждением). Пустая выбранная редакция → предупреждение «редакция fsbu4 не заполнена», имена из OCR, `needs_check`. Код не в выбранной редакции, но есть в другой → - предупреждение, `needs_check`; код нигде → имя из ячейки OCR, `line_code_confidence` из OCR. + предупреждение, `needs_check`; код нигде → имя из ячейки OCR, `line_code_confidence = min(confidence ячейки кода, 99)` (решение №19). - `ocr_lookalikes.yaml`: пары «символ → цифра» и список символов замены. - `statement_labels.yaml`: нормализованные подстроки `label` таблицы → `BALANCE` / `INCOME`. Загрузка один раз при старте (`SCAN_RULES_DIR`, по умолчанию `rules/` сервиса), pydantic-валидация; битый yaml — ошибка @@ -160,3 +160,8 @@ docs: bounded-PR (`03-architecture.md`, OQ-137, §11.1 п. 6 и 9, новые OQ | 18 | 2026-10-07 | R-1. Тест плана `test_aep3_like_rows_without_header` требовал `5 in layout.value_idxs`; ожидание заменено на `layout.value_idxs == (2, 4)`, алгоритм `infer_layout` не менялся | столбец 5 содержит только мусор («:», «+ :») и пустые ячейки: по дословному `_infer_layout` суммы (0) < мусор (2) — столбец не значение; ошибка плана, а не переноса | если реальный `aep_3.json` даст иной набор колонок — выяснится в golden (Task 10) | | 19 | 2026-10-07 | R-2. `line_code_confidence = confidence ячейки кода` только если очищенный текст равен сырому И код есть в выбранной редакции; иначе `min(confidence, 99)`; `line_name_confidence` словарного имени равна `line_code_confidence`, имени из OCR — `confidence` ячейки наименования | требование «Нормализация ответа ИС» спеки; код плана капил уверенность только при мусоре в тексте кода, тест плана ожидал 99 для кода вне справочника | код вне справочника чаще даёт `needs_check` — это то, что нужно аналитику | | 20 | 2026-10-07 | О-3 (наблюдение, не меняет поведение): прочерк «—»/«–» при `confidence = 100` даёт `is_recognized = false` (дословный `parse_amount` вырезает тире → `verified = false`), дефис «-» — `true` | дословный перенос и формулировка спеки «разбор не отбрасывал символов» | лишний `needs_check` на формах с длинным тире; правило уточняется в §11.1 после проверки на стенде | +| 21 | 2026-10-07 | R-3. `values` строки строятся по позиции колонки документа; при ином числе колонок у раскладки строки после повторной шапки — недостающие как «ячейки нет», лишние отбрасываются, одно предупреждение на таблицу, `needs_check` | спека: «values — по одному на колонку»; код плана давал значения по индексам раскладки строки и мог породить `column_index`-сироту, на которой упадёт уникальность `(meta_id, column_index)` в 3b | значения «лишних» колонок повторной шапки теряются в документе (остаются в `raw/`) | +| 22 | 2026-10-07 | R-4. `fix_lookalikes` — только для дат (дата отчёта, подписи колонок); коды — `clean_value`, суммы — `parse_amount`, ИНН — `digits_only`. Формулировка спеки про коды и суммы исправлена | §11.1 п. 1 и проверенный на стенде старый код; замена `S→5` и т. п. в суммах не проверена на стенде | код с латинской `O` вместо нуля отбрасывается как не-целое (остаётся в `raw/`) | +| 23 | 2026-10-07 | R-5. Предупреждения «код N из редакции X» не выдаются, если выбранная редакция пуста | до заполнения `fsbu4` каждая строка 2025 года давала бы шумное предупреждение (на `aep_6` 39+25), информативно одно «редакция не заполнена» | при пустой редакции аналитик не видит, что код из другой редакции | +| 24 | 2026-10-07 | `header.form_name` не передаётся, если у таблицы нет `label` | контракт запрещает `null` в `form_name` (`OptionalNotNull`) | поле отсутствует вместо `null` | +| 25 | 2026-10-07 | R-6. Таблица без колонок значений (`columns = []`) → предупреждение «нет колонок значений», `needs_check` | иначе документ без колонок мог оказаться `done` | — | diff --git a/openspec/changes/slice-3a-ocr-normalizer/specs/image-recognition-gateway/spec.md b/openspec/changes/slice-3a-ocr-normalizer/specs/image-recognition-gateway/spec.md index d4590df..fb4bf22 100644 --- a/openspec/changes/slice-3a-ocr-normalizer/specs/image-recognition-gateway/spec.md +++ b/openspec/changes/slice-3a-ocr-normalizer/specs/image-recognition-gateway/spec.md @@ -168,7 +168,9 @@ нет в справочниках — текст ячейки наименования. `line_code_confidence` — `confidence` ячейки кода, если очищенный текст равен сырому и код есть в справочнике, иначе `min(confidence, 99)`; `line_name_confidence` — то же для словарного имени, иначе `confidence` ячейки наименования. -- `values` — по одному на колонку: `amount` — `parse_amount` (запятая → точка, скобки → минус, пробелы и прочие символы +- `values` — по одному на колонку документа (по позиции; если раскладка строки после повторной шапки даёт иное число колонок — + недостающие значения как «ячейки нет», лишние отбрасываются, одно предупреждение на таблицу «повторная шапка изменила + число колонок значений», решение №21; таблица без колонок значений — предупреждение «нет колонок значений», №25): `amount` — `parse_amount` (запятая → точка, скобки → минус, пробелы и прочие символы отбрасываются, несколько точек — разделители тысяч; прочерк/пусто → `null`); `string_value` — текст ячейки как распознан (`\n`/`\t` → пробел, края обрезаны) **только если `amount = null`** и текст не прочерк (DBML: «как распознано, если не число»), иначе `null`; `confidence` — ячейки; `is_recognized = confidence == 100 и разбор не отбрасывал символов`; @@ -179,8 +181,10 @@ `report_date_text` — сырой текст `date.form_name`, `report_date` — `parse_date` после `fix_lookalikes` (`null`, если текст не дата); `okud`, `okpo`, `okopf`, `okfs`, `address` — `null` (ИС их не отдаёт). - `checks` MUST NOT присутствовать. -Двойники букв и цифр (`fix_lookalikes`) SHALL применяться к тексту кодов, сумм, дат и ИНН только вплотную к цифрам; -`|` SHALL считаться разделителем клеток, если первая строка значения состоит из цифр, пробелов и знаков пунктуации. +Двойники букв и цифр (`fix_lookalikes`) SHALL применяться к тексту дат (дата отчёта и подписи колонок) только вплотную +к цифрам; `|` SHALL считаться разделителем клеток, если первая строка значения состоит из цифр, пробелов и знаков +пунктуации. Коды очищаются `clean_value`, суммы — `parse_amount`, ИНН — выделением цифр (как в проверенном на стенде +старом ядре, §11.1 п. 1; решение №22 `design.md`). #### Scenario: Чистый скан с шапкой (`aep_6.json`) - **WHEN** нормализуется `aep_6.json` (две таблицы: «Бухгалтерский баланс» 122 строки со строкой-шапкой «Наименование показателя / Код / На 30 июня 2025 г / На 31 декабря 2024 г. / На 31 декабря 2023 г.» и «Отчет о финансовых результатах» 49 строк) @@ -217,8 +221,8 @@ `fsbu4` (с 2025-01-01) присутствует и может быть пустой. Редакция документа SHALL выбираться по `header.report_date`, при его отсутствии — по самой поздней `period_date` колонок, при отсутствии обеих — по числу совпадений кодов с предупреждением «редакция определена по кодам». Пустая выбранная редакция → предупреждение «редакция не -заполнена», имена строк — из OCR. Код, отсутствующий в выбранной редакции, но присутствующий в другой → предупреждение -«код <код> из редакции ». `ocr_lookalikes.yaml` SHALL задавать таблицу двойников; `statement_labels.yaml` — +заполнена», имена строк — из OCR. Код, отсутствующий в выбранной непустой редакции, но присутствующий в другой → предупреждение +«код <код> из редакции » (для пустой редакции не выдаётся — достаточно «редакция не заполнена»; решение №23). `ocr_lookalikes.yaml` SHALL задавать таблицу двойников; `statement_labels.yaml` — нормализованные подстроки `label` → `BALANCE`/`INCOME`. Коды справочника SHALL быть строками из 4–5 цифр без дублей. #### Scenario: Редакция по дате отчёта