Spaces:
Running
Running
File size: 13,786 Bytes
9472cf1 703e66d 9472cf1 703e66d 9472cf1 703e66d 9472cf1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 | # 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
@dataclass
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,
}
@dataclass
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()
|