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)
|