Назначение файла: Главное руководство, свод архитектурных правил, инвариантов и протоколов разработки для AI-ассистентов в проекте распознавания судебных и исполнительных документов ScanReader. Базовый стек: Python 3.9+, Pydantic v2, OpenAI API / Ollama / vLLM (Qwen2.5-VL), Pillow, PyMuPDF, openpyxl, pandas, pytest. Основной пакет:
src/scan_reader/(структура PEP 517/621, единый источник правды). Контур безопасности: Закрытый корпоративный периметр. Локальный кэш без утечек памяти и секретов.
┌─────────────────────────────────────────┐
│ LegalDocPlatformFacade (facade.py) │
│ Синхронный и пакетный API обработки │
└────────────────────┬────────────────────┘
│
┌───────────────────┬───────────────┴───────────────────┬───────────────────┐
▼ ▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ file_proc │ │ type_registry│ │ Zero-Trust │ │ Exporter │
│ (PDF, Image, │ │ (Плагины │ │ Auditor │ │ (Excel │
│ DOCX, RTF) │ │ doc_types/) │ │ & Guardrails │ │ openpyxl) │
└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
│ │ │ │
└───────────────────┴───────────────┬───────────────────┴───────────────────┘
▼
┌─────────────────────────────────────────┐
│ src/scan_reader/core/ │
│ utils, io_utils, token_tracker, │
│ rate_limiter, cache, loader, json_exp │
└─────────────────────────────────────────┘
- Запрещено вносить любые изменения в код, схемы или плагины без предварительного составления детального плана и его утверждения.
- Запрещен паттерн
except: pass. - Все ошибки логируются через
get_logger()и изолируются в возвращаемых структурах (status: FAILED,errors: [...]), не прерывая пакетную обработку остальных документов.
- Никогда не доверять выходу VLM вслепую.
- Все ИНН (10 и 12 знаков), СНИЛС (11 знаков), ОГРН/ОГРНИП, БИК и расчетные счета обязаны верифицироваться алгоритмическими контрольными суммами.
- Суммы сверяются по правилам, объявленным в
verification.jsonплагина. Сверка обязана сравнивать два и более числа; наличие одного числа не является проверкой. - Пределы взыскания: 50 % / 70 % по ст. 99 229-ФЗ и 20 % по ст. 138 ТК РФ. Для вреда здоровью лимит 50 % не применяется вовсе (ФЗ № 314-ФЗ от 06.03.2019), и отдельного процентного потолка для него нет — нельзя утверждать обратное.
- Даты процессуальных актов проверяются на хронологическую непротиворечивость.
- Кросс-модальный гейт обязан указывать происхождение эталона (
gate_source,gate_independent). Эталон от той же модели, что и извлечение, не является независимой сверкой и должен помечаться предупреждением.
- Ядро системы не содержит знаний о конкретных типах документов. Ни
verifier/, ниcore/не упоминают ни одного имени поля или идентификатора плагина — это проверяется тестом-сканером по исходникам ядра. - Каждый тип документа оформляется как самодостаточный плагин в
doc_types/<type_id>/с 7 обязательными файлами:manifest.json,schema.py,prompt.md,classifier_rules.json,flat_columns.json,benchmark.json,autonomous.json. - Восьмой файл —
verification.json: декларация проверок (стороны и их идентификаторы, банковский блок, правила сверки денег, лимит удержания, правила хронологии, поля гейта). Файл необязателен: плагин без него проходит только общие проверки, и добавление нового типа сводится к созданию папки. - Пути в
verification.jsonдопускают альтернативы через|(court.act_date|act_date) — один и тот же реквизит у разных типов лежит в разных местах.
- Все ответы от VLM/LLM обязаны проходить через
_safe_parse_jsonизcore.utilsс обязательной очисткой рассуждений<think>...</think>(Qwen/DeepSeek) и извлечением JSON из markdown code blocks.
- Запись артефактов и отчетов производится атомарно через
write_atomic(временный файл + fsync + замена) во избежание повреждения файлов при сбоях питания/процесса. - Логирование и CLI сообщения автоматически фильтруют токены, пароли и паспортные данные через
mask_secret(). - Маскирование обязано покрывать
record.args, а не толькоrecord.msg: вызовlogger.info("токен %s", secret)иначе утекает. Маскируются только строки — приведение чисел кstrломает спецификаторы%d. - Все модули пакета обязаны использовать
get_logger(), а не сыройlogging.getLogger: последний обходит фильтр маскирования.
- СХЕМА (
core/fields.py+schema.pyплагина) отвергает то, что не может быть значением поля: длину, диапазон, допустимый набор, контрольную сумму идентификатора. Обязательныge=0на денежных полях,patternна ИНН/КПП/БИК,model_validator(mode="after")для сверки идентификаторов. - АУДИТОР (
verifier/) отвергает то, что семантически невозможно: арифметику, хронологию, статутные лимиты, неподтверждённые реквизиты. - Правило наименьшей строгости: нечитаемое значение не должно молча превращаться в пустое.
parse_russian_currency("-150000")обязан вернуть-150000.0, чтобы ограничениеge=0дало понятную ошибку, а не «поле не заполнено». - Плагины используют общие примеси
ValidatedPartyMixinиValidatedBankMixin; переобъявлениеinn/kppв подклассе конфликтует с ними и запрещено. - Корневая модель плагина обязана объявлять
__test__ = False. - Модели Pydantic, содержащие слово
Test, обязаны иметь атрибут__test__ = False.
- При экспорте артефактов имена файлов очищаются через
sanitize_filename(). - Консольный вывод настраивается через
setup_console_utf8()(единственная реализация;io_utils.configure_streams— её псевдоним).
- Статус верификации не является доказательством правильности.
zero_trust_verifiedозначает «все применимые проверки пройдены», а не «100 %». Формулировки «100 % подтверждено» и «готов к безоговорочному автоимпорту» в документации запрещены. - Поле, отсутствующее с обеих сторон при сравнении с эталоном, исключается из знаменателя, а не получает 100 баллов. Поле, выдуманное моделью и отсутствующее в эталоне, штрафуется и ломает
validation_passed. - Тип поля в
benchmark.jsonобязан быть из известного словаря (exact,numeric/number,text/string,inn,date); неизвестный тип отвергается при загрузке плагина. Сравнение сумм строкой (а не числом) — недопустимо. - Любая метрика сопровождается
measurement_caveats: каким режимом измерена, сколько документов сверено с эталоном, на скольких полях держится цифра. Сводка запуска обязана передавать эти сведения дальше, а не отбрасывать их. - Эталон сам подлежит проверке: невалидный ИНН/СНИЛС/БИК в
data/ground_truth/делает измерение бессмысленным, поскольку система штрафуется за несуществующую ошибку.
Запуск полного тестового сьюта:
py -3 -m pytestЗамер тестового покрытия пакета (порог — 70 %):
py -3 -m pytest --cov=scan_reader --cov-report=term-missingПроверка линтером и типами:
py -3 -m ruff check src tests examples
py -3 -m mypy