# chatbot_controller.py # Duarte Grilo - 2201320 - Projeto Informático import time import re from models.velvet_runner import generate_response, salvar_completo_em_arquivo # Geração e gravação from models.rag_engine import retrieve_context # Busca com RAG from models.tradutor_local import traduzir # Tradução PT/EN # Conta tokens de um texto (simples split por espaço) def contar_tokens(texto: str) -> int: return len(texto.split()) # Valida se a resposta contém palavras-chave do contexto def validar_resposta_por_keywords(resposta: str, contexto: str, min_match: int = 3) -> tuple[bool, int]: palavras_contexto = re.findall(r'\b\w{4,}\b', contexto.lower()) palavras_resposta = re.findall(r'\b\w{4,}\b', resposta.lower()) comuns = set(palavras_contexto).intersection(set(palavras_resposta)) return len(comuns) >= min_match, len(comuns) # Função principal que processa cada pergunta def process_user_input(question: str) -> str: print("\n🔍 [DEBUG] Entrou em process_user_input()") print(f"🔍 [DEBUG] Pergunta recebida: {question}") # Obtém contexto, scores e dados brutos com debug incluído context_pt, scores, context_en, context_norm, debug_ctx = retrieve_context( question, return_scores=True, return_raw=True ) print(f"\n📚 [DEBUG] Contexto traduzido (PT, início): {context_pt[:120]}...") # Traduz a pergunta para inglês question_en = traduzir(question, origem='pt', destino='en') # Cria prompt final a ser enviado ao Velvet prompt_en = f"Context:\n{debug_ctx['context_en']}\n\nQuestion: {question_en}\nAnswer:" print(f"\n🧠 [DEBUG] Prompt final (EN) enviado à Velvet:\n{prompt_en}\n") # Mede tempo de geração inicio = time.time() resposta_en = generate_response(prompt_en) duracao = time.time() - inicio print(f"\n💬 [DEBUG] Resposta bruta (EN) gerada pelo Velvet:\n{resposta_en.strip()}") # Tenta traduzir de volta para português try: resposta_final = traduzir(resposta_en.strip(), origem="en", destino="pt") except Exception as e: print("⚠️ Erro na tradução da resposta:", e) resposta_final = resposta_en.strip() # Validação da resposta com palavras-chave valido, num_keywords = validar_resposta_por_keywords(resposta_final, context_pt) if not valido or len(resposta_final) < 10: resposta_final = "⚠️ Não foi possível gerar uma resposta adequada com base no contexto." # Exibe métricas no terminal print("\n📊 [MÉTRICAS DE RESPOSTA]") print(f"🔸 Prompt Tokens: {contar_tokens(prompt_en)}") print(f"🔸 Resposta Tokens: {contar_tokens(resposta_en)}") print(f"🔸 Tempo de geração: {duracao:.2f}s") print(f"🔸 Palavras-chave comuns: {num_keywords}") print(f"🔸 Validação por keywords: {'✅' if valido else '❌'}") # Mostra documentos usados com suas pontuações print("\n🔸 Similaridade RAG:") for i, (trecho, score) in enumerate(scores, start=1): preview = trecho.page_content[:80].replace("\n", " ") print(f" Doc#{i}: score={score:.2f} ➜ {preview}...") # Guarda tudo no histórico local com estrutura completa salvar_completo_em_arquivo({ "pergunta_original": question, "query_en": question_en, "context_en": context_en, "context_normalizado": context_norm, "context_pt": context_pt, "prompt_enviado": prompt_en, "resposta_en": resposta_en.strip(), "resposta_pt": resposta_final, "scores": [{"doc": trecho.page_content, "score": float(score)} for trecho, score in scores], "tempo_execucao": round(duracao, 2), "validacao_keywords": valido }) print(f"\n✅ [DEBUG] Resposta final traduzida (PT):\n{resposta_final}\n") return resposta_final