Spaces:
Sleeping
Sleeping
| from pydantic import BaseModel, Field | |
| from typing import List, Dict, Optional | |
| from langchain_core.prompts import ChatPromptTemplate | |
| from langchain_core.prompts.chat import SystemMessagePromptTemplate, HumanMessagePromptTemplate | |
| from langchain_core.output_parsers import JsonOutputParser | |
| from langchain_core.runnables import RunnablePassthrough | |
| from langchain_core.exceptions import OutputParserException | |
| from pydantic import ValidationError | |
| from src.rag.retriever import Retriever | |
| from src.rag.llm import get_model | |
| from src.rag.question_enricher import QuestionEnricher | |
| from src.config import LLM_API_KEY, LLM, CHAT_HISTORY_LENGTH, ENABLE_QUESTION_ENRICHMENT | |
| class LLMResponse(BaseModel): | |
| answer: str = Field(..., min_length=1, description="Прямой точный ответ на вопрос") | |
| reason: str = Field(..., min_length=1, description="Объяснение, почему ответ именно такой") | |
| class RAG: | |
| def __init__(self, embed_model_name: str, embed_index_name: str): | |
| self.retriever = Retriever(embed_model_name, embed_index_name) | |
| self.parser = JsonOutputParser(pydantic_object=LLMResponse) | |
| self.llm = get_model(LLM_API_KEY, LLM) | |
| self.history_length = CHAT_HISTORY_LENGTH | |
| self.enable_enrichment = ENABLE_QUESTION_ENRICHMENT | |
| self.prompt = ChatPromptTemplate.from_messages([ | |
| SystemMessagePromptTemplate.from_template( | |
| "Ты полезный и точный ассистент. " | |
| "Ответь на вопрос, опираясь ТОЛЬКО на предложенный контекст. " | |
| "Если в контексте нет ответа, ответь \"Не знаю.\"" | |
| ), | |
| HumanMessagePromptTemplate.from_template( | |
| "{format_instructions}\n\n" | |
| "Контекст:\n{context}\n\n" | |
| "Вопрос: {question}" | |
| ), | |
| ]) | |
| # Initialize question enricher if enabled | |
| if self.enable_enrichment: | |
| self.question_enricher = QuestionEnricher() | |
| else: | |
| self.question_enricher = None | |
| def invoke(self, query: str, history: Optional[List[Dict]] = None): | |
| """ | |
| Invoke RAG with optional chat history | |
| Args: | |
| query: User question | |
| history: List of previous messages [{"query": "...", "answer": "..."}, ...] | |
| """ | |
| try: | |
| # Enrich question with context from history if enabled | |
| enriched_query = query | |
| if self.enable_enrichment and self.question_enricher and history: | |
| # Use last N messages for enrichment | |
| recent_history = history[-self.history_length:] if len(history) > self.history_length else history | |
| enriched_query = self.question_enricher.enrich(query, recent_history) | |
| # Get context from retriever using enriched query | |
| context = self.retriever.chain.invoke(enriched_query) | |
| # Build chain | |
| chain = ( | |
| self.prompt | |
| | self.llm | |
| | self.parser | |
| ) | |
| # Invoke with enriched question | |
| result = chain.invoke({ | |
| "context": context, | |
| "question": enriched_query, # Use enriched question | |
| "format_instructions": self.parser.get_format_instructions(), | |
| }) | |
| return result | |
| except (OutputParserException, ValidationError) as e: | |
| return LLMResponse( | |
| answer="Не знаю.", | |
| reason="Модель не смогла вернуть ответ в корректном формате." | |
| ) | |