Spaces:
Running
Running
Update ai_logic.py
Browse files- ai_logic.py +361 -566
ai_logic.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1 |
-
melhore esse código, as perguntas não estão sendo respondida corretamente, simplifique, a memoria tem de ser só do usuario da sessão, e não uma unica memoria geral para todos, mostre só o código completo: import requests
|
2 |
import os
|
3 |
-
import json
|
4 |
import re
|
5 |
import time
|
6 |
import pickle
|
|
|
7 |
from typing import Dict, Any, List, Optional, Tuple
|
8 |
from bs4 import BeautifulSoup
|
9 |
from urllib.parse import urljoin, urlparse
|
@@ -11,17 +10,20 @@ from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
11 |
from langchain.vectorstores import FAISS
|
12 |
from langchain.embeddings import HuggingFaceEmbeddings
|
13 |
|
14 |
-
# ---
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
BLOG_URL = "https://aldohenrique.com.br/"
|
|
|
|
|
16 |
VECTOR_STORE_PATH = "faiss_index_store.pkl"
|
17 |
PROCESSED_URLS_PATH = "processed_urls.pkl"
|
18 |
-
# CONVERSATION_MEMORY_PATH não será mais uma constante única
|
19 |
-
|
20 |
-
# --- Configuração da API Hugging Face ---
|
21 |
-
HF_TOKEN = os.getenv("HF_TOKEN")
|
22 |
-
if not HF_TOKEN:
|
23 |
-
raise ValueError("Token HF_TOKEN não encontrado nas variáveis de ambiente")
|
24 |
|
|
|
25 |
MODELS = {
|
26 |
"Mistral 7B": "mistralai/Mistral-7B-Instruct-v0.3",
|
27 |
"Phi-3 Mini (Microsoft)": "microsoft/Phi-3-mini-4k-instruct",
|
@@ -29,593 +31,386 @@ MODELS = {
|
|
29 |
"Gemma 7B (Google)":"google/gemma-7b-it",
|
30 |
"Zephyr 7B": "HuggingFaceH4/zephyr-7b-beta"
|
31 |
}
|
32 |
-
|
33 |
DEFAULT_MODEL = "Phi-3 Mini (Microsoft)"
|
34 |
|
35 |
-
# --- Variáveis Globais
|
|
|
36 |
vector_store: Optional[FAISS] = None
|
37 |
-
|
38 |
-
#
|
|
|
39 |
user_sessions: Dict[str, Dict[str, Any]] = {}
|
40 |
-
|
41 |
|
42 |
# ==============================================================================
|
43 |
-
# SEÇÃO
|
44 |
# ==============================================================================
|
45 |
|
46 |
-
def
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
"""Salva a memória da conversa para uma sessão específica."""
|
71 |
-
memory_path = get_session_memory_path(session_id)
|
72 |
-
session_data = user_sessions.get(session_id, {'conversation': [], 'user_profile': {}})
|
73 |
-
|
74 |
-
try:
|
75 |
-
data = {
|
76 |
-
'conversation': session_data['conversation'],
|
77 |
-
'user_profile': session_data['user_profile'],
|
78 |
-
'last_updated': time.time()
|
79 |
-
}
|
80 |
-
with open(memory_path, 'w', encoding='utf-8') as f:
|
81 |
-
json.dump(data, f, ensure_ascii=False, indent=2)
|
82 |
-
except Exception as e:
|
83 |
-
print(f"Erro ao salvar memória para sessão '{session_id}': {e}")
|
84 |
-
|
85 |
-
def add_to_memory(session_id: str, user_message: str, assistant_response: str):
|
86 |
-
"""Adiciona uma troca de mensagens à memória de uma sessão específica."""
|
87 |
-
|
88 |
-
if session_id not in user_sessions:
|
89 |
-
load_conversation_memory(session_id) # Garante que a sessão está carregada
|
90 |
-
|
91 |
-
session_data = user_sessions[session_id]
|
92 |
-
conversation = session_data['conversation']
|
93 |
-
|
94 |
-
conversation.append({
|
95 |
-
"role": "user",
|
96 |
-
�� "content": user_message,
|
97 |
-
"timestamp": time.time()
|
98 |
-
})
|
99 |
-
|
100 |
-
conversation.append({
|
101 |
-
"role": "assistant",
|
102 |
-
"content": assistant_response,
|
103 |
-
"timestamp": time.time()
|
104 |
-
})
|
105 |
-
|
106 |
-
# Limita o tamanho da memória
|
107 |
-
if len(conversation) > max_memory_length * 2: # *2 porque temos user + assistant
|
108 |
-
user_sessions[session_id]['conversation'] = conversation[-max_memory_length * 2:]
|
109 |
-
|
110 |
-
save_conversation_memory(session_id)
|
111 |
|
112 |
def update_user_profile(session_id: str, user_message: str):
|
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 |
-
conversation_history = user_sessions[session_id]['conversation']
|
152 |
-
if not conversation_history:
|
153 |
-
return ""
|
154 |
-
|
155 |
-
# Pega as últimas 6 mensagens (3 trocas)
|
156 |
-
recent_messages = conversation_history[-6:] if len(conversation_history) > 6 else conversation_history
|
157 |
-
|
158 |
-
context = "--- CONTEXTO DA CONVERSA ANTERIOR ---\n"
|
159 |
-
for msg in recent_messages:
|
160 |
-
role = "USUÁRIO" if msg["role"] == "user" else "PROFESSOR"
|
161 |
-
# Limita o tamanho de cada mensagem no contexto
|
162 |
-
content = msg["content"][:200] + "..." if len(msg["content"]) > 200 else msg["content"]
|
163 |
-
context += f"{role}: {content}\n"
|
164 |
-
|
165 |
-
context += "--- FIM DO CONTEXTO DA CONVERSA ---\n"
|
166 |
-
return context
|
167 |
-
|
168 |
-
def get_user_profile_context(session_id: str) -> str:
|
169 |
-
"""Gera informações sobre o perfil do usuário de uma sessão específica para personalizar a resposta."""
|
170 |
-
if session_id not in user_sessions:
|
171 |
-
load_conversation_memory(session_id)
|
172 |
-
|
173 |
-
user_profile = user_sessions[session_id]['user_profile']
|
174 |
-
if not user_profile:
|
175 |
-
return ""
|
176 |
-
|
177 |
-
context = "--- PERFIL DO ALUNO ---\n"
|
178 |
-
|
179 |
-
# Nível de conhecimento
|
180 |
-
nivel = user_profile.get('nivel', 'intermediario')
|
181 |
-
context += f"Nível: {nivel}\n"
|
182 |
-
|
183 |
-
# Principais interesses
|
184 |
-
interesses = []
|
185 |
-
for key, value in user_profile.items():
|
186 |
-
if key.startswith('interesse_') and value > 0:
|
187 |
-
topic = key.replace('interesse_', '').replace('_', ' ')
|
188 |
-
interesses.append(f"{topic} ({value}x)")
|
189 |
-
|
190 |
-
if interesses:
|
191 |
-
context += f"Principais interesses: {', '.join(interesses)}\n"
|
192 |
-
|
193 |
-
total = user_profile.get('total_perguntas', 0)
|
194 |
-
context += f"Total de perguntas feitas: {total}\n"
|
195 |
-
context += "--- FIM DO PERFIL DO ALUNO ---\n"
|
196 |
-
|
197 |
-
return context
|
198 |
-
|
199 |
-
def clear_memory(session_id: str):
|
200 |
-
"""Limpa a memória da conversa de uma sessão específica."""
|
201 |
-
memory_path = get_session_memory_path(session_id)
|
202 |
-
|
203 |
-
if session_id in user_sessions:
|
204 |
-
del user_sessions[session_id] # Remove da memória em tempo de execução
|
205 |
-
|
206 |
-
try:
|
207 |
-
if os.path.exists(memory_path):
|
208 |
-
os.remove(memory_path)
|
209 |
-
return "✅ Memória da conversa limpa com sucesso!"
|
210 |
-
except Exception as e:
|
211 |
-
return f"❌ Erro ao limpar memória: {e}"
|
212 |
|
213 |
# ==============================================================================
|
214 |
-
# SEÇÃO RAG:
|
215 |
# ==============================================================================
|
216 |
|
217 |
-
def get_all_blog_links(url: str, processed_urls: set) -> set:
|
218 |
-
"""Navega pelo blog para encontrar todos os links de posts e páginas."""
|
219 |
-
links_to_visit = {url}
|
220 |
-
visited_links = set()
|
221 |
-
|
222 |
-
while links_to_visit:
|
223 |
-
current_url = links_to_visit.pop()
|
224 |
-
if current_url in visited_links:
|
225 |
-
continue
|
226 |
-
|
227 |
-
try:
|
228 |
-
response = requests.get(current_url, timeout=10)
|
229 |
-
response.raise_for_status()
|
230 |
-
soup = BeautifulSoup(response.content, 'html.parser')
|
231 |
-
visited_links.add(current_url)
|
232 |
-
print(f"Visitando: {current_url}")
|
233 |
-
|
234 |
-
for link in soup.find_all('a', href=True):
|
235 |
-
href = link['href']
|
236 |
-
full_url = urljoin(url, href)
|
237 |
-
if urlparse(full_url).netloc == urlparse(url).netloc and full_url not in visited_links:
|
238 |
-
links_to_visit.add(full_url)
|
239 |
-
except requests.RequestException as e:
|
240 |
-
print(f"Erro ao acessar {current_url}: {e}")
|
241 |
-
|
242 |
-
final_links = {link for link in visited_links if '/tag/' not in link and '/category/' not in link and '?' not in link}
|
243 |
-
return final_links
|
244 |
-
|
245 |
def scrape_text_from_url(url: str) -> str:
|
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 |
-
with open(VECTOR_STORE_PATH, "wb") as f:
|
289 |
-
pickle.dump(vector_store, f)
|
290 |
-
|
291 |
-
with open(PROCESSED_URLS_PATH, "wb") as f:
|
292 |
-
pickle.dump(all_links, f)
|
293 |
-
|
294 |
-
end_time = time.time()
|
295 |
-
message = f"✅ Retreino do RAG concluído em {end_time - start_time:.2f} segundos. {len(chunks)} chunks de texto processados."
|
296 |
-
return message, VECTOR_STORE_PATH, PROCESSED_URLS_PATH
|
297 |
|
298 |
def load_vector_store():
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
return f"Erro ao buscar contexto: {e}"
|
320 |
-
return ""
|
321 |
|
322 |
# ==============================================================================
|
323 |
-
# SEÇÃO API
|
324 |
# ==============================================================================
|
325 |
|
326 |
class HuggingFaceAPIClient:
|
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 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
400 |
|
401 |
# ==============================================================================
|
402 |
-
#
|
403 |
# ==============================================================================
|
404 |
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
- Adapte sua linguagem ao nível do aluno: {nivel}
|
461 |
-
|
462 |
-
ESTILO DE ENSINO:
|
463 |
-
- Sempre explique o "porquê" antes do "como"
|
464 |
-
- Use analogias e exemplos práticos
|
465 |
-
- Encoraje perguntas e curiosidade
|
466 |
-
- Quando mostrar código, sempre explique com comentários detalhados
|
467 |
-
- Foque na compreensão, não apenas na solução
|
468 |
-
- Conecte conceitos com aplicações do mundo real
|
469 |
-
|
470 |
-
REGRAS:
|
471 |
-
- Responda sempre em português brasileiro
|
472 |
-
- Use blocos de código formatados com ```
|
473 |
-
- Só responda perguntas relacionadas a programação e tecnologia
|
474 |
-
- Se não for sobre TI, informe educadamente que sua especialidade é tecnologia
|
475 |
-
- Quando apresentar código, sempre explique linha por linha nos comentários"""
|
476 |
-
|
477 |
-
# Monta o prompt completo com todos os contextos
|
478 |
-
prompt_parts = []
|
479 |
-
|
480 |
-
if contexto_perfil:
|
481 |
-
prompt_parts.append(contexto_perfil)
|
482 |
-
|
483 |
-
if contexto_conversa:
|
484 |
-
prompt_parts.append(contexto_conversa)
|
485 |
-
|
486 |
-
if contexto_blog:
|
487 |
-
prompt_parts.append("--- CONTEXTO DO SEU BLOG ---")
|
488 |
-
prompt_parts.append(contexto_blog)
|
489 |
-
prompt_parts.append("--- FIM DO CONTEXTO DO BLOG ---")
|
490 |
-
|
491 |
-
prompt_parts.append(f"PERGUNTA ATUAL DO ALUNO: {pergunta}")
|
492 |
-
|
493 |
-
# Adiciona instruções específicas baseadas no contexto
|
494 |
-
if contexto_conversa:
|
495 |
-
prompt_parts.append("\nIMPORTANTE: Considere o contexto da nossa conversa anterior ao responder. Se esta pergunta se relaciona com algo que já discutimos, faça essa conexão naturalmente.")
|
496 |
-
|
497 |
-
pergunta_completa = "\n\n".join(prompt_parts)
|
498 |
-
|
499 |
-
messages = [
|
500 |
-
{"role": "system", "content": system_prompt},
|
501 |
-
{"role": "user", "content": pergunta_completa}
|
502 |
-
]
|
503 |
-
|
504 |
-
model_name = MODELS.get(modelo_escolhido, MODELS[DEFAULT_MODEL])
|
505 |
-
resposta = api_client.query_model(model_name, messages)
|
506 |
-
|
507 |
-
if resposta.startswith("Assistente: "):
|
508 |
-
resposta = resposta.replace("Assistente: ", "")
|
509 |
-
|
510 |
-
# Adiciona a conversa à memória da sessão
|
511 |
-
add_to_memory(session_id, pergunta, resposta)
|
512 |
-
|
513 |
-
resposta_formatada = formatar_resposta_com_codigo(resposta.strip())
|
514 |
-
return resposta_formatada
|
515 |
-
|
516 |
-
except Exception as e:
|
517 |
-
return f"Erro ao processar sua pergunta: {str(e)}"
|
518 |
|
519 |
-
|
520 |
-
|
521 |
-
|
|
|
|
|
522 |
|
523 |
-
def verificar_modelo_disponivel(model_name: str) -> str:
|
524 |
-
try:
|
525 |
-
url = f"[https://api-inference.huggingface.co/models/](https://api-inference.huggingface.co/models/){model_name}"
|
526 |
-
headers = {"Authorization": f"Bearer {HF_TOKEN}"}
|
527 |
-
payload = {"inputs": "Hello", "parameters": {"max_new_tokens": 5}}
|
528 |
-
response = requests.post(url, headers=headers, json=payload, timeout=9999)
|
529 |
-
if response.status_code == 200: return "✅ Disponível"
|
530 |
-
elif response.status_code == 404: return "❌ Não encontrado"
|
531 |
-
elif response.status_code == 503: return "⏳ Carregando..."
|
532 |
-
else: return f"⚠️ Status {response.status_code}"
|
533 |
-
except Exception as e:
|
534 |
-
return f"❌ Erro: {str(e)[:50]}..."
|
535 |
-
|
536 |
-
def testar_todos_modelos():
|
537 |
-
resultados = []
|
538 |
-
for nome, modelo in MODELS.items():
|
539 |
-
status = verificar_modelo_disponivel(modelo)
|
540 |
-
resultados.append(f"{nome}: {status}")
|
541 |
-
return "\n".join(resultados)
|
542 |
-
|
543 |
-
def get_memory_stats(session_id: str) -> str:
|
544 |
-
"""Retorna estatísticas da memória atual para uma sessão específica."""
|
545 |
-
if session_id not in user_sessions:
|
546 |
-
load_conversation_memory(session_id)
|
547 |
-
|
548 |
-
session_data = user_sessions[session_id]
|
549 |
-
conversation_history = session_data['conversation']
|
550 |
-
user_profile = session_data['user_profile']
|
551 |
-
|
552 |
-
total_messages = len(conversation_history)
|
553 |
-
user_messages = len([m for m in conversation_history if m["role"] == "user"])
|
554 |
-
|
555 |
-
stats = f"📊 **Estatísticas da Memória para Sessão '{session_id}':**\n"
|
556 |
-
stats += f"• Total de mensagens: {total_messages}\n"
|
557 |
-
stats += f"• Perguntas do usuário: {user_messages}\n"
|
558 |
-
stats += f"• Nível detectado: {user_profile.get('nivel', 'Não definido')}\n"
|
559 |
-
|
560 |
-
# Principais interesses
|
561 |
-
interesses = []
|
562 |
-
for key, value in user_profile.items():
|
563 |
-
if key.startswith('interesse_') and value > 0:
|
564 |
-
topic = key.replace('interesse_', '').replace('_', ' ').title()
|
565 |
-
interesses.append(f"{topic} ({value})")
|
566 |
-
|
567 |
-
if interesses:
|
568 |
-
stats += f"• Principais interesses: {', '.join(interesses)}\n"
|
569 |
-
|
570 |
-
return stats
|
571 |
|
572 |
# ==============================================================================
|
573 |
-
#
|
574 |
# ==============================================================================
|
575 |
|
576 |
-
def inicializar_sistema():
|
577 |
-
"""Inicializa todos os componentes do sistema."""
|
578 |
-
print("🚀 Inicializando o Chatbot Dr. Aldo Henrique com Memória...")
|
579 |
-
|
580 |
-
# Carrega o vector store (RAG)
|
581 |
-
load_vector_store()
|
582 |
-
|
583 |
-
# **NÃO CARREGAMOS MAIS UMA MEMÓRIA GLOBAL AQUI**
|
584 |
-
# A memória será carregada e gerenciada por sessão individualmente
|
585 |
-
|
586 |
-
print("✅ Sistema inicializado com sucesso!")
|
587 |
-
print(f"🧠 Vector Store: {'Carregado' if vector_store else 'Não encontrado'}")
|
588 |
-
|
589 |
-
# Chama a inicialização quando o módulo é carregado
|
590 |
if __name__ == "__main__":
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
|
|
2 |
import re
|
3 |
import time
|
4 |
import pickle
|
5 |
+
import requests
|
6 |
from typing import Dict, Any, List, Optional, Tuple
|
7 |
from bs4 import BeautifulSoup
|
8 |
from urllib.parse import urljoin, urlparse
|
|
|
10 |
from langchain.vectorstores import FAISS
|
11 |
from langchain.embeddings import HuggingFaceEmbeddings
|
12 |
|
13 |
+
# --- Configurações ---
|
14 |
+
# Chave da API da Hugging Face (essencial para o funcionamento)
|
15 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
16 |
+
if not HF_TOKEN:
|
17 |
+
raise ValueError("A variável de ambiente HF_TOKEN não foi definida. Defina-a com seu token da Hugging Face.")
|
18 |
+
|
19 |
+
# URL do blog para a base de conhecimento (RAG)
|
20 |
BLOG_URL = "https://aldohenrique.com.br/"
|
21 |
+
|
22 |
+
# Caminhos para os arquivos do RAG
|
23 |
VECTOR_STORE_PATH = "faiss_index_store.pkl"
|
24 |
PROCESSED_URLS_PATH = "processed_urls.pkl"
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
+
# Modelos disponíveis na Hugging Face
|
27 |
MODELS = {
|
28 |
"Mistral 7B": "mistralai/Mistral-7B-Instruct-v0.3",
|
29 |
"Phi-3 Mini (Microsoft)": "microsoft/Phi-3-mini-4k-instruct",
|
|
|
31 |
"Gemma 7B (Google)":"google/gemma-7b-it",
|
32 |
"Zephyr 7B": "HuggingFaceH4/zephyr-7b-beta"
|
33 |
}
|
|
|
34 |
DEFAULT_MODEL = "Phi-3 Mini (Microsoft)"
|
35 |
|
36 |
+
# --- Variáveis Globais ---
|
37 |
+
# Armazena o índice vetorial para busca de contexto (RAG)
|
38 |
vector_store: Optional[FAISS] = None
|
39 |
+
|
40 |
+
# Dicionário para gerenciar todas as sessões de usuário em memória
|
41 |
+
# Estrutura: {session_id: {"history": [...], "profile": {...}}}
|
42 |
user_sessions: Dict[str, Dict[str, Any]] = {}
|
43 |
+
MAX_MEMORY_TURNS = 5 # Manter as últimas 5 trocas (usuário + assistente)
|
44 |
|
45 |
# ==============================================================================
|
46 |
+
# SEÇÃO DE GERENCIAMENTO DA SESSÃO (MEMÓRIA E PERFIL)
|
47 |
# ==============================================================================
|
48 |
|
49 |
+
def get_or_create_session(session_id: str) -> Dict[str, Any]:
|
50 |
+
"""
|
51 |
+
Obtém uma sessão de usuário existente ou cria uma nova.
|
52 |
+
A sessão é mantida apenas em memória.
|
53 |
+
"""
|
54 |
+
if session_id not in user_sessions:
|
55 |
+
print(f"Nova sessão criada para o ID: {session_id}")
|
56 |
+
user_sessions[session_id] = {
|
57 |
+
"history": [],
|
58 |
+
"profile": {"nivel": "indefinido", "interesses": {}, "total_perguntas": 0}
|
59 |
+
}
|
60 |
+
return user_sessions[session_id]
|
61 |
+
|
62 |
+
def update_memory(session_id: str, user_message: str, assistant_response: str):
|
63 |
+
"""Adiciona a troca de mensagens ao histórico da sessão."""
|
64 |
+
session = get_or_create_session(session_id)
|
65 |
+
|
66 |
+
# Adiciona as mensagens mais recentes
|
67 |
+
session["history"].append({"role": "user", "content": user_message})
|
68 |
+
session["history"].append({"role": "assistant", "content": assistant_response})
|
69 |
+
|
70 |
+
# Garante que o histórico não exceda o tamanho máximo
|
71 |
+
if len(session["history"]) > MAX_MEMORY_TURNS * 2:
|
72 |
+
session["history"] = session["history"][-(MAX_MEMORY_TURNS * 2):]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
|
74 |
def update_user_profile(session_id: str, user_message: str):
|
75 |
+
"""
|
76 |
+
Analisa a mensagem do usuário para inferir e atualizar seu perfil de interesses e nível.
|
77 |
+
"""
|
78 |
+
session = get_or_create_session(session_id)
|
79 |
+
profile = session["profile"]
|
80 |
+
msg_lower = user_message.lower()
|
81 |
+
|
82 |
+
# Atualiza contador de perguntas
|
83 |
+
profile["total_perguntas"] += 1
|
84 |
+
|
85 |
+
# Inferência de nível
|
86 |
+
if any(word in msg_lower for word in ['básico', 'iniciante', 'começar', 'o que é']):
|
87 |
+
profile['nivel'] = 'iniciante'
|
88 |
+
elif any(word in msg_lower for word in ['avançado', 'complexo', 'otimização', 'performance', 'arquitetura']):
|
89 |
+
profile['nivel'] = 'avançado'
|
90 |
+
elif profile['nivel'] == 'indefinido': # Define como intermediário se ainda não tiver um nível
|
91 |
+
profile['nivel'] = 'intermediário'
|
92 |
+
|
93 |
+
# Inferência de interesses
|
94 |
+
topics = {
|
95 |
+
'java': ['java', 'spring', 'jpa', 'jvm'],
|
96 |
+
'python': ['python', 'django', 'flask', 'pandas'],
|
97 |
+
'web': ['html', 'css', 'javascript', 'react', 'node'],
|
98 |
+
'ia': ['inteligência artificial', 'machine learning', 'llm', 'rag'],
|
99 |
+
'banco de dados': ['sql', 'nosql', 'mongodb', 'postgresql']
|
100 |
+
}
|
101 |
+
for topic, keywords in topics.items():
|
102 |
+
if any(keyword in msg_lower for keyword in keywords):
|
103 |
+
profile['interesses'][topic] = profile['interesses'].get(topic, 0) + 1
|
104 |
+
|
105 |
+
def clear_session_memory(session_id: str) -> str:
|
106 |
+
"""Limpa a memória de uma sessão específica."""
|
107 |
+
if session_id in user_sessions:
|
108 |
+
del user_sessions[session_id]
|
109 |
+
return f"✅ Memória da sessão '{session_id}' foi limpa."
|
110 |
+
return f"⚠️ Sessão '{session_id}' não encontrada."
|
111 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
112 |
|
113 |
# ==============================================================================
|
114 |
+
# SEÇÃO RAG: BUSCA E PROCESSAMENTO DE CONTEÚDO (SEM ALTERAÇÕES SIGNIFICATIVAS)
|
115 |
# ==============================================================================
|
116 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
def scrape_text_from_url(url: str) -> str:
|
118 |
+
"""Extrai texto de uma URL, focando no conteúdo principal."""
|
119 |
+
try:
|
120 |
+
response = requests.get(url, timeout=10)
|
121 |
+
response.raise_for_status()
|
122 |
+
soup = BeautifulSoup(response.content, 'html.parser')
|
123 |
+
main_content = soup.find('article') or soup.find('main')
|
124 |
+
return main_content.get_text(separator='\n', strip=True) if main_content else ""
|
125 |
+
except requests.RequestException as e:
|
126 |
+
print(f"Erro ao acessar {url}: {e}")
|
127 |
+
return ""
|
128 |
+
|
129 |
+
def build_and_save_vector_store():
|
130 |
+
"""Coleta dados do blog, processa e cria um índice vetorial com FAISS."""
|
131 |
+
global vector_store
|
132 |
+
print("Iniciando construção do RAG...")
|
133 |
+
|
134 |
+
# Lógica simplificada de coleta de links (pode ser expandida se necessário)
|
135 |
+
# Para este exemplo, vamos focar em uma URL principal
|
136 |
+
all_texts = [scrape_text_from_url(BLOG_URL)]
|
137 |
+
|
138 |
+
# Adicione mais URLs manualmente se desejar
|
139 |
+
# additional_urls = [f"{BLOG_URL}/sobre", f"{BLOG_URL}/contato"]
|
140 |
+
# all_texts.extend([scrape_text_from_url(url) for url in additional_urls])
|
141 |
+
|
142 |
+
valid_texts = [text for text in all_texts if text and len(text) > 100]
|
143 |
+
if not valid_texts:
|
144 |
+
print("Nenhum texto válido encontrado para criar o RAG.")
|
145 |
+
return
|
146 |
+
|
147 |
+
print(f"Processando {len(valid_texts)} página(s).")
|
148 |
+
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=150)
|
149 |
+
chunks = text_splitter.create_documents(valid_texts)
|
150 |
+
|
151 |
+
print(f"Criando {len(chunks)} chunks de texto.")
|
152 |
+
embeddings_model = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
153 |
+
vector_store = FAISS.from_documents(chunks, embeddings_model)
|
154 |
+
|
155 |
+
with open(VECTOR_STORE_PATH, "wb") as f:
|
156 |
+
pickle.dump(vector_store, f)
|
157 |
+
|
158 |
+
print("✅ RAG construído e salvo com sucesso!")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
|
160 |
def load_vector_store():
|
161 |
+
"""Carrega o índice vetorial do disco."""
|
162 |
+
global vector_store
|
163 |
+
if os.path.exists(VECTOR_STORE_PATH):
|
164 |
+
print(f"Carregando RAG de '{VECTOR_STORE_PATH}'...")
|
165 |
+
with open(VECTOR_STORE_PATH, "rb") as f:
|
166 |
+
vector_store = pickle.load(f)
|
167 |
+
print("✅ RAG carregado.")
|
168 |
+
else:
|
169 |
+
print("Índice RAG não encontrado. Construindo um novo...")
|
170 |
+
build_and_save_vector_store()
|
171 |
+
|
172 |
+
def retrieve_rag_context(query: str, k: int = 3) -> str:
|
173 |
+
"""Busca no RAG por contexto relevante para a pergunta."""
|
174 |
+
if vector_store:
|
175 |
+
try:
|
176 |
+
results = vector_store.similarity_search(query, k=k)
|
177 |
+
return "\n\n---\n\n".join([doc.page_content for doc in results])
|
178 |
+
except Exception as e:
|
179 |
+
print(f"Erro ao buscar contexto no RAG: {e}")
|
180 |
+
return ""
|
|
|
|
|
181 |
|
182 |
# ==============================================================================
|
183 |
+
# SEÇÃO DA API E CONSTRUÇÃO DO PROMPT
|
184 |
# ==============================================================================
|
185 |
|
186 |
class HuggingFaceAPIClient:
|
187 |
+
"""Cliente para interagir com a API de Inferência da Hugging Face."""
|
188 |
+
def __init__(self, token: str):
|
189 |
+
self.headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
|
190 |
+
|
191 |
+
def query(self, model_id: str, messages: List[Dict[str, str]], max_tokens: int = 2048) -> str:
|
192 |
+
api_url = f"https://api-inference.huggingface.co/models/{model_id}"
|
193 |
+
payload = {
|
194 |
+
"inputs": self._format_prompt_for_model(messages),
|
195 |
+
"parameters": {
|
196 |
+
"max_new_tokens": max_tokens,
|
197 |
+
"temperature": 0.7,
|
198 |
+
"top_p": 0.95,
|
199 |
+
"return_full_text": False,
|
200 |
+
},
|
201 |
+
"options": {"wait_for_model": True}
|
202 |
+
}
|
203 |
+
try:
|
204 |
+
response = requests.post(api_url, headers=self.headers, json=payload, timeout=60)
|
205 |
+
response.raise_for_status()
|
206 |
+
result = response.json()
|
207 |
+
# A resposta da API de inferência pode vir em uma lista
|
208 |
+
if isinstance(result, list) and result:
|
209 |
+
return result[0].get("generated_text", "").strip()
|
210 |
+
# Ou em um dicionário
|
211 |
+
elif isinstance(result, dict):
|
212 |
+
return result.get("generated_text", f"Erro: Resposta inesperada do modelo: {result.get('error', '')}").strip()
|
213 |
+
return "Erro: Resposta vazia ou em formato inesperado."
|
214 |
+
except requests.Timeout:
|
215 |
+
return "Erro: A requisição à API demorou muito para responder (timeout)."
|
216 |
+
except requests.HTTPError as http_err:
|
217 |
+
return f"Erro HTTP: {http_err}. Detalhes: {response.text}"
|
218 |
+
except Exception as e:
|
219 |
+
return f"Ocorreu um erro inesperado na chamada da API: {e}"
|
220 |
+
|
221 |
+
def _format_prompt_for_model(self, messages: List[Dict[str, str]]) -> str:
|
222 |
+
"""Formata a lista de mensagens em uma string única para a API de inferência."""
|
223 |
+
prompt_str = ""
|
224 |
+
for msg in messages:
|
225 |
+
if msg['role'] == 'system':
|
226 |
+
prompt_str += f"<|system|>\n{msg['content']}</s>\n"
|
227 |
+
elif msg['role'] == 'user':
|
228 |
+
prompt_str += f"<|user|>\n{msg['content']}</s>\n"
|
229 |
+
elif msg['role'] == 'assistant':
|
230 |
+
prompt_str += f"<|assistant|>\n{msg['content']}</s>\n"
|
231 |
+
prompt_str += "<|assistant|>\n" # Solicita a continuação do assistente
|
232 |
+
return prompt_str
|
233 |
+
|
234 |
+
|
235 |
+
class PromptBuilder:
|
236 |
+
"""Constrói o prompt final a ser enviado para o modelo."""
|
237 |
+
|
238 |
+
SYS_PROMPT_TEMPLATE = """Você é o Professor Aldo, um especialista em programação (Java, C, Web) e IA.
|
239 |
+
|
240 |
+
**Sua Personalidade:**
|
241 |
+
- **Didático e Paciente:** Aja como um professor experiente. Explique o "porquê" das coisas, não apenas o "como".
|
242 |
+
- **Acolhedor e Amigável:** Use uma linguagem calorosa e acessível.
|
243 |
+
- **Adaptável:** Ajuste a complexidade da sua resposta ao nível de conhecimento do aluno.
|
244 |
+
- **Contextual:** Se a pergunta atual se conectar a algo que já discutimos, mencione essa conexão.
|
245 |
+
|
246 |
+
**Suas Regras:**
|
247 |
+
1. Responda sempre em português do Brasil.
|
248 |
+
2. Use blocos de código (```java, ```python, etc.) para exemplos. Comente o código para explicar cada parte.
|
249 |
+
3. Se a pergunta não for sobre tecnologia ou programação, educadamente informe que sua especialidade é outra.
|
250 |
+
4. Baseie sua resposta primariamente nas informações do seu blog (se houver contexto) e no nosso histórico de conversa.
|
251 |
+
|
252 |
+
A seguir, informações para te ajudar a contextualizar sua resposta:"""
|
253 |
+
|
254 |
+
def __init__(self, session_id: str, rag_context: str):
|
255 |
+
self.session = get_or_create_session(session_id)
|
256 |
+
self.rag_context = rag_context
|
257 |
+
self.parts = []
|
258 |
+
|
259 |
+
def _add_profile_context(self):
|
260 |
+
profile = self.session["profile"]
|
261 |
+
if profile["total_perguntas"] > 0:
|
262 |
+
profile_summary = [f"**Perfil do Aluno (Inferido):**"]
|
263 |
+
profile_summary.append(f"- Nível de conhecimento: {profile['nivel'].capitalize()}")
|
264 |
+
interesses = sorted(profile['interesses'].items(), key=lambda item: item[1], reverse=True)
|
265 |
+
if interesses:
|
266 |
+
formatted_interesses = [f"{topic.capitalize()} ({count}x)" for topic, count in interesses]
|
267 |
+
profile_summary.append(f"- Principais interesses: {', '.join(formatted_interesses)}")
|
268 |
+
self.parts.append("\n".join(profile_summary))
|
269 |
+
|
270 |
+
def _add_rag_context(self):
|
271 |
+
if self.rag_context:
|
272 |
+
self.parts.append(f"**Contexto Relevante do seu Blog (RAG):**\n{self.rag_context}")
|
273 |
+
|
274 |
+
def _add_history_context(self, current_question: str) -> List[Dict[str, str]]:
|
275 |
+
"""Prepara o histórico de mensagens para o modelo."""
|
276 |
+
history = self.session.get("history", [])
|
277 |
+
# Pega as mensagens do histórico e adiciona a pergunta atual
|
278 |
+
messages = history + [{"role": "user", "content": current_question}]
|
279 |
+
return messages
|
280 |
+
|
281 |
+
def build(self, user_question: str) -> List[Dict[str, str]]:
|
282 |
+
# Adiciona os contextos ao prompt do sistema
|
283 |
+
self._add_profile_context()
|
284 |
+
self._add_rag_context()
|
285 |
+
|
286 |
+
system_content = self.SYS_PROMPT_TEMPLATE
|
287 |
+
if self.parts:
|
288 |
+
system_content += "\n\n" + "\n\n".join(self.parts)
|
289 |
+
|
290 |
+
# Monta a lista final de mensagens
|
291 |
+
messages = [{"role": "system", "content": system_content}]
|
292 |
+
messages.extend(self._add_history_context(user_question))
|
293 |
+
|
294 |
+
return messages
|
295 |
|
296 |
# ==============================================================================
|
297 |
+
# FUNÇÃO PRINCIPAL E INICIALIZAÇÃO
|
298 |
# ==============================================================================
|
299 |
|
300 |
+
# Inicializa o cliente da API
|
301 |
+
api_client = HuggingFaceAPIClient(token=HF_TOKEN)
|
302 |
+
|
303 |
+
def formatar_resposta(resposta: str) -> str:
|
304 |
+
"""Formata a resposta com HTML para melhor visualização de código e texto."""
|
305 |
+
resposta_html = resposta.replace('<', '<').replace('>', '>')
|
306 |
+
|
307 |
+
# Formata blocos de código
|
308 |
+
resposta_html = re.sub(
|
309 |
+
r'```(\w+)?\n(.*?)\n```',
|
310 |
+
r'<div style="background-color:#f0f0f0; border:1px solid #ddd; border-radius:8px; padding:15px; margin:1em 0; font-family:monospace; color:black;"><pre><code>\2</code></pre></div>',
|
311 |
+
resposta_html,
|
312 |
+
flags=re.DOTALL
|
313 |
+
)
|
314 |
+
# Formata negrito
|
315 |
+
resposta_html = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', resposta_html)
|
316 |
+
# Formata nova linha
|
317 |
+
return resposta_html.replace('\n', '<br>')
|
318 |
+
|
319 |
+
def responder_pergunta(session_id: str, pergunta: str, modelo_escolhido: str = DEFAULT_MODEL) -> str:
|
320 |
+
"""
|
321 |
+
Função principal que orquestra todo o processo de resposta.
|
322 |
+
"""
|
323 |
+
if not pergunta.strip():
|
324 |
+
return "Por favor, faça uma pergunta."
|
325 |
+
|
326 |
+
print(f"\n--- Processando pergunta para a sessão: {session_id} ---")
|
327 |
+
|
328 |
+
# 1. Atualizar perfil do usuário com base na pergunta atual
|
329 |
+
update_user_profile(session_id, pergunta)
|
330 |
+
|
331 |
+
# 2. Buscar contexto relevante no RAG
|
332 |
+
print("Buscando no RAG...")
|
333 |
+
rag_context = retrieve_rag_context(pergunta)
|
334 |
+
if rag_context:
|
335 |
+
print("Contexto encontrado no RAG.")
|
336 |
+
|
337 |
+
# 3. Construir o prompt completo usando o PromptBuilder
|
338 |
+
print("Construindo prompt...")
|
339 |
+
builder = PromptBuilder(session_id, rag_context)
|
340 |
+
messages = builder.build(pergunta)
|
341 |
+
|
342 |
+
# DEBUG: Descomente a linha abaixo para ver o prompt exato enviado ao modelo
|
343 |
+
# print("MENSAGENS ENVIADAS AO MODELO:", json.dumps(messages, indent=2, ensure_ascii=False))
|
344 |
+
|
345 |
+
# 4. Chamar a API da Hugging Face
|
346 |
+
print(f"Enviando para o modelo '{modelo_escolhido}'...")
|
347 |
+
model_id = MODELS.get(modelo_escolhido, MODELS[DEFAULT_MODEL])
|
348 |
+
resposta_bruta = api_client.query(model_id, messages)
|
349 |
+
|
350 |
+
# 5. Adicionar a interação à memória da sessão
|
351 |
+
update_memory(session_id, pergunta, resposta_bruta)
|
352 |
+
|
353 |
+
# 6. Formatar e retornar a resposta
|
354 |
+
return formatar_resposta(resposta_bruta)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
355 |
|
356 |
+
def inicializar_sistema():
|
357 |
+
"""Carrega o RAG ao iniciar."""
|
358 |
+
print("🚀 Inicializando o sistema...")
|
359 |
+
load_vector_store()
|
360 |
+
print("✅ Sistema pronto para uso.")
|
361 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
362 |
|
363 |
# ==============================================================================
|
364 |
+
# BLOCO DE TESTE
|
365 |
# ==============================================================================
|
366 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
367 |
if __name__ == "__main__":
|
368 |
+
inicializar_sistema()
|
369 |
+
|
370 |
+
# --- Simulação de Conversas ---
|
371 |
+
|
372 |
+
# Sessão do Usuário A (interessado em Java)
|
373 |
+
session_a = "aluno_java_123"
|
374 |
+
print("\n--- INÍCIO DA CONVERSA COM ALUNO A (Java) ---")
|
375 |
+
|
376 |
+
pergunta1_a = "Olá! Pode me explicar o que é Polimorfismo em Java de uma forma simples?"
|
377 |
+
resposta1_a = responder_pergunta(session_a, pergunta1_a)
|
378 |
+
print(f"ALUNO A: {pergunta1_a}\nPROFESSOR ALDO:\n{resposta1_a}\n")
|
379 |
+
|
380 |
+
pergunta2_a = "Entendi! Pode me dar um exemplo de código com classes e herança para ilustrar?"
|
381 |
+
resposta2_a = responder_pergunta(session_a, pergunta2_a)
|
382 |
+
print(f"ALUNO A: {pergunta2_a}\nPROFESSOR ALDO:\n{resposta2_a}\n")
|
383 |
+
|
384 |
+
# Sessão do Usuário B (interessado em IA)
|
385 |
+
session_b = "aluna_ia_456"
|
386 |
+
print("\n--- INÍCIO DA CONVERSA COM ALUNA B (IA) ---")
|
387 |
+
|
388 |
+
pergunta1_b = "Oi, professor! Eu sou nova na área. Qual a diferença entre Inteligência Artificial e Machine Learning?"
|
389 |
+
resposta1_b = responder_pergunta(session_b, pergunta1_b)
|
390 |
+
print(f"ALUNA B: {pergunta1_b}\nPROFESSOR ALDO:\n{resposta1_b}\n")
|
391 |
+
|
392 |
+
# Usuário A continua sua conversa, a memória do usuário B não deve interferir
|
393 |
+
print("\n--- ALUNO A CONTINUA SUA CONVERSA ---")
|
394 |
+
pergunta3_a = "Faz sentido. E como o conceito de 'override' se encaixa nisso que acabamos de ver?"
|
395 |
+
resposta3_a = responder_pergunta(session_a, pergunta3_a)
|
396 |
+
print(f"ALUNO A: {pergunta3_a}\nPROFESSOR ALDO:\n{resposta3_a}\n")
|
397 |
+
|
398 |
+
# Exibe o estado final das sessões
|
399 |
+
print("\n--- ESTADO FINAL DAS SESSÕES EM MEMÓRIA ---")
|
400 |
+
print(f"\nSessão A ({session_a}):")
|
401 |
+
# print(json.dumps(user_sessions.get(session_a), indent=2, ensure_ascii=False))
|
402 |
+
print(f" Nível: {user_sessions[session_a]['profile']['nivel']}")
|
403 |
+
print(f" Interesses: {user_sessions[session_a]['profile']['interesses']}")
|
404 |
+
print(f" Tamanho do Histórico: {len(user_sessions[session_a]['history'])} mensagens")
|
405 |
+
|
406 |
+
|
407 |
+
print(f"\nSessão B ({session_b}):")
|
408 |
+
# print(json.dumps(user_sessions.get(session_b), indent=2, ensure_ascii=False))
|
409 |
+
print(f" Nível: {user_sessions[session_b]['profile']['nivel']}")
|
410 |
+
print(f" Interesses: {user_sessions[session_b]['profile']['interesses']}")
|
411 |
+
print(f" Tamanho do Histórico: {len(user_sessions[session_b]['history'])} mensagens")
|
412 |
+
|
413 |
+
# Limpando a memória de uma sessão
|
414 |
+
print("\n--- LIMPANDO MEMÓRIA ---")
|
415 |
+
print(clear_session_memory(session_a))
|
416 |
+
print(f"Sessões ativas agora: {list(user_sessions.keys())}")
|