Files
akyldash/backend/README.md

13 KiB
Raw Blame History

Backend Акылдаш

Версия: 0.8.0

Первая backend-область проекта — загрузка правовых документов из официального Open Data API ЦБД Минюста Кыргызской Республики. Код расположен в ingestion/minjust_cbd.py: его можно запускать как CLI сейчас и импортировать из будущего планировщика backend без запуска отдельного процесса.

Пробная загрузка

Из корня репозитория:

python3 backend/ingestion/minjust_cbd.py --limit 10

В интерактивном терминале отображаются процент, количество документов, ошибки, скорость и примерное оставшееся время.

Полная загрузка выполняется без --limit. По умолчанию архив сохраняется в data/minjust-cbd, который исключён из Git. Повторный запуск пропускает уже загруженные документы; --refresh принудительно проверяет их заново. Если временный идентификатор списка API истечёт или запрос страницы исчерпает повторы во время многодневной загрузки, скрипт пересоздаст список на текущей странице и продолжит автоматически.

В версии 0.1.0 обновление существующих документов выполняется полной проверкой через --refresh. Инкрементальную проверку по lastmod из sitemap следует добавить вместе с backend-планировщиком, когда будет определена частота запуска.

Защита API

Запросы выполняются последовательно, по умолчанию не чаще одного в секунду. Timeout одного ответа — 60 секунд, число попыток — 5, задержка между повторами растёт экспоненциально. При HTTP 429 учитывается заголовок Retry-After. Лимит действует на один процесс, поэтому одновременно следует запускать только один экземпляр загрузчика.

Более осторожный режим:

python3 backend/ingestion/minjust_cbd.py --requests-per-second 0.5

Вызов из будущего backend

from ingestion.minjust_cbd import sync_archive

result = sync_archive(output_path)

Планировщик, очередь задач и PostgreSQL пока не добавлены: модуль не зависит от выбора будущего backend-фреймворка.

Нормализация архива

Нормализатор читает исходный архив без изменений и создаёт отдельный набор данных для будущих поиска, API и RAG:

python3 backend/normalization/minjust_cbd.py --limit 10
python3 backend/normalization/minjust_cbd.py

По умолчанию источник читается из data/minjust-cbd, а результат записывается в data/minjust-normalized. Пути можно изменить параметрами --input и --output; --refresh принудительно обрабатывает неизменившиеся документы, --log-level задаёт уровень журнала. Каталоги --input и --output не должны совпадать, содержать друг друга или пересекаться через разрешённые абсолютные пути.

data/minjust-normalized/
  manifest.sqlite3
  documents/<code>/document.json
  documents/<code>/editions/<edition>/edition.json
  documents/<code>/editions/<edition>/<lang>/content.html
  documents/<code>/editions/<edition>/<lang>/content.txt
  documents/<code>/editions/<edition>/<lang>/fragments.json

content.html содержит только разрешённую безопасную разметку, content.txt — текст для поиска, а fragments.json — адресуемые блоки с детерминированными ID и SHA-256. Манифест пропускает документы с неизменившимися исходниками и повторяет документы, обработка которых завершилась ошибкой.

Первая версия не выполняет OCR, перевод, юридические выводы о редакциях, сопоставление фрагментов, загрузку в PostgreSQL/OpenSearch и построение RAG. Внешние и встроенные data:-изображения из HTML удаляются; сведения и пути к локальным изображениям исходного архива сохраняются в edition.json.

Проверка

PYTHONPATH=backend python3 -m unittest backend/test_minjust_cbd.py -v
PYTHONPATH=backend python3 -m unittest backend/test_minjust_normalization.py -v
PYTHONPATH=backend python3 -m unittest backend/test_minjust_opensearch.py -v

Подготовка индекса OpenSearch

Mapping поискового индекса находится в search/minjust-fragments-index.json. Для кыргызского текста он использует icu_analyzer, поэтому в OpenSearch должен быть установлен плагин analysis-icu.

Потоковый экспорт в формат Bulk API без внешних Python-зависимостей:

python3 backend/search/minjust_opensearch.py

По умолчанию создаётся data/opensearch/minjust-fragments.ndjson. Экспорт атомарный и детерминированный; для проверки можно передать --limit 1. Для прямой загрузки без большого промежуточного файла используется --url:

python3 backend/search/minjust_opensearch.py \
  --url http://127.0.0.1:9200 \
  --index akyldash-fragments-dev-v1 \
  --limit 1

Запросы Bulk API ограничены 25 МБ и не разрывают пару action/source. Для полного прохода убрать --limit и выбрать новое имя версионного индекса. После проверки production-индекса следует переключать alias, чтобы удалённые фрагменты не оставались в поиске.

Чтобы переключить alias атомарно только после успешной полной загрузки, передайте --alias:

python3 backend/search/minjust_opensearch.py \
  --url http://127.0.0.1:9200 \
  --index akyldash-fragments-v2 \
  --alias akyldash-fragments-current

После каждого принятого Bulk-пакета загрузчик атомарно сохраняет checkpoint и печатает код безопасного возобновления. При временных HTTP 429/5xx, timeout и обрыве соединения запрос повторяется автоматически. Прерванную загрузку можно продолжить без ручного выбора документа:

python3 backend/search/minjust_opensearch.py \
  --url http://127.0.0.1:9200 \
  --index akyldash-fragments-v1 \
  --resume

По умолчанию checkpoint хранится в data/opensearch/<index>.checkpoint.json; путь можно изменить через --checkpoint. Checkpoint привязан к URL, cluster UUID, index UUID, --limit, --alias и SHA-256 нормализованного manifest. При --resume передавайте то же значение --alias; старый checkpoint без alias можно продолжить только без него. Resume отклоняется при любом несовпадении: для обновлённого корпуса или пересозданного индекса нужно создать новый версионный индекс, проверить его и переключить alias. Это не оставляет удалённые trailing-фрагменты старых документов.

HTTP API v1

Запустите публичный read-only API поверх текущего alias и нормализованного корпуса:

PYTHONPATH=backend python3 -m search.api

Он публикует OpenAPI в GET /openapi.json и поддерживает GET /search, /search/filters, /documents/{code}, /documents/{code}/editions и /documents/{code}/editions/{edition}. Значения фильтров возвращаются со стабильным кодом справочника v1 и подписями RU/KY; применяйте code как параметр поиска. API не подменяет отсутствующий язык документа.

Локальный OpenSearch

Стенд использует один узел OpenSearch без Dashboards, устанавливает analysis-icu, выделяет JVM 8 ГБ и доступен только на 127.0.0.1:9200. Индекс хранится в data/opensearch-node на диске проекта.

sudo sysctl -w vm.max_map_count=262144
docker compose -f deploy/local-opensearch/compose.yaml up -d --build
curl http://127.0.0.1:9200/_cluster/health

Security plugin отключён только для локальной разработки; этот compose нельзя публиковать в сеть или использовать в production. Mapping локального стенда также задаёт одну shard и ноль replicas; для production число shard следует рассчитать по размеру корпуса и настроить не менее одной replica.

Оценка качества поиска

search/relevance-set-v1.template.json содержит заготовку для 25 русских и 25 кыргызских запросов. Для каждого запроса человек должен указать реальную формулировку и коды всех релевантных документов; пустая или неполная разметка должна быть отклонена при ручной проверке, а технически некорректная — самим оценщиком. Критерии выбора запросов, релевантности и двойной проверки описаны в search/RELEVANCE_ANNOTATION.md. Рабочую копию следует хранить в игнорируемом каталоге data/, пока набор не проверен и не разрешён к публикации.

Baseline использует поля названия и текста соответствующего языка, оставляет в выдаче один результат на документ и вычисляет макро-средние Recall@10 и MRR@10:

mkdir -p data/search
cp backend/search/relevance-set-v1.template.json data/search/relevance-set-v1.json
PYTHONPATH=backend python3 -m search.evaluate_relevance \
  data/search/relevance-set-v1.json \
  > data/search/baseline-v1.json

Менять веса или анализаторы следует только после фиксации этого baseline и разбора ошибок выдачи.

Внутренняя лаборатория релевантности

Запустите Search API на localhost и откройте http://127.0.0.1:8080/review:

PYTHONPATH=backend python3 -m search.api \\
  --reviews-db data/search-reviews.sqlite3

Лаборатория показывает фактический порядок выдачи OpenSearch, позволяет открыть текст редакции, поставить результату оценку от 0 до 3 и сохранить снимок с комментариями. Оценки сохраняются в SQLite, экспорт доступен через GET /search-reviews/export. Интерфейс предназначен только для локальной сети или защищённого reverse proxy; не публикуйте его напрямую в интернет.

Для проверки текущей выдачи без будущего HTTP API используйте CLI:

PYTHONPATH=backend python3 -m search.query "как открыть ОсОО" --language ru
PYTHONPATH=backend python3 -m search.query "ЖЧК ачуу тартиби" --language ky

Акылдаш · Backend v0.8.0 · Frontend — не создан