218 lines
11 KiB
Markdown
218 lines
11 KiB
Markdown
# Задание агенту: нормализация документов ЦБД Минюста КР
|
||
|
||
## Цель
|
||
|
||
Реализовать первую рабочую версию воспроизводимого нормализатора локального
|
||
архива `data/minjust-cbd`. Нормализованные данные должны быть пригодны для
|
||
последующей загрузки в OpenSearch, backend API и RAG, но подключение этих
|
||
сервисов в текущую задачу не входит.
|
||
|
||
## Текущее состояние
|
||
|
||
- загрузчик находится в `backend/ingestion/minjust_cbd.py`;
|
||
- сырой архив хранится в `data/minjust-cbd` и исключён из Git;
|
||
- в манифесте 209 811 успешно загруженных документов;
|
||
- 13 кодов остаются в таблице `errors` и отсутствуют в таблице `documents`;
|
||
- документ содержит `metadata.json` и каталог `editions`;
|
||
- редакция содержит `metadata.json`, `ru.html` и/или `ky.html`, иногда
|
||
изображения;
|
||
- HTML создан Microsoft Word, может содержать некорректный
|
||
`<meta charset=unicode>`, служебные стили и неполную разметку;
|
||
- файлы архива записаны загрузчиком в UTF-8;
|
||
- часть документов одноязычная, а часть не содержит HTML-текста.
|
||
|
||
## Обязательные ограничения
|
||
|
||
1. Не изменять и не перезаписывать `data/minjust-cbd`.
|
||
2. Не запускать полный проход по архиву во время автоматических тестов.
|
||
3. Не подключать PostgreSQL, OpenSearch, OCR, embeddings, машинный перевод и
|
||
сетевые API.
|
||
4. Сначала использовать стандартную библиотеку. Новая зависимость допустима
|
||
только если на реальных образцах доказано, что стандартный HTML-парсер не
|
||
обеспечивает корректность или безопасность.
|
||
5. Все записи выполнять атомарно.
|
||
6. Ошибка одного документа не должна останавливать длительный прогон.
|
||
7. Повторный запуск должен пропускать неизменившиеся документы.
|
||
8. Не изменять пользовательские файлы `logs/`, исходный DOCX и несвязанные
|
||
незакоммиченные изменения.
|
||
|
||
## Размещение
|
||
|
||
Использовать существующую backend-структуру:
|
||
|
||
```text
|
||
backend/
|
||
normalization/
|
||
minjust_cbd.py
|
||
```
|
||
|
||
Результат по умолчанию:
|
||
|
||
```text
|
||
data/minjust-normalized/
|
||
manifest.sqlite3
|
||
documents/<document_code>/document.json
|
||
documents/<document_code>/editions/<edition_code>/edition.json
|
||
documents/<document_code>/editions/<edition_code>/<lang>/content.html
|
||
documents/<document_code>/editions/<edition_code>/<lang>/content.txt
|
||
documents/<document_code>/editions/<edition_code>/<lang>/fragments.json
|
||
```
|
||
|
||
Каталог уже покрывается правилом игнорирования `data/`.
|
||
|
||
## Канонические данные
|
||
|
||
### `document.json`
|
||
|
||
Сохранить как минимум:
|
||
|
||
- `schema_version`;
|
||
- `source_code`;
|
||
- двуязычные `class`, `type`, `title`, `name`, `status`;
|
||
- номера и даты без юридически неподтверждённых выводов;
|
||
- флаги публичности;
|
||
- органы, публикации, ключевые слова и классификаторы с сохранением дерева;
|
||
- пути листьев иерархий для будущих фильтров;
|
||
- ссылки из `References` без выдумывания связей;
|
||
- `available_languages`;
|
||
- список редакций;
|
||
- путь к источнику и SHA-256 исходных файлов;
|
||
- версию нормализатора и время обработки.
|
||
|
||
Пустые строки привести к `null`, но не переводить значения и не заменять
|
||
официальные формулировки собственными.
|
||
|
||
### `edition.json`
|
||
|
||
Сохранить:
|
||
|
||
- исходный код редакции;
|
||
- двуязычное название;
|
||
- исходный тип;
|
||
- доступные языки;
|
||
- изображения без бинарных данных;
|
||
- контрольные суммы источников;
|
||
- признаки качества.
|
||
|
||
Не считать дату из `Name` датой вступления редакции в силу и не назначать
|
||
актуальную редакцию без подтверждённого правила источника.
|
||
|
||
### Языковой вариант
|
||
|
||
Для каждого имеющегося `ru.html` или `ky.html` сформировать:
|
||
|
||
- `content.html` — безопасный HTML для frontend;
|
||
- `content.txt` — извлечённый текст с сохранением смысловых переносов;
|
||
- `fragments.json` — упорядоченные адресуемые блоки.
|
||
|
||
Сырой HTML всегда читать как UTF-8, не доверяя его meta charset.
|
||
|
||
## Очистка HTML
|
||
|
||
- удалить `script`, `style`, `meta`, `link`, комментарии и служебные элементы;
|
||
- удалить обработчики событий, inline-стили и опасные URL;
|
||
- разрешить минимальный набор структурных тегов: заголовки, абзацы, `pre`,
|
||
списки, таблицы, безопасные ссылки, изображения и базовое текстовое
|
||
выделение;
|
||
- нормализовать Unicode в NFC;
|
||
- преобразовать неразрывные пробелы и избыточные пробелы только в
|
||
`content.txt`, не искажая отображаемый юридический текст;
|
||
- не загружать внешние ресурсы;
|
||
- относительные изображения связывать только с файлами внутри редакции;
|
||
- неизвестную или сломанную разметку сохранять как текст, а не терять молча.
|
||
|
||
## Фрагменты
|
||
|
||
Минимальная версия должна создавать фрагмент для каждого содержательного
|
||
блочного элемента. Распознавание статей и пунктов допускается только простыми
|
||
проверяемыми правилами RU/KY; обычный абзац является fallback.
|
||
|
||
Каждый фрагмент содержит:
|
||
|
||
- стабильный `id`;
|
||
- `document_code`, `edition_code`, `language`;
|
||
- порядковую позицию;
|
||
- тип блока;
|
||
- чистый текст;
|
||
- SHA-256 текста.
|
||
|
||
Идентификатор должен быть детерминированным и включать документ, редакцию,
|
||
язык и позицию. Не добавлять сложное сопоставление фрагментов между
|
||
редакциями — это отдельная будущая задача.
|
||
|
||
## Манифест и возобновление
|
||
|
||
SQLite-манифест должен хранить:
|
||
|
||
- код документа;
|
||
- SHA-256 набора исходных файлов;
|
||
- версию схемы и нормализатора;
|
||
- время успешной обработки;
|
||
- состояние и текст последней ошибки.
|
||
|
||
Если checksum и версия нормализатора не изменились, документ пропускается.
|
||
После успешной повторной обработки ошибка удаляется. Добавить `--limit` и
|
||
понятный терминальный прогресс, пригодный для долгого запуска.
|
||
|
||
## CLI и импорт из будущего backend
|
||
|
||
CLI запускается из корня репозитория:
|
||
|
||
```bash
|
||
python3 backend/normalization/minjust_cbd.py
|
||
```
|
||
|
||
Предусмотреть параметры:
|
||
|
||
- `--input`;
|
||
- `--output`;
|
||
- `--limit`;
|
||
- `--refresh`;
|
||
- `--log-level`.
|
||
|
||
Основную функцию можно импортировать без запуска CLI. Не создавать
|
||
планировщик, очередь задач или framework-интеграцию.
|
||
|
||
## Проверки
|
||
|
||
Добавить один компактный тестовый модуль, который проверяет:
|
||
|
||
1. русскую редакцию;
|
||
2. кыргызскую редакцию;
|
||
3. двуязычную редакцию;
|
||
4. Word HTML с опасным `script`, inline-стилем и `javascript:` URL;
|
||
5. документ без HTML;
|
||
6. повторный запуск и пропуск неизменившегося документа;
|
||
7. продолжение после ошибки одного документа;
|
||
8. детерминированные fragment ID и checksums.
|
||
|
||
Провести пилотный read-only запуск на небольшой реальной выборке через
|
||
`--limit`, не нормализовать весь архив в рамках разработки.
|
||
|
||
## Документация и версия
|
||
|
||
- описать запуск, структуру результата и ограничения в `backend/README.md`;
|
||
- отметить реализацию в `docs/operations/project-status.md`;
|
||
- это новая обратно совместимая backend-функция: увеличить minor-версию
|
||
backend по SemVer;
|
||
- обновить все отображаемые backend-версии и footer, не меняя версию
|
||
Telegram-бота;
|
||
- frontend по-прежнему помечать как не созданный.
|
||
|
||
## Критерии приёмки
|
||
|
||
- сырой архив не изменён;
|
||
- тесты проходят;
|
||
- `git diff --check` проходит;
|
||
- пилотный запуск завершается без остановки на отдельных ошибках;
|
||
- повторный пилотный запуск пропускает неизменившиеся документы;
|
||
- unsafe HTML не попадает в `content.html`;
|
||
- каждый фрагмент прослеживается до документа, редакции, языка и исходного
|
||
файла;
|
||
- отсутствуют молча потерянные HTML или ошибки;
|
||
- реализация не содержит PostgreSQL/OpenSearch/RAG-кода «на будущее».
|
||
|
||
---
|
||
|
||
Акылдаш · Telegram-бот v0.2.2 · Backend v0.7.1 · Frontend — не создан
|