velvet-api / controllers /chatbot_controller.py
Grilocas77's picture
Upload 25 files
e679b32 verified
# 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