Update 6 files
- /rag/history_manager.py - /rag/query_processor.py - /rag/rag_orchestrator.py - /rag/rag_api.py - /rag/rag_server.py - /rag/__init__.py
This commit is contained in:
@@ -1,16 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Пакет core – общий код для всех компонентов платформы Эфцекабот.
|
||||
Пакет rag – RAG-ядро платформы Эфцекабот.
|
||||
Содержит:
|
||||
- RAG-сервер и оркестратор,
|
||||
- HTTP-клиент для ботов,
|
||||
- сервисы (PostgreSQL, Qdrant, GigaChat),
|
||||
- функции (классификация, суммаризация, проверка орфографии и др.),
|
||||
- обработчики XMPP-событий,
|
||||
- команды ботов,
|
||||
- фоновые воркеры,
|
||||
- утилиты.
|
||||
Используется как RAG-ядром, так и тонкими XMPP-клиентами-профилями.
|
||||
Используется как HTTP-сервис.
|
||||
"""
|
||||
|
||||
__version__ = "3.0.0"
|
||||
|
||||
@@ -13,10 +13,11 @@
|
||||
import logging
|
||||
from typing import List, Dict, Optional, Any
|
||||
|
||||
from core.services.postgres_service import PostgresService
|
||||
from core.services.giga_client import GigaClient
|
||||
from core.utils.text_utils import count_tokens
|
||||
from core.functions.hierarchical_summarize import hierarchical_summarize
|
||||
from rag.services.postgres_service import PostgresService
|
||||
from rag.services.giga_client import GigaClient
|
||||
from rag.utils.text_utils import count_tokens
|
||||
from rag.functions.hierarchical_summarize import hierarchical_summarize
|
||||
from rag.config_models import AppConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -30,7 +31,7 @@ class HistoryManager:
|
||||
self,
|
||||
db: PostgresService,
|
||||
giga: GigaClient,
|
||||
config,
|
||||
config: AppConfig,
|
||||
default_prompts: Optional[Dict[str, str]] = None
|
||||
):
|
||||
"""
|
||||
@@ -39,7 +40,7 @@ class HistoryManager:
|
||||
Аргументы:
|
||||
db: сервис PostgreSQL (для получения и сохранения истории)
|
||||
giga: клиент GigaChat (для сжатия истории)
|
||||
config: объект конфигурации (BotConfig)
|
||||
config: объект конфигурации (AppConfig)
|
||||
default_prompts: словарь промптов по умолчанию (нужен для hierarchical_summary)
|
||||
"""
|
||||
self.db = db
|
||||
@@ -47,10 +48,6 @@ class HistoryManager:
|
||||
self.config = config
|
||||
self.default_prompts = default_prompts or {}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Получение и форматирование истории
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
async def get_history(
|
||||
self,
|
||||
user_jid: str,
|
||||
@@ -116,7 +113,7 @@ class HistoryManager:
|
||||
if not prompt_template:
|
||||
# Пытаемся загрузить из файла, если он есть
|
||||
try:
|
||||
prompt_path = self.config.prompts_dir / 'hierarchical_summary.txt'
|
||||
prompt_path = self.config.prompts_dir_path / 'hierarchical_summary.txt'
|
||||
with open(prompt_path, 'r', encoding='utf-8') as f:
|
||||
prompt_template = f.read()
|
||||
except Exception as e:
|
||||
@@ -130,11 +127,11 @@ class HistoryManager:
|
||||
)
|
||||
|
||||
# Параметры сжатия из конфига
|
||||
summarization_config = getattr(self.config, 'summarization', {})
|
||||
target_tokens = summarization_config.get('target_tokens_after_summary', max_tokens)
|
||||
chunk_size = summarization_config.get('chunk_size_tokens', 500)
|
||||
max_depth = summarization_config.get('max_depth', 2)
|
||||
temperature = getattr(self.config, 'ai_temperature', 0.1)
|
||||
summarization_config = self.config.summarization
|
||||
target_tokens = summarization_config.target_tokens_after_summary
|
||||
chunk_size = summarization_config.chunk_size_tokens
|
||||
max_depth = summarization_config.max_depth
|
||||
temperature = self.config.ai.temperature
|
||||
|
||||
try:
|
||||
compressed_text = await hierarchical_summarize(
|
||||
|
||||
@@ -9,12 +9,13 @@ import logging
|
||||
import re
|
||||
from typing import Optional, Dict, List, Any
|
||||
|
||||
from core.services.giga_client import GigaClient
|
||||
from core.services.kb_service import KBService
|
||||
from core.functions.expand_query import expand_query
|
||||
from core.functions.rerank_context import rerank_context
|
||||
from core.functions.critique_answer import critique_answer
|
||||
from core.utils.text_utils import count_tokens
|
||||
from rag.services.giga_client import GigaClient
|
||||
from rag.services.kb_service import KBService
|
||||
from rag.functions.expand_query import expand_query
|
||||
from rag.functions.rerank_context import rerank_context
|
||||
from rag.functions.critique_answer import critique_answer
|
||||
from rag.utils.text_utils import count_tokens
|
||||
from rag.config_models import AppConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -28,7 +29,7 @@ class QueryProcessor:
|
||||
self,
|
||||
giga: GigaClient,
|
||||
kb: KBService,
|
||||
config,
|
||||
config: AppConfig,
|
||||
default_prompts: Optional[Dict[str, str]] = None
|
||||
):
|
||||
"""
|
||||
@@ -37,7 +38,7 @@ class QueryProcessor:
|
||||
Аргументы:
|
||||
giga: клиент GigaChat
|
||||
kb: сервис базы знаний
|
||||
config: объект конфигурации
|
||||
config: объект конфигурации (AppConfig)
|
||||
default_prompts: словарь промптов по умолчанию
|
||||
"""
|
||||
self.giga = giga
|
||||
@@ -73,18 +74,20 @@ class QueryProcessor:
|
||||
Словарь с ключами 'answer', 'context', 'sources', 'confidence'
|
||||
"""
|
||||
# 1. Расширение запроса
|
||||
expand_prompt = prompts.get('expand', '')
|
||||
expanded = await expand_query(
|
||||
giga=self.giga,
|
||||
query=query,
|
||||
prompt_text=prompts.get('expand', ''),
|
||||
prompt_text=expand_prompt,
|
||||
bot_config=self.config
|
||||
)
|
||||
search_query = expanded if expanded and expanded != query else query
|
||||
|
||||
# 2. Поиск релевантного контекста в базе знаний
|
||||
top_k = self.config.rag.default_top_k
|
||||
context = await self.kb.find_relevant_info(
|
||||
search_query, user_jid, room_jid,
|
||||
top_k=getattr(self.config, 'rag_default_top_k', 30)
|
||||
top_k=top_k
|
||||
)
|
||||
logger.info(f"Найден контекст длиной {len(context)} символов (room={room_jid})")
|
||||
|
||||
@@ -103,7 +106,7 @@ class QueryProcessor:
|
||||
context = ""
|
||||
|
||||
# 4. Переранжирование контекста (если включено и контекст достаточно длинный)
|
||||
rerank_min_length = getattr(self.config, 'rerank_min_length', 5000)
|
||||
rerank_min_length = self.config.rag.rerank_min_length
|
||||
if intent != "FACT" and len(context) > rerank_min_length:
|
||||
context = await rerank_context(
|
||||
bot=None,
|
||||
@@ -130,16 +133,17 @@ class QueryProcessor:
|
||||
logger.debug(f"Полный запрос к GigaChat (первые 500 символов): {full_query[:500]}")
|
||||
|
||||
# 6. Генерация ответа
|
||||
temperature = self.config.ai.temperature
|
||||
answer = await self.giga.chat(
|
||||
history=history,
|
||||
query=full_query,
|
||||
system_prompt=system_prompt,
|
||||
file_id=None,
|
||||
temperature=getattr(self.config, 'ai_temperature', 0.1)
|
||||
temperature=temperature
|
||||
)
|
||||
|
||||
# 7. Самокритика (если включена)
|
||||
if getattr(self.config, 'enable_self_critique', False) and context:
|
||||
if self.config.features.enable_self_critique and context:
|
||||
critique_prompt = prompts.get('critique', '')
|
||||
if critique_prompt:
|
||||
logger.debug("Запуск самокритики")
|
||||
@@ -159,7 +163,7 @@ class QueryProcessor:
|
||||
query=full_query_retry,
|
||||
system_prompt=system_prompt,
|
||||
file_id=None,
|
||||
temperature=getattr(self.config, 'ai_temperature', 0.1)
|
||||
temperature=temperature
|
||||
)
|
||||
# Повторная проверка после перегенерации
|
||||
if not await critique_answer(
|
||||
|
||||
@@ -16,11 +16,12 @@
|
||||
import logging
|
||||
from typing import Optional, Dict, List, Any
|
||||
|
||||
from core.rag_orchestrator import RAGOrchestrator
|
||||
from core.services.kb_service import KBService
|
||||
from core.services.giga_client import GigaClient
|
||||
from core.services.file_service import FileService
|
||||
from core.services.postgres_service import PostgresService
|
||||
from rag.rag_orchestrator import RAGOrchestrator
|
||||
from rag.services.kb_service import KBService
|
||||
from rag.services.giga_client import GigaClient
|
||||
from rag.services.file_service import FileService
|
||||
from rag.services.postgres_service import PostgresService
|
||||
from rag.config_models import AppConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -40,7 +41,7 @@ class RAGAPI:
|
||||
kb: KBService,
|
||||
giga: GigaClient,
|
||||
files: FileService,
|
||||
config,
|
||||
config: AppConfig,
|
||||
default_prompts: Optional[Dict[str, str]] = None
|
||||
):
|
||||
"""
|
||||
@@ -53,7 +54,7 @@ class RAGAPI:
|
||||
kb: сервис базы знаний (KBService)
|
||||
giga: клиент GigaChat (GigaClient)
|
||||
files: сервис файлов (FileService)
|
||||
config: объект конфигурации (BotConfig)
|
||||
config: объект конфигурации (AppConfig)
|
||||
default_prompts: словарь промптов по умолчанию.
|
||||
Используется, если в запросе не переданы свои промпты.
|
||||
"""
|
||||
|
||||
@@ -8,18 +8,19 @@
|
||||
import logging
|
||||
from typing import Optional, Dict, List, Any
|
||||
|
||||
from core.services.postgres_service import PostgresService
|
||||
from core.services.qdrant_service import QdrantService
|
||||
from core.services.embedding_service import EmbeddingService
|
||||
from core.services.kb_service import KBService
|
||||
from core.services.giga_client import GigaClient
|
||||
from core.services.file_service import FileService
|
||||
from core.history_manager import HistoryManager
|
||||
from core.intent_router import IntentRouter
|
||||
from core.query_processor import QueryProcessor
|
||||
from core.indexing_manager import IndexingManager
|
||||
from core.functions.intent_classify import classify_intent
|
||||
from core.utils.text_utils import count_tokens
|
||||
from rag.services.postgres_service import PostgresService
|
||||
from rag.services.qdrant_service import QdrantService
|
||||
from rag.services.embedding_service import EmbeddingService
|
||||
from rag.services.kb_service import KBService
|
||||
from rag.services.giga_client import GigaClient
|
||||
from rag.services.file_service import FileService
|
||||
from rag.history_manager import HistoryManager
|
||||
from rag.intent_router import IntentRouter
|
||||
from rag.query_processor import QueryProcessor
|
||||
from rag.indexing_manager import IndexingManager
|
||||
from rag.functions.intent_classify import classify_intent
|
||||
from rag.utils.text_utils import count_tokens
|
||||
from rag.config_models import AppConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -39,7 +40,7 @@ class RAGOrchestrator:
|
||||
kb: KBService,
|
||||
giga: GigaClient,
|
||||
files: FileService,
|
||||
config,
|
||||
config: AppConfig,
|
||||
default_prompts: Optional[Dict[str, str]] = None
|
||||
):
|
||||
"""
|
||||
@@ -52,7 +53,7 @@ class RAGOrchestrator:
|
||||
kb: сервис базы знаний
|
||||
giga: клиент GigaChat
|
||||
files: сервис файлов
|
||||
config: объект конфигурации (BotConfig)
|
||||
config: объект конфигурации (AppConfig)
|
||||
default_prompts: словарь промптов по умолчанию
|
||||
"""
|
||||
self.db = db
|
||||
@@ -182,9 +183,9 @@ class RAGOrchestrator:
|
||||
system_prompt = prompts.get('system', None)
|
||||
|
||||
# 2. Расчёт лимитов токенов
|
||||
max_model_tokens = getattr(self.config, 'max_model_tokens', 8192)
|
||||
reserved_for_answer = getattr(self.config, 'reserved_for_answer_tokens', 1000)
|
||||
reserved_for_overhead = getattr(self.config, 'reserved_for_overhead_tokens', 200)
|
||||
max_model_tokens = self.config.rag.max_model_tokens
|
||||
reserved_for_answer = self.config.rag.reserved_for_answer_tokens
|
||||
reserved_for_overhead = self.config.rag.reserved_for_overhead_tokens
|
||||
|
||||
token_info = self._prepare_prompt_parts(
|
||||
synthesis_template=synthesis_template,
|
||||
@@ -210,7 +211,7 @@ class RAGOrchestrator:
|
||||
|
||||
# 5. Классификация намерений
|
||||
intent = intent_override
|
||||
if intent is None and getattr(self.config, 'enable_intent_classification', True):
|
||||
if intent is None and self.config.features.enable_intent_classification:
|
||||
intent_prompt = prompts.get('intent', '')
|
||||
if intent_prompt:
|
||||
intent = await classify_intent(
|
||||
@@ -225,7 +226,7 @@ class RAGOrchestrator:
|
||||
intent = intent or "GENERAL"
|
||||
|
||||
# 6. Принудительная установка SURGICAL по ключевым словам
|
||||
keywords = getattr(self.config, 'surgical_keywords', [])
|
||||
keywords = self.config.features.surgical_keywords
|
||||
if any(kw in query.lower() for kw in keywords) and last_file_path:
|
||||
intent = "SURGICAL"
|
||||
logger.info(f"Принудительный Intent: SURGICAL (есть файл и ключевые слова)")
|
||||
|
||||
@@ -24,7 +24,7 @@ import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, List, Any
|
||||
|
||||
# Добавляем путь к core, если запускаем из корня проекта
|
||||
# Добавляем путь к rag, если запускаем из корня проекта
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Depends, UploadFile, File as FastAPIFile
|
||||
@@ -32,16 +32,16 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from pydantic import BaseModel, Field
|
||||
import uvicorn
|
||||
|
||||
# Импортируем наши модули
|
||||
from core.utils.config_loader import BotConfig
|
||||
from core.utils.logger import setup_logging
|
||||
from core.services.postgres_service import PostgresService
|
||||
from core.services.qdrant_service import QdrantService
|
||||
from core.services.embedding_service import EmbeddingService
|
||||
from core.services.kb_service import KBService
|
||||
from core.services.giga_client import GigaClient
|
||||
from core.services.file_service import FileService
|
||||
from core.rag_orchestrator import RAGOrchestrator
|
||||
# Импортируем наши модули (теперь из пакета rag)
|
||||
from rag.utils.config_loader import load_config
|
||||
from rag.utils.logger import setup_logging
|
||||
from rag.services.postgres_service import PostgresService
|
||||
from rag.services.qdrant_service import QdrantService
|
||||
from rag.services.embedding_service import EmbeddingService
|
||||
from rag.services.kb_service import KBService
|
||||
from rag.services.giga_client import GigaClient
|
||||
from rag.services.file_service import FileService
|
||||
from rag.rag_orchestrator import RAGOrchestrator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -58,14 +58,10 @@ class QueryRequest(BaseModel):
|
||||
query: str = Field(..., description="Текст запроса пользователя")
|
||||
user_jid: str = Field(..., description="JID пользователя (без ресурса)")
|
||||
room_jid: Optional[str] = Field(None, description="JID комнаты (None для личного чата)")
|
||||
|
||||
# history: List[Dict[str, str]] = Field(...) # УБРАНО!
|
||||
|
||||
prompts: Dict[str, str] = Field(
|
||||
default_factory=dict,
|
||||
description="Словарь промптов (system, synthesis, intent, expand, critique и т.д.)"
|
||||
)
|
||||
|
||||
intent_override: Optional[str] = Field(None, description="Принудительное переопределение намерения")
|
||||
last_file_path: Optional[str] = Field(None, description="Путь к последнему загруженному файлу")
|
||||
last_file_text: Optional[str] = Field(None, description="Текст последнего загруженного файла")
|
||||
@@ -118,7 +114,7 @@ class TranscribeResponse(BaseModel):
|
||||
# ============================================================
|
||||
|
||||
_orchestrator: Optional[RAGOrchestrator] = None
|
||||
_config: Optional[BotConfig] = None
|
||||
_config: Optional[AppConfig] = None
|
||||
|
||||
|
||||
def get_orchestrator() -> RAGOrchestrator:
|
||||
@@ -129,7 +125,7 @@ def get_orchestrator() -> RAGOrchestrator:
|
||||
return _orchestrator
|
||||
|
||||
|
||||
def init_orchestrator(config: BotConfig) -> RAGOrchestrator:
|
||||
def init_orchestrator(config: AppConfig) -> RAGOrchestrator:
|
||||
"""Инициализирует все сервисы и создаёт RAGOrchestrator."""
|
||||
global _orchestrator, _config
|
||||
_config = config
|
||||
@@ -138,31 +134,31 @@ def init_orchestrator(config: BotConfig) -> RAGOrchestrator:
|
||||
# ---- 1. Инициализация сервисов ----
|
||||
# PostgreSQL (история, документы)
|
||||
db = PostgresService(
|
||||
host=config.db_host,
|
||||
port=config.db_port,
|
||||
user=config.db_user,
|
||||
host=config.database.host,
|
||||
port=config.database.port,
|
||||
user=config.database.user,
|
||||
password=config.db_password,
|
||||
db_name=config.db_name
|
||||
db_name=config.database.database
|
||||
)
|
||||
|
||||
# Qdrant (векторный поиск)
|
||||
qdrant = QdrantService(
|
||||
host=config.qdrant_host,
|
||||
port=config.qdrant_port,
|
||||
grpc_port=config.qdrant_grpc_port,
|
||||
collection_name=config.qdrant_collection,
|
||||
vector_size=config.qdrant_vector_size,
|
||||
distance=config.qdrant_distance,
|
||||
host=config.qdrant.host,
|
||||
port=config.qdrant.port,
|
||||
grpc_port=config.qdrant.grpc_port,
|
||||
collection_name=config.qdrant.collection,
|
||||
vector_size=config.qdrant.vector_size,
|
||||
distance=config.qdrant.distance,
|
||||
prefer_grpc=False
|
||||
)
|
||||
|
||||
# Эмбеддинги (GigaChat)
|
||||
embedding = EmbeddingService(
|
||||
api_key=config.gigachat_api_key,
|
||||
model=config.embedding_model,
|
||||
timeout=config.embedding_timeout,
|
||||
cache_size=config.embedding_cache_size,
|
||||
verify_ssl=config.embedding_verify_ssl
|
||||
model=config.embedding.model,
|
||||
timeout=config.embedding.timeout,
|
||||
cache_size=config.embedding.cache_size,
|
||||
verify_ssl=config.embedding.verify_ssl_certs
|
||||
)
|
||||
|
||||
# База знаний (индексация, поиск)
|
||||
@@ -170,19 +166,19 @@ def init_orchestrator(config: BotConfig) -> RAGOrchestrator:
|
||||
db=db,
|
||||
qdrant=qdrant,
|
||||
embedding=embedding,
|
||||
collection_name=config.qdrant_collection,
|
||||
chunk_size_tokens=config.chunk_size_tokens,
|
||||
overlap_tokens=config.overlap_tokens,
|
||||
approx_chunk_chars=config.chunking_approx_chunk_chars,
|
||||
approx_overlap_chars=config.chunking_approx_overlap_chars
|
||||
collection_name=config.qdrant.collection,
|
||||
chunk_size_tokens=config.chunking.chunk_size_tokens,
|
||||
overlap_tokens=config.chunking.overlap_tokens,
|
||||
approx_chunk_chars=config.chunking.approx_chunk_chars,
|
||||
approx_overlap_chars=config.chunking.approx_overlap_chars
|
||||
)
|
||||
|
||||
# GigaChat клиент (генерация)
|
||||
giga = GigaClient(
|
||||
api_key=config.gigachat_api_key,
|
||||
model=config.ai_model,
|
||||
temperature=config.ai_temperature,
|
||||
timeout=config.ai_timeout,
|
||||
model=config.gigachat.model_generation,
|
||||
temperature=config.gigachat.temperature,
|
||||
timeout=config.gigachat.timeout,
|
||||
verify_ssl=False
|
||||
)
|
||||
|
||||
@@ -190,15 +186,11 @@ def init_orchestrator(config: BotConfig) -> RAGOrchestrator:
|
||||
files = FileService(config)
|
||||
|
||||
# ---- 2. Создание оркестратора ----
|
||||
# Загружаем дефолтные промпты (опционально)
|
||||
default_prompts = {}
|
||||
system_prompt_path = getattr(config, 'system_prompt_file', None)
|
||||
if system_prompt_path and system_prompt_path.exists():
|
||||
try:
|
||||
with open(system_prompt_path, 'r', encoding='utf-8') as f:
|
||||
default_prompts['system'] = f.read()
|
||||
except Exception as e:
|
||||
logger.warning(f"Не удалось загрузить системный промпт: {e}")
|
||||
# Загружаем дефолтные промпты (из config.prompts_content)
|
||||
default_prompts = config.prompts_content or {}
|
||||
system_prompt = default_prompts.get('system')
|
||||
if system_prompt:
|
||||
default_prompts['system'] = system_prompt
|
||||
|
||||
orchestrator = RAGOrchestrator(
|
||||
db=db,
|
||||
@@ -444,16 +436,18 @@ def main():
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Загружаем конфигурацию
|
||||
# Загружаем конфигурацию с помощью новой функции load_config
|
||||
try:
|
||||
config = BotConfig(args.profile_dir)
|
||||
config = load_config(args.profile_dir)
|
||||
except Exception as e:
|
||||
print(f"Ошибка загрузки конфигурации: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
# Настраиваем логирование
|
||||
log_level = getattr(logging, args.log_level.upper(), logging.INFO)
|
||||
setup_logging(config.name, config.log_file, log_level=log_level)
|
||||
# Передаём путь к логу из конфига
|
||||
log_file_path = config.log_file_path or Path("/tmp/rag_server.log")
|
||||
setup_logging(config.name, log_file_path, log_level=log_level)
|
||||
|
||||
logger.info(f"Запуск RAG-сервера с профилем {args.profile_dir}")
|
||||
logger.info(f"Хост: {args.host}, порт: {args.port}")
|
||||
@@ -467,7 +461,7 @@ def main():
|
||||
|
||||
# Запускаем Uvicorn
|
||||
uvicorn.run(
|
||||
"core.rag_server:app",
|
||||
"rag.rag_server:app", # теперь модуль из пакета rag
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
log_level=args.log_level,
|
||||
|
||||
Reference in New Issue
Block a user