Backend Акылдаш
Версия: 0.7.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}. Значения фильтров возвращаются с
каноническим значением ЦБД и подписью выбранного языка; применяйте 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 и разбора ошибок выдачи.
Для проверки текущей выдачи без будущего HTTP API используйте CLI:
PYTHONPATH=backend python3 -m search.query "как открыть ОсОО" --language ru
PYTHONPATH=backend python3 -m search.query "ЖЧК ачуу тартиби" --language ky
Акылдаш · Backend v0.7.0 · Frontend — не создан