# Backend Акылдаш Версия: `0.9.1` Первая backend-область проекта — загрузка правовых документов из официального Open Data API ЦБД Минюста Кыргызской Республики. Код расположен в `ingestion/minjust_cbd.py`: его можно запускать как CLI сейчас и импортировать из будущего планировщика backend без запуска отдельного процесса. ## Пробная загрузка Из корня репозитория: ```bash 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`. Лимит действует на один процесс, поэтому одновременно следует запускать только один экземпляр загрузчика. Более осторожный режим: ```bash python3 backend/ingestion/minjust_cbd.py --requests-per-second 0.5 ``` ## Вызов из будущего backend ```python from ingestion.minjust_cbd import sync_archive result = sync_archive(output_path) ``` Планировщик, очередь задач и PostgreSQL пока не добавлены: модуль не зависит от выбора будущего backend-фреймворка. ## Нормализация архива Нормализатор читает исходный архив без изменений и создаёт отдельный набор данных для будущих поиска, API и RAG: ```bash 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` не должны совпадать, содержать друг друга или пересекаться через разрешённые абсолютные пути. ```text data/minjust-normalized/ manifest.sqlite3 documents//document.json documents//editions//edition.json documents//editions///content.html documents//editions///content.txt documents//editions///fragments.json ``` `content.html` содержит только разрешённую безопасную разметку, `content.txt` — текст для поиска, а `fragments.json` — адресуемые блоки с детерминированными ID и SHA-256. Манифест пропускает документы с неизменившимися исходниками и повторяет документы, обработка которых завершилась ошибкой. Первая версия не выполняет OCR, перевод, юридические выводы о редакциях, сопоставление фрагментов, загрузку в PostgreSQL/OpenSearch и построение RAG. Внешние и встроенные `data:`-изображения из HTML удаляются; сведения и пути к локальным изображениям исходного архива сохраняются в `edition.json`. ## Проверка ```bash 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-зависимостей: ```bash python3 backend/search/minjust_opensearch.py ``` По умолчанию создаётся `data/opensearch/minjust-fragments.ndjson`. Экспорт атомарный и детерминированный; для проверки можно передать `--limit 1`. Для прямой загрузки без большого промежуточного файла используется `--url`: ```bash 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`: ```bash 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 и обрыве соединения запрос повторяется автоматически. Прерванную загрузку можно продолжить без ручного выбора документа: ```bash python3 backend/search/minjust_opensearch.py \ --url http://127.0.0.1:9200 \ --index akyldash-fragments-v1 \ --resume ``` По умолчанию checkpoint хранится в `data/opensearch/.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 и нормализованного корпуса: ```bash 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` на диске проекта. ```bash 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: ```bash 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`: ```bash 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: ```bash PYTHONPATH=backend python3 -m search.query "как открыть ОсОО" --language ru PYTHONPATH=backend python3 -m search.query "ЖЧК ачуу тартиби" --language ky ``` --- Акылдаш · Backend v0.9.1 · Frontend — не создан