Update 9 files

- /rag/config_models.py
- /rag/utils/config_loader.py
- /rag/auth.py
- /rag/rag_server.py
- /rag/rag_api.py
- /rag/rag_orchestrator.py
- /rag/__init__.py
- /bots/xmpp/client.py
- /bots/rag_client.py
This commit is contained in:
Markov Andrey
2026-06-30 14:27:34 +00:00
parent 068504c988
commit 59feffc190
9 changed files with 180 additions and 179 deletions

View File

@@ -2,7 +2,6 @@
"""
Главный оркестратор RAG-пайплайна (фасад).
Координирует работу менеджеров: HistoryManager, IntentRouter, QueryProcessor, IndexingManager.
Принимает запрос пользователя, обрабатывает его и возвращает ответ.
"""
import logging
@@ -29,7 +28,6 @@ class RAGOrchestrator:
"""
Оркестратор RAG-пайплайна.
Содержит ссылки на все сервисы и менеджеры.
Предоставляет два основных метода: process_query и index_document.
"""
def __init__(
@@ -43,19 +41,6 @@ class RAGOrchestrator:
config: AppConfig,
default_prompts: Optional[Dict[str, str]] = None
):
"""
Инициализация оркестратора.
Аргументы:
db: сервис PostgreSQL
qdrant: сервис Qdrant
embedding: сервис эмбеддингов
kb: сервис базы знаний
giga: клиент GigaChat
files: сервис файлов
config: объект конфигурации (AppConfig)
default_prompts: словарь промптов по умолчанию
"""
self.db = db
self.qdrant = qdrant
self.embedding = embedding
@@ -97,10 +82,6 @@ class RAGOrchestrator:
logger.info("RAGOrchestrator инициализирован с менеджерами")
# ------------------------------------------------------------------
# Вспомогательный метод для расчёта токенов
# ------------------------------------------------------------------
def _prepare_prompt_parts(
self,
synthesis_template: str,
@@ -110,17 +91,6 @@ class RAGOrchestrator:
reserved_for_answer: int = 1000,
reserved_for_overhead: int = 200
) -> Dict[str, Any]:
"""
Подсчитывает токены в статичных частях промта и вычисляет,
сколько токенов остаётся для истории и контекста.
Возвращает словарь с ключами:
- available_for_history_and_context: int
- prompt_tokens: int
- system_tokens: int
- synthesis_tokens: int
- query_tokens: int
"""
system_tokens = count_tokens(system_prompt) if system_prompt else 0
synthesis_tokens = count_tokens(synthesis_template)
query_tokens = count_tokens(query)
@@ -142,10 +112,6 @@ class RAGOrchestrator:
"query_tokens": query_tokens,
}
# ------------------------------------------------------------------
# Основной метод обработки запроса
# ------------------------------------------------------------------
async def process_query(
self,
query: str,
@@ -156,25 +122,6 @@ class RAGOrchestrator:
last_file_path: Optional[str] = None,
last_file_text: Optional[str] = None,
) -> Dict[str, Any]:
"""
Основной метод обработки запроса.
ИСТОРИЯ ДИАЛОГА ПОЛУЧАЕТСЯ ИЗ БД, а не из запроса.
Это обеспечивает единый контекст для всех клиентов.
Аргументы:
query: текст запроса пользователя
user_jid: JID пользователя (без ресурса)
room_jid: JID комнаты (None для личного чата)
prompts: словарь промптов (если None, используются default_prompts)
intent_override: принудительное переопределение намерения
last_file_path: путь к последнему загруженному файлу
last_file_text: текст последнего загруженного файла
Возвращает:
Словарь с ключами: answer, intent, context, sources, confidence, error
"""
# 1. Подготовка промптов
if prompts is None:
prompts = self.default_prompts.copy()
synthesis_template = prompts.get('synthesis', '')
@@ -182,7 +129,6 @@ class RAGOrchestrator:
synthesis_template = "{context}\n\n{query}\n\nОтвет:"
system_prompt = prompts.get('system', None)
# 2. Расчёт лимитов токенов
max_model_tokens = self.config.max_model_tokens
reserved_for_answer = self.config.reserved_for_answer_tokens
reserved_for_overhead = self.config.reserved_for_overhead_tokens
@@ -198,10 +144,8 @@ class RAGOrchestrator:
available_for_history_and_context = token_info["available_for_history_and_context"]
logger.debug(f"Доступно для истории и контекста: {available_for_history_and_context} токенов")
# 3. Получение истории из БД
raw_history = await self.history_manager.get_history(user_jid, room_jid, limit=100)
# 4. Сжатие истории, если она слишком длинная
max_history_tokens = min(available_for_history_and_context // 2, 2000)
formatted_history = await self.history_manager.compress_history_if_needed(
raw_history,
@@ -209,7 +153,6 @@ class RAGOrchestrator:
prompt_template=prompts.get('hierarchical_summary', '')
)
# 5. Классификация намерений
intent = intent_override
if intent is None and self.config.enable_intent_classification:
intent_prompt = prompts.get('intent', '')
@@ -225,13 +168,11 @@ class RAGOrchestrator:
else:
intent = intent or "GENERAL"
# 6. Принудительная установка SURGICAL по ключевым словам
keywords = self.config.surgical_keywords
if any(kw in query.lower() for kw in keywords) and last_file_path:
intent = "SURGICAL"
logger.info(f"Принудительный Intent: SURGICAL (есть файл и ключевые слова)")
# 7. Маршрутизация специализированных намерений
router_result = await self.intent_router.route(
intent=intent,
query=query,
@@ -249,12 +190,10 @@ class RAGOrchestrator:
sources = []
if router_result is not None:
# Намерение обработано маршрутизатором
answer = router_result.get("answer")
context = router_result.get("context", "")
sources = router_result.get("sources", [])
else:
# Обычный RAG-пайплайн (GENERAL, FACT, PROCEDURE, COMPARISON, CALCULATION)
history_tokens = sum(count_tokens(msg['content']) for msg in formatted_history)
available_for_context = available_for_history_and_context - history_tokens
available_for_context = max(available_for_context, 0)
@@ -273,12 +212,10 @@ class RAGOrchestrator:
context = processor_result.get("context", "")
sources = processor_result.get("sources", [])
# 8. Сохранение истории диалога в БД
await self.history_manager.save_message(user_jid, "user", query, room_jid)
if answer:
await self.history_manager.save_message(user_jid, "assistant", answer, room_jid)
# 9. Формирование результата
return {
"answer": answer or "⚠️ Не удалось сгенерировать ответ.",
"intent": intent,
@@ -288,10 +225,6 @@ class RAGOrchestrator:
"error": None
}
# ------------------------------------------------------------------
# Индексация документа
# ------------------------------------------------------------------
async def index_document(
self,
file_name: str,
@@ -304,24 +237,6 @@ class RAGOrchestrator:
file_hash: Optional[str] = None,
update_if_exists: bool = True
) -> Dict[str, Any]:
"""
Индексирует документ в базу знаний.
Делегирует работу IndexingManager.
Аргументы:
file_name: исходное имя файла
file_text: извлечённый текст
user_jid: JID владельца
room_jid: JID комнаты (None для личного)
is_global: глобальный ли документ
title: отображаемое название
metadata: дополнительные метаданные
file_hash: SHA-256 хеш содержимого
update_if_exists: заменять ли существующий документ в комнате
Возвращает:
Словарь с ключами doc_id, chunk_count, error
"""
try:
doc_id, chunk_count = await self.indexing_manager.index_document(
file_name=file_name,