import os import sqlite3 import pandas as pd from dotenv import load_dotenv from bs4 import BeautifulSoup import requests from langchain_community.document_loaders import WebBaseLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import FAISS from langchain_huggingface import HuggingFaceEmbeddings from langchain_openai import ChatOpenAI from langchain.chains import ConversationalRetrievalChain from langchain.memory import ConversationBufferMemory import gradio as gr # Carrega chave da API load_dotenv() os.environ["OPENAI_API_KEY"] = os.getenv("OPENROUTER_API_KEY") os.environ["OPENAI_API_BASE"] = "https://openrouter.ai/api/v1" # Embeddings fixos embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") # Mapeamento de temas para documentação de Data Science temas_para_links = { "classificação": "https://scikit-learn.org/stable/modules/classification.html", "regressão": "https://scikit-learn.org/stable/modules/linear_model.html", "dataframes": "https://pandas.pydata.org/docs/user_guide/dsintro.html", "séries temporais": "https://pandas.pydata.org/docs/user_guide/timeseries.html", "visualização": "https://matplotlib.org/stable/users/index.html", "aprendizado não supervisionado": "https://scikit-learn.org/stable/modules/unsupervised_learning.html", "pré-processamento": "https://scikit-learn.org/stable/modules/preprocessing.html", "análise exploratória": "https://pandas.pydata.org/docs/getting_started/intro_tutorials/01_table_oriented.html", "machine learning": "https://scikit-learn.org/stable/user_guide.html", "deep learning": "https://pytorch.org/tutorials/beginner/basics/intro.html" } # Detecta tema com base na pergunta def identificar_tema(pergunta): pergunta = pergunta.lower() for tema, link in temas_para_links.items(): if tema in pergunta: return link return None # Cria banco de dados SQLite se não existir conn = sqlite3.connect("historico_conversas.db") cursor = conn.cursor() cursor.execute(''' CREATE TABLE IF NOT EXISTS conversas ( id INTEGER PRIMARY KEY AUTOINCREMENT, aluno TEXT, pergunta TEXT, resposta TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ) ''') conn.commit() conn.close() # LLM da OpenRouter llm = ChatOpenAI(model="deepseek/deepseek-r1:free", temperature=0.4) # Memória da conversa memoria = ConversationBufferMemory(memory_key="chat_history", return_messages=True, output_key="answer") # Função para salvar no banco def salvar_conversa(nome, pergunta, resposta): conn = sqlite3.connect("historico_conversas.db") cursor = conn.cursor() cursor.execute("INSERT INTO conversas (aluno, pergunta, resposta) VALUES (?, ?, ?)", (nome or "Anônimo", pergunta, str(resposta))) conn.commit() conn.close() # Função principal com roteamento por tema def responder(pergunta, nome): try: link = identificar_tema(pergunta) if link: loader = WebBaseLoader(link) docs = loader.load() splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100) documents = splitter.split_documents(docs) if len(documents) > 0: vectordb = FAISS.from_documents(documents, embeddings) retriever = vectordb.as_retriever() resultado = ConversationalRetrievalChain.from_llm( llm=llm, retriever=retriever, memory=memoria, return_source_documents=True, output_key="answer" ).invoke({"question": pergunta}) raw = resultado["answer"] resposta = raw.content if hasattr(raw, "content") else str(raw) else: resposta = llm.invoke(pergunta).content else: resposta = llm.invoke(pergunta).content salvar_conversa(nome, pergunta, resposta) return resposta except Exception as e: import traceback return f"❌ **Erro:**\n```\n{traceback.format_exc()}\n```" # Resetar memória def resetar_memoria(): memoria.clear() return "✅ Memória resetada com sucesso!" # Exportar conversas para arquivos def exportar_conversas(): conn = sqlite3.connect("historico_conversas.db") df = pd.read_sql_query("SELECT * FROM conversas ORDER BY timestamp DESC", conn) df.to_csv("conversas_exportadas.csv", index=False) df.to_excel("conversas_exportadas.xlsx", index=False, engine="openpyxl") conn.close() return "✅ Arquivos 'conversas_exportadas.csv' e 'conversas_exportadas.xlsx' foram salvos!" # Interface Gradio with gr.Blocks() as app: gr.Markdown("## 📊 Tutor de Data Science com IA + Documentação Técnica") nome = gr.Textbox(label="Seu nome (opcional)", placeholder="Ex: Ana") pergunta = gr.Textbox(label="Sua dúvida sobre Data Science", placeholder="Ex: Como funciona a validação cruzada?") resposta = gr.Markdown(value="ℹ️ Aguardando sua pergunta...") botao_enviar = gr.Button("Enviar") botao_resetar = gr.Button("🔁 Resetar Memória") botao_exportar = gr.Button("📤 Exportar Histórico") botao_enviar.click(fn=responder, inputs=[pergunta, nome], outputs=resposta) botao_resetar.click(fn=resetar_memoria, outputs=resposta) botao_exportar.click(fn=exportar_conversas, outputs=resposta) app.launch(share=True)