docs: актуализировать документацию (Infisical, OpenRouter, bge-m3, PMC) + чистка
Документация сильно разошлась с кодом за последние недели работы — привёл в
соответствие ARCHITECTURE.md, README, DIAGRAM.md, INGESTION.md:
- LLM (L4) и эмбеддинги (L3) теперь переключаемые бэкенды (LLM_BACKEND,
EMBED_BACKEND), а не жёстко Ollama/qwen2.5:7b — старая модель эмбеддингов
(768d mpnet) заменена на bge-m3 (1024d); L4 может идти через OpenRouter
(DeepSeek) с прокси singbox для обхода геоблокировки.
- .env на проде больше не редактируется руками — на каждом деплое
генерируется из Infisical; старая инструкция "cp .env.example .env; nano
.env" вводила в заблуждение (правки терялись бы на следующем деплое).
- README "Три docker-compose файла"/deploy разделы противоречили реальности:
фронтенд+TLS реально раздаёт хостовой nginx на CT 102, не докеризованный
nginx-сервис из compose (тот не запускается вообще).
добавлен PMC-парсер, self-match исключение объяснено, число тестов (113)
синхронизировано между файлами (было 82/122 в разных местах).
- INGESTION.md: снимок "42K доков, 0 русских" от 12.08 заменён актуальным
(165K доков, ru теперь большинство) — иначе документ откровенно врёт.
- Diagram: узлы под текущую топологию (embedding-gpu, OpenRouter, Infisical).
Заодно, раз перепроверял код на соответствие докам:
- Удалён мёртвый и битый эндпоинт GET /reports/{task_id} — фильтровал по
внутреннему Task.id вместо public_id (никогда не мог сработать через
обычный клиентский поток), фронтенд его всё равно не вызывал —
функциональность полностью дублирует рабочий GET /tasks/{public_id}.
- Убран мёртвый конфиг FAISS_NLIST/FAISS_NPROBE (worker-gpu/config.py) —
индекс всегда IndexFlatIP, IVF нигде не строится, эти поля ничего не делали.
This commit is contained in:
58
README.md
58
README.md
@@ -87,7 +87,7 @@ make clean # Удалить контейнеры и volumes
|
||||
| Компонент | Технологии |
|
||||
|-----------|-----------|
|
||||
| API Gateway | FastAPI 0.111, Python 3.11, SQLAlchemy 2.0, Alembic |
|
||||
| GPU Worker | sentence-transformers, FAISS-CPU (IndexIDMap2 · IndexFlatIP), Ollama (qwen2.5:7b) |
|
||||
| GPU Worker | FAISS-CPU (IndexIDMap2 · IndexFlatIP), эмбеддинги bge-m3/1024d (Ollama/sentence-transformers/облако — `EMBED_BACKEND`), LLM-парафраз через Ollama или OpenRouter (`LLM_BACKEND`) |
|
||||
| Indexer | PyMuPDF, python-docx, Winnowing, MinHash LSH |
|
||||
| Очереди | RabbitMQ (брокер) + Celery 5 (воркеры) + Redis (результаты) |
|
||||
| База данных | PostgreSQL 16 |
|
||||
@@ -99,8 +99,13 @@ make clean # Удалить контейнеры и volumes
|
||||
|
||||
1. **Winnowing** — точные/частичные совпадения по fingerprint'ам (xxHash + скользящее окно)
|
||||
2. **MinHash LSH** — нечёткие совпадения (шинглы + Jaccard, индекс в общем Redis)
|
||||
3. **FAISS cosine** — семантическое сходство (порог 0.75; IndexFlatIP на нормированных эмбеддингах)
|
||||
4. **Ollama qwen2.5:7b** — LLM-анализ парафраза (порог confidence 0.7)
|
||||
3. **FAISS cosine** — семантическое сходство (порог 0.75; эмбеддинги bge-m3/1024d, IndexFlatIP на нормированных векторах)
|
||||
4. **LLM-анализ парафраза** (порог confidence 0.7) — бэкенд переключается `LLM_BACKEND`: локальная Ollama или облачный OpenRouter/DeepSeek (геоблокировка РФ обходится через SOCKS5-прокси `singbox-proxy`, см. `infra/singbox/`)
|
||||
|
||||
Проверенные работы сами пополняют корпус (`source=user_submission`) — это даёт эффект
|
||||
как у коммерческих систем (база растёт от каждой проверки), но такие документы
|
||||
**исключены** из сравнения на всех уровнях L1-L3 (`AUTO_APPROVE_SUBMISSIONS`,
|
||||
по умолчанию выключено), иначе работа матчилась бы сама с собой на 100%.
|
||||
|
||||
## Тарифные планы
|
||||
|
||||
@@ -130,8 +135,17 @@ python scripts/run_parser.py arxiv \
|
||||
--query "deep learning" \
|
||||
--categories cs.AI cs.LG \
|
||||
--limit 5000
|
||||
|
||||
# PubMed Central (PMC) — англоязычные научные статьи открытого доступа
|
||||
python scripts/run_parser.py pmc \
|
||||
--query "public health" \
|
||||
--limit 1000
|
||||
```
|
||||
|
||||
Массовое расширение корпуса по дисциплинам (не единичный запрос) — `scripts/seed_broad_corpus.py`
|
||||
и `scripts/seed_ru_sources.py` заводят десятки `parse_sources` записей сразу, дальше их
|
||||
разбирает `index.run_parser` через очередь. Подробности и текущий охват — [docs/INGESTION.md](docs/INGESTION.md).
|
||||
|
||||
## Переменные окружения
|
||||
|
||||
Смотри `.env.example` для полного списка переменных.
|
||||
@@ -141,26 +155,34 @@ python scripts/run_parser.py arxiv \
|
||||
|
||||
| Файл | Назначение |
|
||||
|------|-----------|
|
||||
| `docker-compose.prod.yml` | **Реальный прод.** app-сервисы + nginx (собранный фронтенд + TLS) + локальный Elasticsearch. Postgres/Redis/RabbitMQ/MinIO/Ollama — уже существующие общие серверы, адреса в `.env`. |
|
||||
| `docker-compose.prod.yml` | **Реальный прод.** app-сервисы + локальный Elasticsearch. Postgres/Redis/RabbitMQ/MinIO/Ollama — уже существующие общие серверы, адреса в `.env`. Файл описывает и сервис `nginx` (собранный фронтенд + TLS), но в текущей топологии он **не запускается** — фронтенд и TLS реально раздаёт хостовой nginx на отдельном CT 102 (см. «Продакшн деплой» ниже), а `api` публикует `8000:8000` наружу именно под него. |
|
||||
| `docker-compose.test.yml` | Повседневная разработка — hot reload против той же общей инфры, что и прод (`make test-up`). |
|
||||
| `docker-compose.selfhosted.yml.example` | Не используется. Полностью автономный вариант (свои Postgres/Redis/RabbitMQ/ES/MinIO/Ollama + GPU passthrough) — на случай отдельного выделенного сервера в будущем. |
|
||||
|
||||
## Продакшн деплой
|
||||
|
||||
```bash
|
||||
# На сервере
|
||||
cp .env.example .env
|
||||
nano .env # Настроить все пароли и ключи, указать реальные адреса общих Postgres/Redis/RabbitMQ/MinIO/Ollama
|
||||
Автоматический: push в `main` → Gitea Actions (`.gitea/workflows/deploy.yml`) → гейт
|
||||
`test` (lint + юнит-тесты) → `deploy` → `scripts/deploy.sh` на app-хосте. Умная
|
||||
пересборка — образ пересобирается только у сервисов, чей код изменился с прошлого
|
||||
деплоя (маркер SHA в `.last_deploy_sha`); правка `docker-compose.prod.yml` триггерит
|
||||
полную пересборку всех пяти бэкенд-сервисов.
|
||||
|
||||
# Сертификат ДО первого запуска nginx-контейнера (standalone, порт 80 должен быть свободен)
|
||||
certbot certonly --standalone -d academic.jze9.ru
|
||||
**`.env` на проде НЕ редактируется руками.** Первым шагом `deploy.sh` логинится в
|
||||
self-hosted Infisical (Machine Identity, Universal Auth) и генерирует `.env` заново
|
||||
из окружения `prod` на каждом запуске — ручные правки файла на сервере переживут
|
||||
максимум до следующего деплоя. Менять секреты/конфиг — через Infisical
|
||||
(`https://infisical.jze9.ru`, проект `academ`), не через `.env` напрямую. Если
|
||||
Infisical недоступен или вернул подозрительно мало ключей, деплой падает раньше
|
||||
синка кода и не трогает рабочий `.env`.
|
||||
|
||||
make build
|
||||
make up
|
||||
make migrate
|
||||
```
|
||||
Фронтенд собирается отдельно (`docker run node:20-slim` → `npm run build`) и
|
||||
заливается по `scp`/`pct push` на CT 102, где раздаётся **хостовым** (не
|
||||
докеризованным) nginx с TLS через certbot — см. `/etc/nginx/sites-available/academic`
|
||||
на CT 102. Это отдельная машина от app-хоста, деплоится только когда меняется
|
||||
`services/frontend/`.
|
||||
|
||||
Nginx работает внутри контейнера (`docker-compose.prod.yml`, сервис `nginx`, Dockerfile в `infra/nginx/Dockerfile`) — собирает `services/frontend` в статику и отдаёт её вместе с проксированием `/api/`, `/ws/` на `api:8000` по конфигу `infra/nginx/nginx.conf`. Контейнер монтирует `/etc/letsencrypt` с хоста как read-only — сертификат обновляется на хосте (`certbot renew`), контейнер просто читает его.
|
||||
Ручной запуск деплоя (например, после смены секрета в Infisical без изменений кода) —
|
||||
`workflow_dispatch` в Gitea Actions, или локально: `PVE_PASSWORD=... INFISICAL_CLIENT_ID=... INFISICAL_CLIENT_SECRET=... bash scripts/deploy.sh` из полного чекаута репозитория.
|
||||
|
||||
## Векторный бэкенд (FAISS / Qdrant)
|
||||
|
||||
@@ -199,7 +221,9 @@ docker compose -f docker-compose.prod.yml --profile observability up -d promethe
|
||||
- Prometheus (`infra/prometheus/prometheus.yml`) скрейпит API и **Flower** — из
|
||||
Flower приходят метрики Celery (задачи, время выполнения, воркеры) без доп. кода.
|
||||
- Grafana с автоподключённым источником Prometheus (`infra/grafana/provisioning/`);
|
||||
пароль admin — через `GRAFANA_ADMIN_PASSWORD` в `.env` (по умолчанию `admin`).
|
||||
пароль admin — `GRAFANA_ADMIN_PASSWORD` в Infisical (prod). Если не задан — падает
|
||||
на дефолт `admin`, поэтому перед включением профиля `observability` в проде
|
||||
убедиться, что значение в Infisical реально установлено (не пустое).
|
||||
|
||||
## Тестирование и качество кода
|
||||
|
||||
@@ -208,7 +232,7 @@ docker compose -f docker-compose.prod.yml --profile observability up -d promethe
|
||||
|
||||
1. **Линт** — `ruff` (весь Python) + `mypy` (чистая доменная логика).
|
||||
Конфиги: [`ruff.toml`](ruff.toml), [`mypy.ini`](mypy.ini).
|
||||
2. **Юнит-тесты** — `pytest` по сервисам: 122 теста на ядро детекции, скоринга,
|
||||
2. **Юнит-тесты** — `pytest` по сервисам: 113 тестов на ядро детекции, скоринга,
|
||||
парсеров, форматирования и OAuth, без внешней инфры (БД/Redis/GPU/Ollama
|
||||
замоканы либо не нужны).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user