Files
jze9 d8630ca0f9 fix(admin): покрытие L3 мерить по индексу, а не по отметкам в базе
Проверка на живых данных вскрыла, что панель отладки врала в мою же пользу:
показывала 154 046 «документов с эмбеддингом» (87%), тогда как в FAISS реально
93 053 вектора (52.5%). Колонка documents.faiss_id для этого непригодна —
отметка остаётся после пересоздания индекса (смена модели: 768 → 1024) и после
сбоев worker-gpu. Выборочная проверка: у 8 из 20 «отмеченных» вектора нет.

- gpu.index_stats — новая задача, отдаёт реальное содержимое активного
  векторного бэкенда; API спрашивает её для панели отладки;
- панель показывает «векторов в индексе» и отдельно предупреждает о ложных
  отметках (их 60 993), потому что такие документы молча выпадают из L3:
  в индексе их нет, а на пересчёт они не попадут — reembed ищет faiss_id IS NULL;
- scripts/ops/faiss_reconcile.py — сверяет отметки с индексом и обнуляет ложные,
  после чего reembed_missing.py отправляет их на пересчёт. Проверен вживую
  (dry-run на проде: 93 053 в индексе, 60 993 ложных отметок).

Документация: зафиксирована реальная глубина корпуса — полный текст только у
27.5% документов, у 79% меньше 500 отпечатков (уровень аннотации). Система
ловит списывание из того, что есть целиком; это граница, а не поломка.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:17:41 +05:00

40 lines
1.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Служебная задача: состояние векторного индекса для панели отладки.
Индекс живёт в памяти и на диске worker-gpu, у API к нему доступа нет. Без этой
задачи админка показывала покрытие L3 по колонке `documents.faiss_id` — а она
врёт: пометка остаётся и тогда, когда вектор в индекс не попал (или индекс был
пересоздан после смены модели эмбеддингов). Здесь возвращается то, что в индексе
есть на самом деле.
"""
from typing import Any
from celery.utils.log import get_task_logger
from app.celery_app import celery_app
from app.config import settings
from app.vector_store import get_backend
logger = get_task_logger(__name__)
@celery_app.task(name="gpu.index_stats")
def index_stats() -> dict[str, Any]:
"""Реальное число векторов в активном бэкенде (FAISS или Qdrant)."""
backend = get_backend()
try:
# У FAISS индекс ленивый: без обращения ntotal вернёт 0 на холодном воркере
if hasattr(backend, "_ensure"):
backend._ensure()
total = backend.total_vectors()
except Exception as e:
logger.warning(f"index_stats: не удалось прочитать индекс: {e}")
return {"backend": settings.VECTOR_BACKEND, "error": str(e)[:200]}
return {
"backend": settings.VECTOR_BACKEND,
"vectors": total,
"dim": settings.EMBED_DIM,
"embed_model": settings.EMBED_MODEL,
}