Документация сильно разошлась с кодом за последние недели работы — привёл в
соответствие 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 нигде не строится, эти поля ничего не делали.
Ollama обрабатывает тексты в пачке последовательно (один "slot" в
llama.cpp, не параллельно) — 64 реальных документа в одном HTTP-запросе
регулярно не укладывались в 120с, весь батч падал с ReadTimeout и
терялся без ретрая (embed_documents была без bind/max_retries).
Теперь: пачка 16, таймаут 300с, задача ретраится до 3 раз при сбое.
OpenRouter отдаёт 403 с российских IP (подтверждено вживую). Добавлен
sing-box как отдельный сервис (VLESS+WS+TLS до ноды вне РФ) — только для
исходящих запросов к OpenRouter из ollama_client.py, остальной трафик
(Postgres/RabbitMQ/MinIO/Ollama) идёт напрямую. Реальный конфиг с UUID —
только на проде (см. config.json.example), в git не попадает.
EMBED_BACKEND=cloud — эмбеддинги через DeepInfra (OpenAI-совместимый API),
модель та же BAAI/bge-m3, что уже льётся на VM109 — переключение не рвёт
совместимость с уже пересчитанным FAISS-индексом (тот же вектор 1024).
Не активно по умолчанию (EMBED_BACKEND всё ещё "ollama").
LLM_BACKEND=ollama (дефолт, поведение не меняется) | openrouter (облачный
API, дешёвая модель — для да/нет+уверенность "ум" не критичен, зато
не нужен локальный GPU-хост вообще). Общая _complete() прячет разницу
форматов запроса/ответа за check_paraphrase/summarize.
Раньше эмбеддинг-модель (уровень 3) гоняла на CPU внутри worker-gpu —
GPU CT108 использовался только под LLM-парафраз (уровень 4). Теперь
эмбеддинги идут через Ollama /api/embed на отдельной VM с RX 580
(Vulkan-бэкенд, без возни с ROCm/HIP для этой карты). EMBED_BACKEND
переключаемый ("ollama" | "sentence_transformers"), дефолт — ollama.
Модель сменилась на bge-m3 (1024-мерный вектор вместо 768 у
paraphrase-multilingual-mpnet-base-v2) — несовместимо с уже посчитанным
FAISS-индексом, нужна полная переиндексация корпуса после деплоя.
Заодно докстринг delete_task в tasks.py — снятое раньше ограничение
"нельзя удалить processing" оставляло враньё в докстринге.
FAISS-индекс — файловый синглтон в RAM одного воркера (save на каждую запись,
без блокировок, без HA, не горизонтален). Добавлен альтернативный бэкенд Qdrant
с тем же classmethod-интерфейсом, выбор через VECTOR_BACKEND — аддитивно и
безопасно: дефолт остаётся faiss, ничего не ломается, пока не переключат.
- app/qdrant_manager.py — search/add_vectors/save(no-op)/total_vectors поверх
qdrant-client (коллекция Cosine, id точки = doc_id, upsert идемпотентен);
- app/vector_store.py — get_backend() по настройке; задачи search/plagiarism
переведены на него (больше не импортируют FAISSManager напрямую);
- app/migrate_faiss_to_qdrant.py — перелив существующих векторов (reconstruct
из IndexIDMap2 → upsert), идемпотентно;
- docker-compose.prod.yml — сервис qdrant под профилем `qdrant` (по умолчанию не
поднимается, ресурсов не ест) + volume; README — раздел про переключение.
Тесты (9) гоняют QdrantManager против ВСТРОЕННОГО Qdrant (qdrant-client :memory:,
не моки) + диспетчеризацию бэкенда. Всего тестов: 82 (indexer 24, gost 24, gpu 34).
Плюсы Qdrant активируются только после явного переключения + миграции.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>