Spaces:
Running
Running
Update ai_logic.py
Browse files- ai_logic.py +142 -66
ai_logic.py
CHANGED
|
@@ -46,7 +46,7 @@ DEFAULT_MODEL = "Llama 3.2 3B"
|
|
| 46 |
|
| 47 |
# --- Gerenciamento de Sessão ---
|
| 48 |
user_sessions: Dict[str, Dict[str, List | Dict]] = {}
|
| 49 |
-
MAX_MEMORY_LENGTH =
|
| 50 |
|
| 51 |
def get_session_memory_path(session_id: str) -> str:
|
| 52 |
"""Retorna o caminho do arquivo de memória para a sessão."""
|
|
@@ -85,6 +85,7 @@ def add_to_memory(session_id: str, user_message: str, assistant_response: str):
|
|
| 85 |
{"role": "user", "content": user_message, "timestamp": time.time()},
|
| 86 |
{"role": "assistant", "content": assistant_response, "timestamp": time.time()}
|
| 87 |
])
|
|
|
|
| 88 |
if len(conversation) > MAX_MEMORY_LENGTH * 2:
|
| 89 |
user_sessions[session_id]['conversation'] = conversation[-MAX_MEMORY_LENGTH * 2:]
|
| 90 |
save_conversation_memory(session_id)
|
|
@@ -112,24 +113,54 @@ def update_user_profile(session_id: str, user_message: str):
|
|
| 112 |
profile['total_perguntas'] = profile.get('total_perguntas', 0) + 1
|
| 113 |
user_sessions[session_id]['user_profile'] = profile
|
| 114 |
|
| 115 |
-
def
|
| 116 |
-
"""
|
|
|
|
|
|
|
|
|
|
| 117 |
load_conversation_memory(session_id)
|
| 118 |
-
conversation = user_sessions[session_id]['conversation']
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
|
| 123 |
def get_user_profile_context(session_id: str) -> str:
|
| 124 |
-
"""Gera o contexto do perfil do usuário."""
|
| 125 |
load_conversation_memory(session_id)
|
| 126 |
profile = user_sessions[session_id]['user_profile']
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
if interesses:
|
| 131 |
-
|
| 132 |
-
|
|
|
|
| 133 |
|
| 134 |
def clear_memory(session_id: str) -> str:
|
| 135 |
"""Limpa a memória de uma sessão específica."""
|
|
@@ -216,12 +247,23 @@ def load_vector_store():
|
|
| 216 |
print("Tentando criar novo vector store...")
|
| 217 |
build_and_save_vector_store()
|
| 218 |
|
| 219 |
-
def retrieve_context_from_blog(query: str, k: int =
|
| 220 |
-
"""Busca contexto relevante no vector store."""
|
| 221 |
if vector_store:
|
| 222 |
try:
|
| 223 |
results = vector_store.similarity_search(query, k=k)
|
| 224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
except Exception as e:
|
| 226 |
print(f"Erro ao buscar contexto: {e}")
|
| 227 |
return ""
|
|
@@ -277,8 +319,8 @@ class HuggingFaceInferenceClient:
|
|
| 277 |
else:
|
| 278 |
return False, f"Erro: {str(e)[:100]}"
|
| 279 |
|
| 280 |
-
def query_model(self, model_name: str, messages: List[Dict], max_tokens: int =
|
| 281 |
-
"""Faz requisição ao modelo usando chat completion."""
|
| 282 |
try:
|
| 283 |
client = self.get_client(model_name)
|
| 284 |
|
|
@@ -385,9 +427,11 @@ def test_and_update_models() -> int:
|
|
| 385 |
|
| 386 |
return len(MODELS)
|
| 387 |
|
| 388 |
-
# --- Chat Principal ---
|
| 389 |
def responder_como_aldo(session_id: str, pergunta: str, modelo: str = None) -> str:
|
| 390 |
-
"""
|
|
|
|
|
|
|
| 391 |
if not pergunta.strip():
|
| 392 |
return "Por favor, faça uma pergunta válida."
|
| 393 |
|
|
@@ -400,50 +444,77 @@ def responder_como_aldo(session_id: str, pergunta: str, modelo: str = None) -> s
|
|
| 400 |
load_conversation_memory(session_id)
|
| 401 |
update_user_profile(session_id, pergunta)
|
| 402 |
|
| 403 |
-
# Monta
|
| 404 |
-
contexto = []
|
| 405 |
-
if perfil := get_user_profile_context(session_id):
|
| 406 |
-
contexto.append(f"**Perfil do Usuário**\n{perfil}")
|
| 407 |
-
if conversa := get_conversation_context(session_id):
|
| 408 |
-
contexto.append(f"**Conversa Anterior**\n{conversa}")
|
| 409 |
-
if blog := retrieve_context_from_blog(pergunta):
|
| 410 |
-
contexto.append(f"**Contexto do Blog**\n{blog}")
|
| 411 |
-
|
| 412 |
-
system_prompt = """Você é o Dr. Aldo Henrique,
|
| 413 |
-
Doutor em Ciências da Computação pela UnB (2024), professor universitário especializado em:
|
| 414 |
-
- Algoritmos e Estruturas de Dados
|
| 415 |
-
- Inteligência Artificial
|
| 416 |
-
- Ciência de Dados e Mineração de Dados
|
| 417 |
-
- Desenvolvimento de Software
|
| 418 |
-
|
| 419 |
-
Responda sempre em português, de forma didática e clara.
|
| 420 |
-
- Explique conceitos antes de mostrar código
|
| 421 |
-
- Use exemplos práticos
|
| 422 |
-
- Considere o nível do usuário
|
| 423 |
-
- Faça sempre uma pequena observação que seja engraçada ou interessente relacionada a algo na pergunta.
|
| 424 |
-
- Use Markdown para formatação
|
| 425 |
-
- Adicione comentários explicativos cada parte do código
|
| 426 |
-
"""
|
| 427 |
|
| 428 |
-
|
| 429 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 430 |
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
|
| 436 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
inference_client = HuggingFaceInferenceClient(HF_TOKEN)
|
| 438 |
model_name = MODELS[modelo]
|
| 439 |
-
resposta = inference_client.query_model(model_name, messages)
|
| 440 |
|
| 441 |
-
#
|
| 442 |
-
|
| 443 |
|
| 444 |
-
#
|
| 445 |
-
|
| 446 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
|
| 448 |
# --- Inicialização ---
|
| 449 |
def inicializar_sistema():
|
|
@@ -484,7 +555,7 @@ if __name__ == "__main__":
|
|
| 484 |
|
| 485 |
if status:
|
| 486 |
print("\n" + "="*50)
|
| 487 |
-
print("TESTE DO SISTEMA")
|
| 488 |
print("="*50)
|
| 489 |
|
| 490 |
session_id = "teste_123"
|
|
@@ -494,20 +565,25 @@ if __name__ == "__main__":
|
|
| 494 |
resposta1 = responder_como_aldo(session_id, "O que é Python?")
|
| 495 |
print(f"Resposta: {resposta1[:200]}...")
|
| 496 |
|
| 497 |
-
# Teste 2
|
| 498 |
-
print("\n2. Testando pergunta
|
| 499 |
-
resposta2 = responder_como_aldo(session_id, "
|
| 500 |
print(f"Resposta: {resposta2[:200]}...")
|
| 501 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
# Limpeza
|
| 503 |
-
print(f"\
|
| 504 |
|
| 505 |
print("\n" + "="*50)
|
| 506 |
-
print("SISTEMA PRONTO
|
| 507 |
print("="*50)
|
| 508 |
-
print("✓
|
| 509 |
-
print("
|
| 510 |
-
print("
|
| 511 |
|
| 512 |
else:
|
| 513 |
print("\n" + "="*50)
|
|
|
|
| 46 |
|
| 47 |
# --- Gerenciamento de Sessão ---
|
| 48 |
user_sessions: Dict[str, Dict[str, List | Dict]] = {}
|
| 49 |
+
MAX_MEMORY_LENGTH = 8 # Aumentado para ter mais contexto útil
|
| 50 |
|
| 51 |
def get_session_memory_path(session_id: str) -> str:
|
| 52 |
"""Retorna o caminho do arquivo de memória para a sessão."""
|
|
|
|
| 85 |
{"role": "user", "content": user_message, "timestamp": time.time()},
|
| 86 |
{"role": "assistant", "content": assistant_response, "timestamp": time.time()}
|
| 87 |
])
|
| 88 |
+
# Mantém apenas as últimas conversas para evitar contexto muito longo
|
| 89 |
if len(conversation) > MAX_MEMORY_LENGTH * 2:
|
| 90 |
user_sessions[session_id]['conversation'] = conversation[-MAX_MEMORY_LENGTH * 2:]
|
| 91 |
save_conversation_memory(session_id)
|
|
|
|
| 113 |
profile['total_perguntas'] = profile.get('total_perguntas', 0) + 1
|
| 114 |
user_sessions[session_id]['user_profile'] = profile
|
| 115 |
|
| 116 |
+
def get_conversation_messages(session_id: str) -> List[Dict]:
|
| 117 |
+
"""
|
| 118 |
+
NOVA FUNÇÃO: Retorna as mensagens da conversa em formato adequado para o modelo.
|
| 119 |
+
Esta é a chave para resolver o problema de duplicação!
|
| 120 |
+
"""
|
| 121 |
load_conversation_memory(session_id)
|
| 122 |
+
conversation = user_sessions[session_id]['conversation']
|
| 123 |
+
|
| 124 |
+
# Pega apenas as últimas 6 mensagens (3 trocas) para não sobrecarregar
|
| 125 |
+
recent_conversation = conversation[-6:] if len(conversation) > 6 else conversation
|
| 126 |
+
|
| 127 |
+
# Converte para formato de mensagens do modelo
|
| 128 |
+
messages = []
|
| 129 |
+
for msg in recent_conversation:
|
| 130 |
+
# Remove metadados desnecessários das mensagens antigas
|
| 131 |
+
clean_content = msg['content']
|
| 132 |
+
|
| 133 |
+
# Remove a linha de informação do modelo das respostas antigas
|
| 134 |
+
if msg['role'] == 'assistant' and '*Resposta gerada pelo modelo:' in clean_content:
|
| 135 |
+
clean_content = clean_content.split('*Resposta gerada pelo modelo:')[0].strip()
|
| 136 |
+
|
| 137 |
+
messages.append({
|
| 138 |
+
"role": msg['role'],
|
| 139 |
+
"content": clean_content
|
| 140 |
+
})
|
| 141 |
+
|
| 142 |
+
return messages
|
| 143 |
|
| 144 |
def get_user_profile_context(session_id: str) -> str:
|
| 145 |
+
"""Gera o contexto do perfil do usuário de forma mais concisa."""
|
| 146 |
load_conversation_memory(session_id)
|
| 147 |
profile = user_sessions[session_id]['user_profile']
|
| 148 |
+
|
| 149 |
+
# Contexto mais conciso para não poluir o prompt
|
| 150 |
+
nivel = profile.get('nivel', 'intermediario')
|
| 151 |
+
total = profile.get('total_perguntas', 0)
|
| 152 |
+
|
| 153 |
+
context_parts = [f"Nível: {nivel}"]
|
| 154 |
+
|
| 155 |
+
# Só inclui interesses se há algum padrão significativo
|
| 156 |
+
interesses = [k.replace('interesse_', '').title()
|
| 157 |
+
for k, v in profile.items()
|
| 158 |
+
if k.startswith('interesse_') and v >= 2] # Só se perguntou pelo menos 2 vezes
|
| 159 |
+
|
| 160 |
if interesses:
|
| 161 |
+
context_parts.append(f"Interesses: {', '.join(interesses)}")
|
| 162 |
+
|
| 163 |
+
return " | ".join(context_parts)
|
| 164 |
|
| 165 |
def clear_memory(session_id: str) -> str:
|
| 166 |
"""Limpa a memória de uma sessão específica."""
|
|
|
|
| 247 |
print("Tentando criar novo vector store...")
|
| 248 |
build_and_save_vector_store()
|
| 249 |
|
| 250 |
+
def retrieve_context_from_blog(query: str, k: int = 3) -> str:
|
| 251 |
+
"""Busca contexto relevante no vector store - Reduzido para evitar sobrecarga."""
|
| 252 |
if vector_store:
|
| 253 |
try:
|
| 254 |
results = vector_store.similarity_search(query, k=k)
|
| 255 |
+
# Limita o tamanho do contexto para evitar tokens excessivos
|
| 256 |
+
context_parts = []
|
| 257 |
+
total_chars = 0
|
| 258 |
+
max_chars = 1500 # Limite de caracteres do contexto do blog
|
| 259 |
+
|
| 260 |
+
for doc in results:
|
| 261 |
+
if total_chars + len(doc.page_content) > max_chars:
|
| 262 |
+
break
|
| 263 |
+
context_parts.append(doc.page_content)
|
| 264 |
+
total_chars += len(doc.page_content)
|
| 265 |
+
|
| 266 |
+
return "\n---\n".join(context_parts)
|
| 267 |
except Exception as e:
|
| 268 |
print(f"Erro ao buscar contexto: {e}")
|
| 269 |
return ""
|
|
|
|
| 319 |
else:
|
| 320 |
return False, f"Erro: {str(e)[:100]}"
|
| 321 |
|
| 322 |
+
def query_model(self, model_name: str, messages: List[Dict], max_tokens: int = 1500, temperature: float = 0.5) -> str:
|
| 323 |
+
"""Faz requisição ao modelo usando chat completion - Reduzido max_tokens."""
|
| 324 |
try:
|
| 325 |
client = self.get_client(model_name)
|
| 326 |
|
|
|
|
| 427 |
|
| 428 |
return len(MODELS)
|
| 429 |
|
| 430 |
+
# --- Chat Principal (VERSÃO CORRIGIDA) ---
|
| 431 |
def responder_como_aldo(session_id: str, pergunta: str, modelo: str = None) -> str:
|
| 432 |
+
"""
|
| 433 |
+
FUNÇÃO PRINCIPAL CORRIGIDA: Gera resposta como Dr. Aldo Henrique sem duplicação.
|
| 434 |
+
"""
|
| 435 |
if not pergunta.strip():
|
| 436 |
return "Por favor, faça uma pergunta válida."
|
| 437 |
|
|
|
|
| 444 |
load_conversation_memory(session_id)
|
| 445 |
update_user_profile(session_id, pergunta)
|
| 446 |
|
| 447 |
+
# === NOVA ABORDAGEM: Monta mensagens em formato adequado ===
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
|
| 449 |
+
# 1. Obtém mensagens anteriores da conversa (já formatadas)
|
| 450 |
+
conversation_messages = get_conversation_messages(session_id)
|
| 451 |
+
|
| 452 |
+
# 2. Monta o system prompt (mais conciso)
|
| 453 |
+
perfil_info = get_user_profile_context(session_id)
|
| 454 |
+
|
| 455 |
+
system_prompt = f"""Você é o Dr. Aldo Henrique, Doutor em Ciências da Computação pela UnB (2024), professor universitário especializado em:
|
| 456 |
+
- Algoritmos e Estruturas de Dados
|
| 457 |
+
- Inteligência Artificial
|
| 458 |
+
- Ciência de Dados e Mineração de Dados
|
| 459 |
+
- Desenvolvimento de Software
|
| 460 |
+
|
| 461 |
+
Informações do usuário: {perfil_info}
|
| 462 |
+
|
| 463 |
+
Responda sempre em português, de forma didática e clara:
|
| 464 |
+
- Explique conceitos antes de mostrar código
|
| 465 |
+
- Use exemplos práticos adaptados ao nível do usuário
|
| 466 |
+
- Faça uma pequena observação interessante ou engraçada relacionada à pergunta
|
| 467 |
+
- Use Markdown para formatação
|
| 468 |
+
- Adicione comentários explicativos no código"""
|
| 469 |
|
| 470 |
+
# 3. Adiciona contexto do blog apenas se relevante (sem repetir na conversa)
|
| 471 |
+
blog_context = retrieve_context_from_blog(pergunta)
|
| 472 |
+
if blog_context:
|
| 473 |
+
system_prompt += f"\n\nContexto do seu blog (use apenas se relevante para a pergunta):\n{blog_context}"
|
| 474 |
|
| 475 |
+
# 4. Monta as mensagens finais
|
| 476 |
+
messages = [{"role": "system", "content": system_prompt}]
|
| 477 |
+
|
| 478 |
+
# Adiciona mensagens anteriores da conversa (sem duplicação)
|
| 479 |
+
messages.extend(conversation_messages)
|
| 480 |
+
|
| 481 |
+
# Adiciona a pergunta atual
|
| 482 |
+
messages.append({"role": "user", "content": pergunta})
|
| 483 |
+
|
| 484 |
+
# === DEBUG: Log do que está sendo enviado ===
|
| 485 |
+
print(f"\n=== DEBUG SESSION {session_id} ===")
|
| 486 |
+
print(f"Pergunta atual: {pergunta}")
|
| 487 |
+
print(f"Mensagens na conversa: {len(conversation_messages)}")
|
| 488 |
+
print(f"Total de mensagens enviadas: {len(messages)}")
|
| 489 |
+
print("=" * 40)
|
| 490 |
+
|
| 491 |
+
# 5. Faz requisição usando InferenceClient
|
| 492 |
inference_client = HuggingFaceInferenceClient(HF_TOKEN)
|
| 493 |
model_name = MODELS[modelo]
|
| 494 |
+
resposta = inference_client.query_model(model_name, messages, max_tokens=1200) # Reduzido
|
| 495 |
|
| 496 |
+
# 6. Limpa a resposta (remove possíveis repetições)
|
| 497 |
+
resposta_limpa = resposta.strip()
|
| 498 |
|
| 499 |
+
# Remove qualquer repetição óbvia da pergunta
|
| 500 |
+
if pergunta.lower() in resposta_limpa.lower()[:100]: # Se a pergunta aparece no início
|
| 501 |
+
lines = resposta_limpa.split('\n')
|
| 502 |
+
# Remove linhas que são muito similares à pergunta
|
| 503 |
+
filtered_lines = []
|
| 504 |
+
for line in lines:
|
| 505 |
+
if not (len(line.strip()) > 0 and
|
| 506 |
+
any(word in line.lower() for word in pergunta.lower().split() if len(word) > 3) and
|
| 507 |
+
len(line.strip()) < len(pergunta) * 1.5):
|
| 508 |
+
filtered_lines.append(line)
|
| 509 |
+
resposta_limpa = '\n'.join(filtered_lines).strip()
|
| 510 |
+
|
| 511 |
+
# 7. Adiciona informação sobre modelo usado (mais discreta)
|
| 512 |
+
resposta_final = f"{resposta_limpa}\n\n*— {modelo}*"
|
| 513 |
+
|
| 514 |
+
# 8. Salva na memória (a resposta limpa, sem a informação do modelo)
|
| 515 |
+
add_to_memory(session_id, pergunta, resposta_limpa)
|
| 516 |
+
|
| 517 |
+
return resposta_final
|
| 518 |
|
| 519 |
# --- Inicialização ---
|
| 520 |
def inicializar_sistema():
|
|
|
|
| 555 |
|
| 556 |
if status:
|
| 557 |
print("\n" + "="*50)
|
| 558 |
+
print("TESTE DO SISTEMA CORRIGIDO")
|
| 559 |
print("="*50)
|
| 560 |
|
| 561 |
session_id = "teste_123"
|
|
|
|
| 565 |
resposta1 = responder_como_aldo(session_id, "O que é Python?")
|
| 566 |
print(f"Resposta: {resposta1[:200]}...")
|
| 567 |
|
| 568 |
+
# Teste 2 - Pergunta relacionada (para testar memória)
|
| 569 |
+
print("\n2. Testando pergunta relacionada...")
|
| 570 |
+
resposta2 = responder_como_aldo(session_id, "Como posso começar a aprender Python?")
|
| 571 |
print(f"Resposta: {resposta2[:200]}...")
|
| 572 |
|
| 573 |
+
# Teste 3 - Pergunta completamente diferente
|
| 574 |
+
print("\n3. Testando pergunta diferente...")
|
| 575 |
+
resposta3 = responder_como_aldo(session_id, "Explique estruturas de dados")
|
| 576 |
+
print(f"Resposta: {resposta3[:200]}...")
|
| 577 |
+
|
| 578 |
# Limpeza
|
| 579 |
+
print(f"\n4. {clear_memory(session_id)}")
|
| 580 |
|
| 581 |
print("\n" + "="*50)
|
| 582 |
+
print("SISTEMA CORRIGIDO PRONTO!")
|
| 583 |
print("="*50)
|
| 584 |
+
print("✓ Memória sem duplicação implementada")
|
| 585 |
+
print("✓ Contexto otimizado para reduzir tokens")
|
| 586 |
+
print("✓ Respostas mais limpas e diretas")
|
| 587 |
|
| 588 |
else:
|
| 589 |
print("\n" + "="*50)
|