feat: интеграция ML-скоринга с использованием RAG и DeepSeek

- Обновлен Dockerfile для установки необходимых зависимостей.
- Добавлены новые переменные окружения для настройки ML-скоринга в env.example.
- Реализованы методы для получения и обновления ML-скоров в AsyncBotDB и PostRepository.
- Обновлены обработчики публикации постов для интеграции ML-скоринга.
- Добавлен новый обработчик для получения статистики ML-скоринга в админ-панели.
- Обновлены функции для форматирования сообщений с учетом ML-скоров.
This commit is contained in:
2026-01-26 18:40:38 +03:00
parent e2b1353408
commit 7f6f0f028c
25 changed files with 2833 additions and 52 deletions

View File

@@ -5,6 +5,7 @@ from typing import Optional
from database.async_db import AsyncBotDB
from dotenv import load_dotenv
from helper_bot.utils.s3_storage import S3StorageService
from logs.custom_logger import logger
class BaseDependencyFactory:
@@ -15,6 +16,7 @@ class BaseDependencyFactory:
load_dotenv(env_path)
self.settings = {}
self._project_dir = project_dir
database_path = os.getenv('DATABASE_PATH', 'database/tg-bot-database.db')
if not os.path.isabs(database_path):
@@ -24,6 +26,9 @@ class BaseDependencyFactory:
self._load_settings_from_env()
self._init_s3_storage()
# ScoringManager инициализируется лениво
self._scoring_manager = None
def _load_settings_from_env(self):
"""Загружает настройки из переменных окружения."""
@@ -59,6 +64,23 @@ class BaseDependencyFactory:
'bucket_name': os.getenv('S3_BUCKET_NAME', ''),
'region': os.getenv('S3_REGION', 'us-east-1')
}
# Настройки ML-скоринга
self.settings['Scoring'] = {
# RAG (ruBERT)
'rag_enabled': self._parse_bool(os.getenv('RAG_ENABLED', 'false')),
'rag_model': os.getenv('RAG_MODEL', 'DeepPavlov/rubert-base-cased'),
'rag_cache_dir': os.getenv('RAG_CACHE_DIR', 'data/models'),
'rag_vectors_path': os.getenv('RAG_VECTORS_PATH', 'data/vectors.npz'),
'rag_max_examples': self._parse_int(os.getenv('RAG_MAX_EXAMPLES', '10000')),
'rag_score_multiplier': self._parse_float(os.getenv('RAG_SCORE_MULTIPLIER', '5.0')),
# DeepSeek
'deepseek_enabled': self._parse_bool(os.getenv('DEEPSEEK_ENABLED', 'false')),
'deepseek_api_key': os.getenv('DEEPSEEK_API_KEY', ''),
'deepseek_api_url': os.getenv('DEEPSEEK_API_URL', 'https://api.deepseek.com/v1/chat/completions'),
'deepseek_model': os.getenv('DEEPSEEK_MODEL', 'deepseek-chat'),
'deepseek_timeout': self._parse_int(os.getenv('DEEPSEEK_TIMEOUT', '30')),
}
def _init_s3_storage(self):
"""Инициализирует S3StorageService если S3 включен."""
@@ -84,6 +106,13 @@ class BaseDependencyFactory:
return int(value)
except (ValueError, TypeError):
return 0
def _parse_float(self, value: str) -> float:
"""Парсит строковое значение в float."""
try:
return float(value)
except (ValueError, TypeError):
return 0.0
def get_settings(self):
return self.settings
@@ -95,6 +124,100 @@ class BaseDependencyFactory:
def get_s3_storage(self) -> Optional[S3StorageService]:
"""Возвращает S3StorageService если S3 включен, иначе None."""
return self.s3_storage
def _init_scoring_manager(self):
"""
Инициализирует ScoringManager с RAG и DeepSeek сервисами.
Вызывается лениво при первом обращении к get_scoring_manager().
"""
from helper_bot.services.scoring import (
ScoringManager,
RAGService,
DeepSeekService,
VectorStore,
)
scoring_config = self.settings['Scoring']
# Инициализация RAG сервиса
rag_service = None
if scoring_config['rag_enabled']:
# Путь к векторам
vectors_path = scoring_config['rag_vectors_path']
if not os.path.isabs(vectors_path):
vectors_path = os.path.join(self._project_dir, vectors_path)
# Путь к кешу моделей
cache_dir = scoring_config['rag_cache_dir']
if not os.path.isabs(cache_dir):
cache_dir = os.path.join(self._project_dir, cache_dir)
# Создаем директории если нужно
os.makedirs(os.path.dirname(vectors_path), exist_ok=True)
os.makedirs(cache_dir, exist_ok=True)
# Создаем VectorStore
vector_store = VectorStore(
vector_dim=768, # ruBERT dimension
max_examples=scoring_config['rag_max_examples'],
storage_path=vectors_path,
score_multiplier=scoring_config['rag_score_multiplier'],
)
# Создаем RAGService
rag_service = RAGService(
model_name=scoring_config['rag_model'],
vector_store=vector_store,
cache_dir=cache_dir,
enabled=True,
)
logger.info(f"RAGService инициализирован: {scoring_config['rag_model']}")
# Инициализация DeepSeek сервиса
deepseek_service = None
if scoring_config['deepseek_enabled'] and scoring_config['deepseek_api_key']:
deepseek_service = DeepSeekService(
api_key=scoring_config['deepseek_api_key'],
api_url=scoring_config['deepseek_api_url'],
model=scoring_config['deepseek_model'],
timeout=scoring_config['deepseek_timeout'],
enabled=True,
)
logger.info(f"DeepSeekService инициализирован: {scoring_config['deepseek_model']}")
# Создаем менеджер
self._scoring_manager = ScoringManager(
rag_service=rag_service,
deepseek_service=deepseek_service,
)
return self._scoring_manager
def get_scoring_manager(self):
"""
Возвращает ScoringManager для ML-скоринга постов.
Инициализируется лениво при первом вызове.
Returns:
ScoringManager или None если скоринг полностью отключен
"""
if self._scoring_manager is None:
scoring_config = self.settings.get('Scoring', {})
# Проверяем, включен ли хотя бы один сервис
rag_enabled = scoring_config.get('rag_enabled', False)
deepseek_enabled = scoring_config.get('deepseek_enabled', False)
if not rag_enabled and not deepseek_enabled:
logger.info("Scoring полностью отключен (RAG и DeepSeek disabled)")
return None
self._init_scoring_manager()
return self._scoring_manager
_global_instance = None