import os import sqlite3 import pandas as pd from dotenv import load_dotenv from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import FAISS from langchain.embeddings 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" # Carrega e divide o PDF loader = PyPDFLoader("apostila_python.pdf") docs = loader.load() text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=100) documents = text_splitter.split_documents(docs) # Cria embeddings com modelo local (sem necessidade de API) embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2") vectordb = FAISS.from_documents(documents, embeddings) retriever = vectordb.as_retriever() # 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") # Cadeia com recuperação de contexto e memória qa_chain = ConversationalRetrievalChain.from_llm( llm=llm, retriever=retriever, memory=memoria, return_source_documents=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, resposta)) conn.commit() conn.close() # Função para responder usando o PDF e memória def responder(pergunta, nome): try: resultado = qa_chain.invoke({"question": pergunta}) resposta = resultado["answer"] fontes = resultado.get("source_documents", []) # Fallback se nenhuma fonte relevante for encontrada if not fontes or all(len(doc.page_content.strip()) == 0 for doc in fontes): resposta = llm.invoke(pergunta) salvar_conversa(nome, pergunta, resposta) return resposta except Exception as e: import traceback return f"❌ Erro:\n{traceback.format_exc()}" # 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 Python com IA + Apostila PDF + Histórico em BD") nome = gr.Textbox(label="Seu nome (opcional)", placeholder="Ex: João") pergunta = gr.Textbox(label="Sua dúvida sobre Python", placeholder="Ex: Como usar listas em Python?") resposta = gr.Textbox(label="Resposta do Assistente") 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)