Update 3 files
- /rag/intent_router.py - /rag/query_processor.py - /rag/history_manager.py
This commit is contained in:
@@ -9,14 +9,14 @@ import logging
|
||||
import re
|
||||
from typing import Optional, Dict, List, Any
|
||||
|
||||
from .services.giga_client import GigaClient
|
||||
from .services.kb_service import KBService
|
||||
from .functions.expand_query import expand_query
|
||||
from .functions.rerank_context import rerank_context
|
||||
from .functions.critique_answer import critique_answer
|
||||
from .utils.text_utils import count_tokens
|
||||
from .prompt_builder import PromptBuilder
|
||||
from .config_models import AppConfig
|
||||
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 core.prompt_builder import PromptBuilder
|
||||
from core.config_models import AppConfig
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -32,12 +32,12 @@ class QueryProcessor:
|
||||
kb: KBService,
|
||||
config: AppConfig,
|
||||
default_prompts: Optional[Dict[str, str]] = None
|
||||
):
|
||||
self.giga = giga
|
||||
self.kb = kb
|
||||
self.config = config
|
||||
self.default_prompts = default_prompts or {}
|
||||
self.prompt_builder = PromptBuilder(config)
|
||||
) -> None:
|
||||
self.giga: GigaClient = giga
|
||||
self.kb: KBService = kb
|
||||
self.config: AppConfig = config
|
||||
self.default_prompts: Dict[str, str] = default_prompts or {}
|
||||
self.prompt_builder: PromptBuilder = PromptBuilder(config)
|
||||
|
||||
async def process(
|
||||
self,
|
||||
@@ -52,18 +52,31 @@ class QueryProcessor:
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Выполняет полный RAG-пайплайн для обычного запроса.
|
||||
|
||||
Аргументы:
|
||||
query: текст запроса пользователя
|
||||
user_jid: JID пользователя
|
||||
room_jid: JID комнаты (None для личного чата)
|
||||
prompts: словарь промптов (expand, synthesis, critique, ...)
|
||||
intent: код намерения (для выбора стратегии)
|
||||
history: история диалога (уже сжатая, если нужно)
|
||||
system_prompt: системный промпт
|
||||
available_tokens_for_context: сколько токенов доступно для контекста
|
||||
|
||||
Возвращает:
|
||||
Словарь с ключами 'answer', 'context', 'sources', 'confidence'
|
||||
"""
|
||||
# 1. Расширение запроса
|
||||
expanded = await expand_query(
|
||||
expanded: str = await expand_query(
|
||||
giga=self.giga,
|
||||
query=query,
|
||||
prompt_text=prompts.get('expand', ''),
|
||||
bot_config=self.config
|
||||
)
|
||||
search_query = expanded if expanded and expanded != query else query
|
||||
search_query: str = expanded if expanded and expanded != query else query
|
||||
|
||||
# 2. Поиск релевантного контекста в базе знаний
|
||||
context = await self.kb.find_relevant_info(
|
||||
context: str = await self.kb.find_relevant_info(
|
||||
search_query, user_jid, room_jid,
|
||||
top_k=getattr(self.config, 'rag_default_top_k', 30)
|
||||
)
|
||||
@@ -71,20 +84,20 @@ class QueryProcessor:
|
||||
|
||||
# 3. Обрезка контекста по токенам
|
||||
if context:
|
||||
context_tokens = count_tokens(context)
|
||||
context_tokens: int = count_tokens(context)
|
||||
if context_tokens > available_tokens_for_context:
|
||||
logger.warning(
|
||||
f"Контекст слишком длинный ({context_tokens} токенов), "
|
||||
f"обрезаем до {available_tokens_for_context}"
|
||||
)
|
||||
max_context_chars = int(available_tokens_for_context * 3.5)
|
||||
max_context_chars: int = int(available_tokens_for_context * 3.5)
|
||||
if max_context_chars > 0:
|
||||
context = context[:max_context_chars]
|
||||
else:
|
||||
context = ""
|
||||
|
||||
# 4. Переранжирование контекста (если включено и контекст достаточно длинный)
|
||||
rerank_min_length = getattr(self.config, 'rerank_min_length', 5000)
|
||||
rerank_min_length: int = getattr(self.config, 'rerank_min_length', 5000)
|
||||
if intent != "FACT" and len(context) > rerank_min_length:
|
||||
context = await rerank_context(
|
||||
bot=None,
|
||||
@@ -94,8 +107,8 @@ class QueryProcessor:
|
||||
bot_config=self.config
|
||||
)
|
||||
|
||||
# 5. Формирование промта с помощью PromptBuilder (динамические few-shot, сэндвич)
|
||||
extra_instructions = ""
|
||||
# 5. Формирование промта с помощью PromptBuilder
|
||||
extra_instructions: str = ""
|
||||
if intent in ("CALCULATION", "PROCEDURE"):
|
||||
extra_instructions = (
|
||||
"Пожалуйста, покажи пошаговое решение перед итоговым ответом. "
|
||||
@@ -103,13 +116,11 @@ class QueryProcessor:
|
||||
"После всех шагов дай итоговый ответ."
|
||||
)
|
||||
|
||||
# Если synthesis_template задан, добавим его в extra_instructions (но PromptBuilder уже использует стандартные инструкции)
|
||||
synthesis_template = prompts.get('synthesis', '')
|
||||
synthesis_template: str = prompts.get('synthesis', '')
|
||||
if synthesis_template and not extra_instructions:
|
||||
# Если есть кастомный шаблон, используем его как дополнительную инструкцию
|
||||
extra_instructions = synthesis_template.format(context=context, query=query) if '{context}' in synthesis_template else synthesis_template
|
||||
|
||||
prompt = self.prompt_builder.build_prompt(
|
||||
prompt: str = self.prompt_builder.build_prompt(
|
||||
query=query,
|
||||
intent=intent,
|
||||
context=context,
|
||||
@@ -119,21 +130,21 @@ class QueryProcessor:
|
||||
)
|
||||
logger.debug(f"Сформированный промт (первые 500 символов): {prompt[:500]}")
|
||||
|
||||
# 6. Генерация ответа (без отдельной передачи истории, она уже в промте)
|
||||
answer = await self.giga.chat(
|
||||
history=[], # история уже в промте
|
||||
# 6. Генерация ответа
|
||||
answer: str = await self.giga.chat(
|
||||
history=[],
|
||||
query=prompt,
|
||||
system_prompt=None, # системный промпт тоже в промте
|
||||
system_prompt=None,
|
||||
file_id=None,
|
||||
temperature=getattr(self.config, 'ai_temperature', 0.1)
|
||||
)
|
||||
|
||||
# 7. Самокритика (если включена)
|
||||
if getattr(self.config, 'enable_self_critique', False) and context:
|
||||
critique_prompt = prompts.get('critique', '')
|
||||
critique_prompt: str = prompts.get('critique', '')
|
||||
if critique_prompt:
|
||||
logger.debug("Запуск самокритики")
|
||||
is_ok = await critique_answer(
|
||||
is_ok: bool = await critique_answer(
|
||||
giga=self.giga,
|
||||
query=query,
|
||||
context=context,
|
||||
@@ -143,7 +154,6 @@ class QueryProcessor:
|
||||
)
|
||||
if not is_ok:
|
||||
logger.warning("Ответ не прошёл самокритику, перегенерация")
|
||||
# Перегенерируем с тем же промтом
|
||||
answer = await self.giga.chat(
|
||||
history=[],
|
||||
query=prompt,
|
||||
@@ -151,7 +161,6 @@ class QueryProcessor:
|
||||
file_id=None,
|
||||
temperature=getattr(self.config, 'ai_temperature', 0.1)
|
||||
)
|
||||
# Повторная проверка после перегенерации
|
||||
if not await critique_answer(
|
||||
giga=self.giga,
|
||||
query=query,
|
||||
@@ -163,7 +172,7 @@ class QueryProcessor:
|
||||
answer = "⚠️ Извините, я не уверен в точности ответа. Проверьте данные."
|
||||
|
||||
# 8. Извлечение источников
|
||||
sources = []
|
||||
sources: List[str] = []
|
||||
if context:
|
||||
for match in re.finditer(r'\[источник:\s*([^\]]+)\]', context):
|
||||
sources.append(match.group(1))
|
||||
|
||||
Reference in New Issue
Block a user