Files
core/settings.py
Untone 3c40bbde2b 0.9.29] - 2025-10-08
### 🎯 Search Quality Upgrade: ColBERT + Native MUVERA + FAISS

- **🚀 +175% Recall**: Интегрирован ColBERT через pylate с НАТИВНЫМ MUVERA multi-vector retrieval
- **🎯 TRUE MaxSim**: Настоящий token-level MaxSim scoring, а не упрощенный max pooling
- **🗜️ Native Multi-Vector FDE**: Каждый токен encode_fde отдельно → список FDE векторов
- **🚀 FAISS Acceleration**: Двухэтапный поиск O(log N) для масштабирования >10K документов
- **🎯 Dual Architecture**: Поддержка BiEncoder (быстрый) и ColBERT (качественный) через `SEARCH_MODEL_TYPE`
- ** Faster Indexing**: ColBERT индексация ~12s vs BiEncoder ~26s на бенчмарке
- **📊 Better Results**: Recall@10 улучшен с 0.16 до 0.44 (+175%)

### 🛠️ Technical Changes

- **requirements.txt**: Добавлены `pylate>=1.0.0` и `faiss-cpu>=1.7.4`
- **services/search.py**:
  - Добавлен `MuveraPylateWrapper` с **native MUVERA multi-vector** retrieval
  - 🎯 **TRUE MaxSim**: token-level scoring через списки FDE векторов
  - 🚀 **FAISS prefilter**: двухэтапный поиск (грубый → точный)
  - Обновлен `SearchService` для динамического выбора модели
  - Каждый токен → отдельный FDE вектор (не max pooling!)
- **settings.py**:
  - `SEARCH_MODEL_TYPE` - выбор модели (default: "colbert")
  - `SEARCH_USE_FAISS` - включить FAISS (default: true)
  - `SEARCH_FAISS_CANDIDATES` - количество кандидатов (default: 1000)

### 📚 Documentation

- **docs/search-system.md**: Полностью обновлена документация
  - Сравнение BiEncoder vs ColBERT с бенчмарками
  - 🚀 **Секция про FAISS**: когда включать, архитектура, производительность
  - Руководство по выбору модели для разных сценариев
  - 🎯 **Детальное описание native MUVERA multi-vector**: каждый токен → FDE
  - TRUE MaxSim scoring алгоритм с примерами кода
  - Двухэтапный поиск: FAISS prefilter → MaxSim rerank
  - 🤖 Предупреждение о проблеме дистилляционных моделей (pylate#142)

### ⚙️ Configuration

```bash
# Включить ColBERT (рекомендуется для production)
SEARCH_MODEL_TYPE=colbert

# 🚀 FAISS acceleration (обязательно для >10K документов)
SEARCH_USE_FAISS=true              # default: true
SEARCH_FAISS_CANDIDATES=1000       # default: 1000

# Fallback к BiEncoder (быстрее, но -62% recall)
SEARCH_MODEL_TYPE=biencoder
```

### 🎯 Impact

-  **Качество поиска**: +175% recall на бенчмарке NanoFiQA2018
-  **TRUE ColBERT**: Native multi-vector без упрощений (max pooling)
-  **MUVERA правильно**: Используется по назначению для multi-vector retrieval
-  **Масштабируемость**: FAISS prefilter → O(log N) вместо O(N)
-  **Готовность к росту**: Архитектура выдержит >50K документов
-  **Индексация**: Быстрее на ~54% (12s vs 26s)
- ⚠️ **Latency**: С FAISS остается приемлемой даже на больших индексах
-  **Backward Compatible**: BiEncoder + отключение FAISS через env

### 🔗 References

- GitHub PR: https://github.com/sionic-ai/muvera-py/pull/1
- pylate issue: https://github.com/lightonai/pylate/issues/142
- Model: `answerdotai/answerai-colbert-small-v1`
2025-10-09 01:15:19 +03:00

115 lines
4.5 KiB
Python

"""Настройки приложения"""
import datetime
import os
from os import environ
from pathlib import Path
from typing import Literal, cast
# Корневая директория проекта
ROOT_DIR = Path(__file__).parent.absolute()
DEV_SERVER_PID_FILE_NAME = "dev-server.pid"
PORT = environ.get("PORT") or 8000
# storages
DB_URL = (
environ.get("DATABASE_URL", "").replace("postgres://", "postgresql://")
or environ.get("DB_URL", "").replace("postgres://", "postgresql://")
or "sqlite:///discoursio.db"
)
DATABASE_URL = DB_URL
REDIS_URL = environ.get("REDIS_URL") or "redis://127.0.0.1"
# debug
GLITCHTIP_DSN = environ.get("GLITCHTIP_DSN")
# auth
ADMIN_SECRET = environ.get("AUTH_SECRET") or "nothing"
ADMIN_EMAILS = (
environ.get("ADMIN_EMAILS") or "services@discours.io,guests@discours.io,welcome@discours.io,test_admin@discours.io"
)
# own auth
ONETIME_TOKEN_LIFE_SPAN = 60 * 15 # 15 минут
SESSION_TOKEN_LIFE_SPAN = 60 * 60 * 24 * 30 # 30 дней
SESSION_TOKEN_HEADER = "Authorization"
JWT_ALGORITHM = "HS256"
JWT_SECRET_KEY = os.getenv("JWT_SECRET_KEY", "default_secret_key_change_in_production-ok?")
JWT_ISSUER = "discours"
JWT_EXPIRATION_DELTA = datetime.timedelta(days=30) # Токен действителен 30 дней
# URL фронтенда
FRONTEND_URL = os.getenv("FRONTEND_URL", "http://localhost:3000")
# Настройки OAuth провайдеров
OAUTH_CLIENTS = {
"GOOGLE": {
"id": os.getenv("GOOGLE_CLIENT_ID", ""),
"key": os.getenv("GOOGLE_CLIENT_SECRET", ""),
},
"GITHUB": {
"id": os.getenv("GITHUB_CLIENT_ID", ""),
"key": os.getenv("GITHUB_CLIENT_SECRET", ""),
},
"FACEBOOK": {
"id": os.getenv("FACEBOOK_APP_ID", ""),
"key": os.getenv("FACEBOOK_APP_SECRET", ""),
},
"X": {
"id": os.getenv("X_CLIENT_ID", ""),
"key": os.getenv("X_CLIENT_SECRET", ""),
},
"YANDEX": {
"id": os.getenv("YANDEX_CLIENT_ID", ""),
"key": os.getenv("YANDEX_CLIENT_SECRET", ""),
},
"VK": {
"id": os.getenv("VK_APP_ID", ""),
"key": os.getenv("VK_APP_SECRET", ""),
},
"TELEGRAM": {
"id": os.getenv("TELEGRAM_CLIENT_ID", ""),
"key": os.getenv("TELEGRAM_CLIENT_SECRET", ""),
},
}
# Настройки JWT
JWT_SECRET = os.getenv("JWT_SECRET", "your-secret-key")
JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 30
JWT_REFRESH_TOKEN_EXPIRE_DAYS = int(environ.get("JWT_REFRESH_TOKEN_EXPIRE_DAYS", "30"))
# Настройки для HTTP cookies (используется в auth middleware)
SESSION_COOKIE_NAME = "session_token"
# 🔒 Автоматически определяем HTTPS на основе окружения
SESSION_COOKIE_SECURE = os.getenv("HTTPS_ENABLED", "true").lower() in ["true", "1", "yes"]
SESSION_COOKIE_HTTPONLY = True
# 🌐 Для cross-origin SSE на поддоменах
SESSION_COOKIE_DOMAIN = os.getenv("SESSION_COOKIE_DOMAIN", ".discours.io") # ✅ Работает для всех поддоменов
# ✅ Типобезопасная настройка SameSite для cross-origin
_samesite_env = os.getenv("SESSION_COOKIE_SAMESITE", "none")
SESSION_COOKIE_SAMESITE: Literal["strict", "lax", "none"] = cast(
Literal["strict", "lax", "none"], _samesite_env if _samesite_env in ["strict", "lax", "none"] else "none"
)
SESSION_COOKIE_MAX_AGE = 30 * 24 * 60 * 60 # 30 дней
MAILGUN_API_KEY = os.getenv("MAILGUN_API_KEY", "")
MAILGUN_DOMAIN = os.getenv("MAILGUN_DOMAIN", "discours.io")
# Search service configuration
SEARCH_MAX_BATCH_SIZE = int(os.environ.get("SEARCH_MAX_BATCH_SIZE", "25"))
SEARCH_CACHE_ENABLED = bool(os.environ.get("SEARCH_CACHE_ENABLED", "true").lower() in ["true", "1", "yes"])
SEARCH_CACHE_TTL_SECONDS = int(os.environ.get("SEARCH_CACHE_TTL_SECONDS", "300"))
SEARCH_PREFETCH_SIZE = int(os.environ.get("SEARCH_PREFETCH_SIZE", "200"))
MUVERA_INDEX_NAME = "discours"
# 🎯 Search model selection: "biencoder" (default) or "colbert" (better quality)
# ColBERT дает +175% recall но медленнее на ~50ms per query
SEARCH_MODEL_TYPE = os.environ.get("SEARCH_MODEL_TYPE", "colbert").lower() # "biencoder" | "colbert"
# 🚀 FAISS acceleration for large indices (>10K documents)
# Двухэтапный поиск: FAISS prefilter → TRUE MaxSim на кандидатах
SEARCH_USE_FAISS = os.environ.get("SEARCH_USE_FAISS", "true").lower() in ["true", "1", "yes"]
SEARCH_FAISS_CANDIDATES = int(os.environ.get("SEARCH_FAISS_CANDIDATES", "1000")) # Кандидатов для rerank