DeveloperSSEV/assistente-utfpr
0
1import os2from dotenv import load_dotenv3 4import streamlit as st5from google import genai6from google.genai.types import GenerateContentConfig7from pypdf import PdfReader # para ler PDFs. [web:466]8 9 10# -------------------------------------------------------11# Carregar variáveis de ambiente12# -------------------------------------------------------13load_dotenv()14GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")15 16if not GEMINI_API_KEY:17 raise ValueError("Defina a variável de ambiente GEMINI_API_KEY (no .env local ou em Secrets do Space).")18 19 20# -------------------------------------------------------21# Configurar cliente Gemini (SDK novo)22# -------------------------------------------------------23client = genai.Client(api_key=GEMINI_API_KEY)24MODEL_ID = "gemini-2.5-flash" # modelo rápido, adequado para chat. [web:280]25 26 27# -------------------------------------------------------28# Função para carregar texto dos PDFs29# -------------------------------------------------------30def carregar_pdfs_texto(pasta: str = "documents") -> str:31 """32 Lê todos os PDFs da pasta indicada e concatena o texto.33 Retorna uma string com o conteúdo de todos os documentos.34 """35 textos = []36 37 if not os.path.isdir(pasta):38 return ""39 40 for nome in os.listdir(pasta):41 if not nome.lower().endswith(".pdf"):42 continue43 44 caminho = os.path.join(pasta, nome)45 try:46 reader = PdfReader(caminho)47 partes_pdf = []48 for page in reader.pages:49 texto_pag = page.extract_text() or ""50 partes_pdf.append(texto_pag)51 texto_pdf = "\n".join(partes_pdf)52 53 textos.append(f"\n\n=== INÍCIO DO DOCUMENTO: {nome} ===\n{texto_pdf}")54 except Exception as e:55 textos.append(f"\n\n[ERRO AO LER {nome}: {e}]")56 57 return "\n".join(textos)58 59 60# Carrega o texto dos PDFs uma vez, na inicialização do app61TEXTOS_PDFS = carregar_pdfs_texto("documents")62 63 64# -------------------------------------------------------65# System prompt (instruções do assistente)66# -------------------------------------------------------67SYSTEM_PROMPT = """68Você é um assistente especializado em pós-graduação stricto sensu da UTFPR.69 70INSTRUÇÕES GERAIS:71- Responda sempre em português do Brasil, de forma clara, objetiva e acolhedora.72- Estruture as respostas com seções, listas numeradas e bullets quando isso ajudar na compreensão.73- Foque em orientar estudantes e candidatos sobre mestrado e doutorado (processos seletivos, matrícula, bolsas, prazos, créditos, qualificação, defesa, vida acadêmica).74 75CONHECIMENTO E FONTES:76- Use seu conhecimento geral sobre pós-graduação no Brasil (CAPES, CNPq, práticas comuns em universidades federais) quando a pergunta não citar regras específicas.77- Quando a pergunta for específica da UTFPR, explique o que é mais comum e, se houver variação entre campi ou programas, deixe isso explícito.78- Sempre que falar de regras, prazos ou documentos oficiais, recomende confirmar em:79 - editais do programa,80 - regulamentos internos,81 - secretaria ou coordenação da pós-graduação.82 83ESTILO DE RESPOSTA:84- Evite respostas muito curtas; traga contexto suficiente para que um aluno iniciante entenda o assunto.85- Evite jargões sem explicação. Quando usar termos técnicos (por exemplo, “conceito CAPES” ou “créditos”), explique em linguagem simples.86- Se a pergunta estiver confusa ou incompleta, peça esclarecimentos de forma educada antes de assumir algo importante.87 88LIMITAÇÕES:89- Não invente nomes de pessoas, documentos oficiais, números de vaga ou prazos específicos. Em vez disso, diga que esses detalhes podem variar e devem ser consultados no edital ou site oficial.90- Se não tiver informação suficiente para responder com segurança, diga claramente o que não é possível afirmar e indique caminhos para o aluno buscar a confirmação.91"""92 93 94# -------------------------------------------------------95# Configuração de geração (respostas completas)96# -------------------------------------------------------97GENERATION_CONFIG = GenerateContentConfig(98 max_output_tokens=1500, # mais espaço para respostas longas. [web:470][web:476]99 temperature=0.6,100 top_p=0.9,101 top_k=40,102)103 104 105# -------------------------------------------------------106# Função para chamar o modelo usando PDFs + histórico107# -------------------------------------------------------108def chamar_gemini(mensagens: list[dict]) -> str:109 """110 mensagens: lista de dicionários:111 {"role": "user" ou "assistant", "content": "texto"}112 Retorna o texto da resposta do modelo.113 """114 115 partes = [SYSTEM_PROMPT.strip()]116 117 # Se houver PDFs carregados, inclui um bloco com esses textos118 if TEXTOS_PDFS.strip():119 partes.append(120 "\n\nA seguir estão trechos de documentos oficiais (regulamentos, editais, normas) "121 "relacionados à pós-graduação da UTFPR. Use esses textos como referência principal "122 "quando a pergunta envolver regras, prazos, procedimentos formais ou exigências.\n"123 )124 partes.append(TEXTOS_PDFS)125 126 partes.append("\n\nHistórico da conversa:")127 for msg in mensagens:128 prefixo = "Usuário" if msg["role"] == "user" else "Assistente"129 partes.append(f"{prefixo}: {msg['content']}")130 131 partes.append(132 "\nResponda agora como o assistente, em português do Brasil, "133 "com uma resposta completa e contínua, sem cortar no meio, "134 "citando o que for relevante dos documentos quando necessário."135 )136 137 prompt = "\n".join(partes)138 139 response = client.models.generate_content(140 model=MODEL_ID,141 contents=prompt, # string única com tudo (instruções, PDFs, histórico). [web:280][web:408]142 config=GENERATION_CONFIG,143 )144 145 return response.text146 147 148# -------------------------------------------------------149# Interface Streamlit150# -------------------------------------------------------151st.set_page_config(page_title="Assistente de Pós-graduação UTFPR", page_icon="🎓")152st.title("Assistente de Pós-graduação UTFPR 🎓")153 154st.write(155 "Faça perguntas sobre mestrado e doutorado na UTFPR "156 "(processos seletivos, bolsas, créditos, prazos, regulamentos, defesas etc.)."157)158 159# Mostrar aviso se nenhum PDF foi encontrado160if not TEXTOS_PDFS.strip():161 st.warning(162 "Nenhum PDF foi encontrado na pasta 'documents'. "163 "Para usar documentos oficiais, envie os PDFs para o Space em 'documents/'."164 )165 166# Histórico na sessão167if "messages" not in st.session_state:168 st.session_state.messages = []169 170# Mostrar histórico no chat171for msg in st.session_state.messages:172 with st.chat_message("user" if msg["role"] == "user" else "assistant"):173 st.markdown(msg["content"])174 175# Entrada do usuário176pergunta = st.chat_input("Digite sua pergunta sobre pós-graduação...")177 178if pergunta:179 # Adiciona pergunta ao histórico180 st.session_state.messages.append({"role": "user", "content": pergunta})181 182 # Mostra pergunta183 with st.chat_message("user"):184 st.markdown(pergunta)185 186 # Gera resposta187 with st.chat_message("assistant"):188 with st.spinner("Pensando..."):189 resposta = chamar_gemini(st.session_state.messages)190 st.markdown(resposta)191 192 # Guarda resposta no histórico193 st.session_state.messages.append({"role": "assistant", "content": resposta})194 