Spaces:
Running
Running
| # type: ignore | |
| """ | |
| ================================================================================ | |
| IMPROVED CONTEXT HANDLER - Melhor gerenciamento de contexto para Akira | |
| ================================================================================ | |
| IMPORTANTE: Este módulo NÃO modifica context_builder.py ou contexto.py! | |
| Ele adiciona uma camada INTELIGENTE de análise de contexto para perguntas curtas. | |
| Função: Resolver o problema de perguntas curtas ("Oq é isso?") perdendo contexto | |
| Preserva: Toda a arquitetura e lógica existente do sistema de contexto | |
| ================================================================================ | |
| """ | |
| import re | |
| from typing import Dict, List, Optional, Tuple, Any | |
| from dataclasses import dataclass | |
| try: | |
| from . import config | |
| except ImportError: | |
| import modules.config as config | |
| class ContextWeights: | |
| """Pesos calculados para diferentes tipos de contexto.""" | |
| reply_context: float = 0.0 | |
| quoted_analysis: float = 0.0 | |
| short_term_memory: float = 1.0 | |
| vector_memory: float = 0.7 | |
| def to_dict(self) -> Dict[str, float]: | |
| """Converte para dicionário.""" | |
| return { | |
| "reply_context": self.reply_context, | |
| "quoted_analysis": self.quoted_analysis, | |
| "short_term_memory": self.short_term_memory, | |
| "vector_memory": self.vector_memory, | |
| } | |
| class QuestionAnalysis: | |
| """Análise de uma pergunta.""" | |
| is_short:bool = False # <= 5 palavras | |
| is_very_short: bool = False # <= 2 palavras | |
| has_pronoun: bool = False # tem "isso", "aquilo", "ele", etc | |
| has_reply: bool = False | |
| needs_context: bool = False # precisa de contexto extra | |
| question_type: str = "general" # "what", "how", "where", "why", "general" | |
| class ImprovedContextHandler: | |
| """ | |
| Gerenciador inteligente de contexto para perguntas curtas. | |
| IMPORTANTE: | |
| - NÃO substitui o context_builder.py existente | |
| - Funciona como HELPER para calcular pesos de contexto | |
| - AUMENTA contexto para perguntas curtas com reply (contrário da lógica antiga) | |
| """ | |
| def __init__(self): | |
| # Pronomes que indicam necessidade de contexto | |
| self.context_pronouns = { | |
| "isso", "aquilo", "este", "esse", "aquele", | |
| "ele", "ela", "eles", "elas", | |
| "la", "lo", "las", "los", # "a la", "o lo" | |
| } | |
| # Palavras interrogativas | |
| self.question_words = { | |
| "what": ["oq", "o que", "oque", "que é"], | |
| "how": ["como"], | |
| "where": ["onde", "aonde"], | |
| "when": ["quando", "que horas"], | |
| "why": ["porque", "porquê", "por que", "pq"], | |
| "who": ["quem"], | |
| } | |
| # Limites de palavras | |
| self.very_short_threshold = 2 # "Oq é?" | |
| self.short_threshold = 5 # "Como funciona isso?" | |
| def analyze_question( | |
| self, | |
| message: str, | |
| reply_metadata: Optional[Dict[str, Any]] = None | |
| ) -> QuestionAnalysis: | |
| """ | |
| Analisa uma mensagem para determinar necessidade de contexto. | |
| Args: | |
| message: Mensagem do usuário | |
| reply_metadata: Metadados de reply (se for reply) | |
| Returns: | |
| QuestionAnalysis com detalhes da análise | |
| """ | |
| message_lower = message.lower().strip() | |
| words = message_lower.split() | |
| word_count = len(words) | |
| analysis = QuestionAnalysis() | |
| # Classifica tamanho | |
| analysis.is_very_short = word_count <= self.very_short_threshold | |
| analysis.is_short = word_count <= self.short_threshold | |
| # Detecta pronomes contextuais | |
| analysis.has_pronoun = any( | |
| pronoun in message_lower | |
| for pronoun in self.context_pronouns | |
| ) | |
| # Verifica se tem reply | |
| if reply_metadata: | |
| analysis.has_reply = reply_metadata.get("is_reply", False) | |
| # Detecta tipo de pergunta | |
| for q_type, patterns in self.question_words.items(): | |
| if any(pattern in message_lower for pattern in patterns): | |
| analysis.question_type = q_type | |
| break | |
| # Determina se precisa de contexto extra | |
| analysis.needs_context = ( | |
| analysis.is_short and | |
| (analysis.has_pronoun or analysis.has_reply) | |
| ) | |
| return analysis | |
| def calculate_context_weights( | |
| self, | |
| message: str, | |
| reply_metadata: Optional[Dict[str, Any]] = None | |
| ) -> ContextWeights: | |
| """ | |
| Calcula pesos de contexto de forma inteligente. | |
| LÓGICA INVERTIDA da original: | |
| - Perguntas curtas COM reply = MAIS contexto de reply | |
| - Perguntas normais = balanço | |
| - Sem reply = contexto geral | |
| Args: | |
| message: Mensagem do usuário | |
| reply_metadata: Metadados de reply | |
| Returns: | |
| ContextWeights com pesos calculados | |
| """ | |
| analysis = self.analyze_question(message, reply_metadata) | |
| weights = ContextWeights() | |
| # CASO 1: Pergunta MUITO curta COM reply | |
| # Exemplo: "Oq é isso?" (reply a mensagem sobre Radiohead) | |
| if analysis.is_very_short and analysis.has_reply: | |
| weights.reply_context = 1.0 # ✅ MÁXIMO para reply | |
| weights.quoted_analysis = 0.95 # Analisa profundamente a citação | |
| weights.short_term_memory = 0.8 # ✅ MANTÉM texto curto + contexto | |
| weights.vector_memory = 0.3 # Fatos gerais baixo | |
| # CASO 2: Pergunta curta COM reply | |
| # Exemplo: "Como funciona isso?" (reply a explicação técnica) | |
| elif analysis.is_short and analysis.has_reply: | |
| weights.reply_context = 0.9 # Alto para reply | |
| weights.quoted_analysis = 0.85 | |
| weights.short_term_memory = 0.85 # ✅ MANTÉM texto curto no contexto | |
| weights.vector_memory = 0.4 | |
| # CASO 3: Pergunta curta COM pronome mas SEM reply | |
| # Exemplo: "Oq é isso?" (sem reply - contexto ambíguo) | |
| elif analysis.is_short and analysis.has_pronoun: | |
| weights.reply_context = 0.0 # Sem reply | |
| weights.quoted_analysis = 0.0 | |
| weights.short_term_memory = 1.0 # Usa histórico recente completo | |
| weights.vector_memory = 0.8 # Busca memória de fatos | |
| # CASO 4: Pergunta normal COM reply | |
| # Exemplo: "Você pode explicar melhor esse conceito?" (reply a explicação) | |
| elif analysis.has_reply: | |
| weights.reply_context = 0.8 | |
| weights.quoted_analysis = 0.7 | |
| weights.short_term_memory = 0.8 | |
| weights.vector_memory = 0.5 | |
| # CASO 5: Pergunta normal SEM reply | |
| # Exemplo: "Como funciona inteligência artificial?" | |
| else: | |
| weights.reply_context = 0.0 | |
| weights.quoted_analysis = 0.0 | |
| weights.short_term_memory = 1.0 | |
| weights.vector_memory = 0.7 | |
| return weights | |
| def extract_quoted_content_deep( | |
| self, | |
| reply_metadata: Dict[str, Any] | |
| ) -> str: | |
| """ | |
| Extrai conteúdo citado de forma profunda. | |
| Prioriza campos mais completos. | |
| Args: | |
| reply_metadata: Metadados do reply | |
| Returns: | |
| Conteúdo completo citado | |
| """ | |
| # Ordem de prioridade (do mais completo para o menos) | |
| priority_fields = [ | |
| "mensagem_citada", | |
| "full_message", | |
| "quoted_text_original", | |
| "quoted_text", | |
| "reply_content", | |
| "context_hint", | |
| ] | |
| for field in priority_fields: | |
| if field in reply_metadata and reply_metadata[field]: | |
| content = str(reply_metadata[field]).strip() | |
| if len(content) > 5: # Ignora conteúdos muito curtos | |
| return content | |
| # Fallback: tenta extrair de qualquer campo que pareça mensagem | |
| for key, value in reply_metadata.items(): | |
| if isinstance(value, str) and len(value) > 10: | |
| # Verifica se tem palavras comuns de mensagem | |
| if any(word in value.lower() for word in ["eu", "você", "tu", "ele"]): | |
| return value.strip() | |
| return "" | |
| def analyze_quoted_content( | |
| self, | |
| quoted_content: str, | |
| current_message: str | |
| ) -> Dict[str, Any]: | |
| """ | |
| Analisa conteúdo citado para entender o contexto. | |
| Args: | |
| quoted_content: Conteúdo da mensagem citada | |
| current_message: Mensagem atual do usuário | |
| Returns: | |
| Análise do conteúdo citado | |
| """ | |
| if not quoted_content: | |
| return {"empty": True} | |
| quoted_lower = quoted_content.lower() | |
| current_lower = current_message.lower() | |
| # Detecta tipo de conteúdo | |
| content_type = "general" | |
| if any(w in quoted_lower for w in ["?", "qual", "quando", "onde", "como", "por que"]): | |
| content_type = "question" | |
| elif any(w in quoted_lower for w in ["eu", "mim", "meu", "minha"]): | |
| content_type = "personal" | |
| elif any(w in quoted_lower for w in ["akira", "bot", "você", "vc"]): | |
| content_type = "about_bot" | |
| # Extrai keywords principais | |
| keywords = self._extract_keywords(quoted_content) | |
| # Detecta tom | |
| tone = "neutral" | |
| if any(w in quoted_lower for w in ["kkk", "haha", "😂", "🤣"]): | |
| tone = "humorous" | |
| elif any(w in quoted_lower for w in ["!!!", "???", "nossa", "eita"]): | |
| tone = "excited" | |
| # Detecta se há informação técnica/específica | |
| has_specific_info = any( | |
| word in quoted_lower | |
| for word in ["Estudo", "Academica", "Programação", "Ciência", "política", "País"] | |
| ) | |
| return { | |
| "content_type": content_type, | |
| "keywords": keywords, | |
| "tone": tone, | |
| "length": len(quoted_content), | |
| "has_question": "?" in quoted_content, | |
| "has_specific_info": has_specific_info, | |
| } | |
| def _extract_keywords(self, text: str, max_keywords: int = 5) -> List[str]: | |
| """Extrai keywords principais do texto.""" | |
| # Remove stopwords comuns | |
| stopwords = { | |
| "o", "a", "de", "da", "do", "em", "para", "com", "por", | |
| "que", "é", "um", "uma", "os", "as", "dos", "das", | |
| "e", "ou", "mas", "se", "não", "sim", | |
| } | |
| words = re.findall(r'\w+', text.lower()) | |
| keywords = [w for w in words if w not in stopwords and len(w) > 3] | |
| # Retorna os primeiros N | |
| return keywords[:max_keywords] | |
| # ============================================================ | |
| # FUNÇÕES DE CONVENIÊNCIA | |
| # ============================================================ | |
| _handler_instance: Optional[ImprovedContextHandler] = None | |
| def get_context_handler() -> ImprovedContextHandler: | |
| """Retorna instância singleton do handler.""" | |
| global _handler_instance | |
| if _handler_instance is None: | |
| _handler_instance = ImprovedContextHandler() | |
| return _handler_instance | |
| def calculate_smart_context_weights( | |
| message: str, | |
| reply_metadata: Optional[Dict[str, Any]] = None | |
| ) -> Dict[str, float]: | |
| """ | |
| Função helper para calcular pesos de contexto inteligentemente. | |
| Args: | |
| message: Mensagem do usuário | |
| reply_metadata: Metadados de reply | |
| Returns: | |
| Dict com pesos de contexto | |
| """ | |
| handler = get_context_handler() | |
| weights = handler.calculate_context_weights(message, reply_metadata) | |
| return weights.to_dict() | |
| # ============================================================ | |
| # EXEMPLO DE USO | |
| # ============================================================ | |
| if __name__ == "__main__": | |
| # Teste básico | |
| handler = ImprovedContextHandler() | |
| test_cases = [ | |
| # (mensagem, tem_reply, descrição) | |
| ("Oq é isso?", True, "Pergunta muito curta com reply"), | |
| ("Como funciona isso?", True, "Pergunta curta com reply"), | |
| ("Oq é isso?", False, "Pergunta curta SEM reply (ambígua)"), | |
| ("Você pode explicar melhor esse conceito?", True, "Pergunta normal com reply"), | |
| ("Como funciona inteligência artificial?", False, "Pergunta normal sem reply"), | |
| ] | |
| print("=== TESTE DE PESOS DE CONTEXTO ===\n") | |
| for message, has_reply, description in test_cases: | |
| print(f"Caso: {description}") | |
| print(f"Mensagem: \"{message}\"") | |
| print(f"Tem reply: {has_reply}") | |
| reply_meta = {"is_reply": has_reply} if has_reply else None | |
| weights = handler.calculate_context_weights(message, reply_meta) | |
| print(f"Pesos calculados:") | |
| print(f" - Reply context: {weights.reply_context:.2f}") | |
| print(f" - Quoted analysis: {weights.quoted_analysis:.2f}") | |
| print(f" - Short-term memory: {weights.short_term_memory:.2f}") | |
| print(f" - Vector memory: {weights.vector_memory:.2f}") | |
| print() | |