From e6c44f30dd5f958a4a3b290ef6b1bdf18e6c5cdf Mon Sep 17 00:00:00 2001 From: jze9 Date: Tue, 11 Aug 2026 20:45:23 +0500 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=BE=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20=E2=80=94=20docs/ARCHITECTURE.?= =?UTF-8?q?md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Каноничная документация всей системы: назначение, схема, 6 сервисов и их зоны ответственности, модель данных (все таблицы), Celery-очереди/задачи, ключевые потоки (проверка плагиата, поиск, библиография, наполнение корпуса), 4 уровня детекции, векторный бэкенд, инфра-топология (узлы/адреса), конфигурация, гейты качества, наблюдаемость/эксплуатация, безопасность, раскладка репозитория. README ссылается на ARCHITECTURE.md и DR-HA.md. Данные сверены с кодом (модели, TaskType/TaskStatus, источники, тарифы, маршрутизация задач). Co-Authored-By: Claude Opus 4.8 --- README.md | 3 + docs/ARCHITECTURE.md | 190 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 docs/ARCHITECTURE.md diff --git a/README.md b/README.md index 6b2c14f..2f67b57 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,9 @@ Студент вводит тему → система ищет источники → проверяет плагиат → форматирует ГОСТ-библиографию. Всё асинхронно: студент закрыл браузер, получил email когда готово. +> 📐 Полное описание системы — [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md). +> Отказоустойчивость и восстановление — [docs/DR-HA.md](docs/DR-HA.md). + ## Архитектура ``` diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..b416775 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,190 @@ +# Архитектура — Академический помощник + +Каноничное описание системы. Обновляется вместе с кодом; при расхождении верить +коду, а не этому файлу. Смежные документы: [DR-HA.md](DR-HA.md) (отказоустойчивость), +[../README.md](../README.md) (быстрый старт и команды). + +## 1. Назначение + +Веб-сервис для студентов: ввёл тему → система находит **открытые** академические +источники, делает изложения, форматирует список литературы по ГОСТ и проверяет текст +на плагиат. Всё **асинхронно**: пользователь отправляет задачу, закрывает браузер и +получает результат на email (или в реальном времени через WebSocket, если вкладка открыта). + +## 2. Общая схема + +``` + ┌────────────────────────────┐ + │ Frontend (React SPA) │ CT 102 → nginx + │ Home/Search/Check/Cabinet/ │ + │ Bibliography/Settings/Admin │ + └──────────────┬──────────────┘ + │ HTTPS (/api, /ws) + ┌──────────────▼──────────────┐ + │ API Gateway (FastAPI) │ 1.32:8000 + │ JWT · rate-limit · WebSocket │ + │ /metrics (Prometheus) │ + └───┬───────────────────────┬──┘ + │ publish (RabbitMQ) │ read/write + │ │ + ┌────────────────────┼───────────────────┐ │ + ▼ ▼ ▼ │ + queue.index queue.gpu queue.gost + ┌───────────┐ ┌────────────┐ ┌───────────┐ + │ worker- │ │ worker-gpu │ │ worker- │ + │ indexer │ │ FAISS/Qdr. │ │ gost │ + │ L1 Winnow │ │ L3 семант. │ │ ГОСТ 7.1/ │ + │ L2 MinHash│ │ L4 LLM │ │ 7.0.5 │ + │ PDF/DOCX │ │ эмбеддинги │ │ │ + └─────┬─────┘ └──────┬─────┘ └─────┬─────┘ + │ │ │ + └─────────┬─────────┴───────┬──────────┘ + ▼ ▼ + queue.notify общие данные + ┌────────────┐ ┌──────────────────────────────┐ + │ worker- │ │ PostgreSQL 1.38 · Redis 1.35 │ + │ notifier │ │ RabbitMQ .82 · MinIO 1.21 │ + │ SMTP email │ │ Elasticsearch (local 1.32) │ + └────────────┘ │ Ollama .163 (qwen2.5:7b) │ + │ [opt] Qdrant · Prometheus/Graf.│ + └──────────────────────────────┘ +``` + +## 3. Сервисы + +| Сервис | Технологии | Ответственность | +|--------|-----------|-----------------| +| **api** | FastAPI, SQLAlchemy async (asyncpg), Redis, Celery-producer | HTTP/WS API, auth (JWT), rate-limits, диспетч задач в очереди, админ-панель | +| **worker-indexer** | Celery, PyMuPDF, python-docx, xxhash, datasketch | Извлечение текста (PDF/DOCX/TXT), фрагментация, **L1 Winnowing**, **L2 MinHash LSH**, парсинг источников, обогащение full-text | +| **worker-gpu** | Celery, sentence-transformers, FAISS/Qdrant, httpx→Ollama | Эмбеддинги, **L3** семантический поиск, **L4** LLM-анализ парафраза, семантический поиск источников | +| **worker-gost** | Celery | Список литературы по **ГОСТ 7.1-2003 / Р 7.0.5-2008** | +| **worker-notifier** | Celery, smtplib | Email: письма-результаты и верификация (jze9mail.ru) | +| **frontend** | React 18, Vite, TS, Tailwind, Zustand, React Query | SPA: 11 публичных страниц + админ-панель. Собирается в статику, отдаётся nginx | + +## 4. Модель данных (PostgreSQL) + +- **users** — `email`, `hashed_password` (bcrypt), `name`, `is_verified`, `is_admin`, + `plan` (free/student/premium), `verification_token`. +- **tasks** — `id` (UUID), `public_id` (внешний), `user_id`, `type` (`TaskType`: + search/plagiarism/summarize/gost), `status` (`TaskStatus`: + queued→processing→done/failed), `celery_task_id`, `input_data` (JSON), + `result` (JSON), `error`, `queue_position`. +- **documents** — корпус источников: `source` (openalex/arxiv/cyberleninka/ + user_submission), `ext_id`, `doi`, `title`, `authors` (JSON), `year`, `lang`, + `journal/volume/issue/pages`, `abstract`, `url`, `minio_key` (full-text в MinIO), + `faiss_id`. +- **fingerprints** — `doc_id`, `hash_value` (BIGINT, Winnowing), `position` — для L1. +- **usage_logs** — `user_id`, `action` — учёт лимитов по тарифу. +- **parse_sources** — задания парсеров (админка): тип, query, годы, лимит, статус. +- **staged_works** — пользовательские загрузки на модерацию перед добавлением в корпус. +- **admin_sessions** — одноразовые коды входа в админку. + +## 5. Асинхронный конвейер (Celery + RabbitMQ) + +Брокер — RabbitMQ, backend результатов — Redis. Маршрутизация по префиксу задачи: + +| Очередь | Задачи | Воркер | +|---------|--------|--------| +| `queue.index` | `index.extract_and_check`, `index.add_document`, `index.run_parser`, `index.enrich_full_text` | worker-indexer | +| `queue.gpu` | `gpu.check_plagiarism`, `gpu.embed_documents`, `gpu.search_semantic` | worker-gpu | +| `queue.gost` | `gost.format_bibliography` | worker-gost | +| `queue.notify` | `notify.send_task_done`, `notify.send_verification` | worker-notifier | + +Важно: API **коммитит задачу в БД до** `send_task` (иначе гонка dispatch-before-commit). + +## 6. Ключевые потоки + +**Проверка плагиата.** upload (api, файл→MinIO, Task) → `index.extract_and_check` +(извлечь текст → фрагментация → **L1 Winnowing** по fingerprints → **L2 MinHash LSH** +в Redis) → передаёт частичные совпадения в `gpu.check_plagiarism` (**L3** FAISS/Qdrant +семантика по фрагментам → для подозрительных **L4** Ollama-парафраз) → +`app.scoring.aggregate_results` (итоговый %) → результат в Task → `notify.send_task_done`. + +**Поиск источников.** api → `gpu.search_semantic`: эмбеддинг запроса → векторный поиск +(FAISS/Qdrant) + Elasticsearch BM25 → объединение → результат. + +**Список литературы.** api → `gost.format_bibliography`: документы из БД → +`app.bibliography.build_bibliography` (сортировка кириллица→латиница, нумерация, +формат 7.1/7.0.5) → результат. + +**Наполнение корпуса.** админка/CLI → `index.run_parser` (OpenAlex/arXiv/КиберЛенинка, +фильтр `is_oa`) → `index.add_document` (дедуп по `ext_id`, fingerprints, MinHash, +эмбеддинг) → `index.enrich_full_text` (скачать OA-PDF → MinIO → переиндексация). + +## 7. Детекция плагиата — 4 уровня + +1. **L1 Winnowing** (`worker-indexer/app/algorithms/winnowing.py`) — точные/частичные + совпадения: k-граммы → xxHash → минимум в скользящем окне → fingerprint; Jaccard. +2. **L2 MinHash LSH** (`.../minhash.py`) — нечёткие совпадения: шинглы → MinHash (128 + перм.) → LSH-индекс в **общем Redis** (префикс `antiplag_lsh`, upsert, graceful-фолбэк + в память). +3. **L3 семантика** (`worker-gpu`) — эмбеддинги `paraphrase-multilingual-mpnet-base-v2` + (768d, нормированы) → cosine в FAISS `IndexIDMap2(IndexFlatIP)` **или** Qdrant + (`VECTOR_BACKEND`); порог 0.75. +4. **L4 LLM-парафраз** — Ollama `qwen2.5:7b` оценивает пары «источник↔фрагмент» для + подозрительных из L3; порог confidence 0.7. + +Итог: `scoring.aggregate_results` — доля уникальных помеченных позиций (не выше 100%). + +## 8. Векторный бэкенд + +Абстрагирован за `worker-gpu/app/vector_store.get_backend()`; `VECTOR_BACKEND=faiss` +(файловый синглтон, дефолт) или `qdrant` (сервис, снимает SPOF/конкурентную запись). +Переключение и миграция — см. README, раздел «Векторный бэкенд». + +## 9. Инфраструктура и топология + +| Компонент | Узел | Примечание | +|-----------|------|-----------| +| api + воркеры + Elasticsearch | 1.32 (app-хост) | docker-compose.prod.yml | +| Frontend (nginx, статика + TLS) | CT 102 | деплоится отдельно | +| PostgreSQL 16 | 1.38 (выделенный LXC) | UTF8; бэкап→MinIO | +| Redis 7 | 1.35 (выделенный LXC) | кэш, rate-limits, LSH | +| RabbitMQ | 192.168.20.82 | брокер Celery | +| MinIO (S3) | 1.21 | документы, full-text, бэкапы | +| Ollama (qwen2.5:7b) | 192.168.20.163 | GPU-сервер | +| Qdrant / Prometheus / Grafana | 1.32 | опционально, под compose-профилями | + +**Деплой** — Gitea Actions по push в `main`: гейт `test` (ruff+mypy → 82 юнит-теста), +затем `deploy` (`needs: test`) через `scripts/deploy.sh` (умная пересборка изменённых +сервисов). PG/Redis не в compose — общая инфра берётся из `.env`. + +## 10. Конфигурация + +Всё через `.env` (пример — `.env.example`). Обязательно менять: `SECRET_KEY`, +`POSTGRES_PASSWORD`, `MINIO_SECRET_KEY`. Опции: `VECTOR_BACKEND`, `QDRANT_URL`, +`GRAFANA_ADMIN_PASSWORD`. `.env` не в git и исключён из деплой-rsync (не откатывается). + +## 11. Качество и тесты + +- **82 юнит-теста** (pytest, per-service) на чистую логику L1-L4/скоринг/фрагменты/ + ГОСТ/библиография; инфра замокана или не нужна. Запуск: `make test`. +- **Гейты CI**: ruff (весь Python) + mypy (доменная логика) + тесты — блокируют деплой. + `make lint`. Хермет-раннеры в `python:3.11-slim`. +- Чистая доменная логика вынесена из Celery-задач в тестируемые модули + (`scoring.py`, `fragments.py`, `bibliography.py`). Подробнее — README «Тестирование». + +## 12. Наблюдаемость и эксплуатация + +- **Мониторинг**: `antiplag_monitor.py` (cron 5 мин, 7 сервисов, email-алерт при смене + статуса). Опционально — Prometheus+Grafana (профиль `observability`, метрики API + Flower). +- **Бэкапы**: `pg_dump→gzip→MinIO`, cron 03:00, ротация 14. Проверка восстановления — + `scripts/ops/pg_restore_verify.sh`. HA/DR — [DR-HA.md](DR-HA.md). + +## 13. Безопасность + +JWT-аутентификация, bcrypt-хэши паролей, email-верификация, отдельный вход в админку +(одноразовые коды, `admin_sessions`). Rate-limits по тарифу — в Redis. CORS — явные +origins. Пользователь видит только свои задачи (ownership проверяется, в т.ч. на WebSocket). + +## 14. Раскладка репозитория + +``` +services/ api, worker-{gpu,indexer,notifier,gost}, frontend +scripts/ parsers/ (OpenAlex/arXiv/КиберЛенинка), ops/, deploy.sh, + run_tests.sh, run_lint.sh +infra/ nginx/, prometheus/, grafana/ +docs/ ARCHITECTURE.md (этот файл), DR-HA.md +.gitea/workflows/ deploy.yml (гейт test → deploy) +ruff.toml · mypy.ini · Makefile · docker-compose.prod.yml +```