import os import gradio as gr from huggingface_hub import InferenceClient from langchain_community.document_loaders import PyPDFLoader, WebBaseLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_huggingface import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma # --- CONFIGURAÇÕES --- HF_TOKEN = os.getenv("Circulo_bot") MODEL_ID = "meta-llama/Meta-Llama-3-8B-Instruct" DB_DIR = "./chroma_db" client = InferenceClient(MODEL_ID, token=HF_TOKEN) os.environ["USER_AGENT"] = "SRF_Marilia_Bot/1.0" # --- 1. CARREGAMENTO E VETORIZAÇÃO (RAG) --- def inicializar_base_conhecimento(): embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2") # Se o banco já existe localmente, carrega ele em vez de reprocessar tudo if os.path.exists(DB_DIR) and os.listdir(DB_DIR): print("Carregando base de dados existente...") return Chroma(persist_directory=DB_DIR, embedding_function=embeddings) print("Criando nova base de dados...") urls = ["https://www.srf-marilia.com/atividades.html", "https://srf-marilia.com/index.html", "https://srf-marilia.com/srf.html", "https://srf-marilia.com/yogananda.html", "https://srf-marilia.com/meditacao.html", "https://srf-marilia.com/livraria.html", "https://srf-marilia.com/links.html", "https://www.instagram.com/srf.marilia", "https://yogananda.org/pt/perguntas-frequentes"] pdf_files = ["Autobiografia.pdf", "Circulo.pdf", "SRF.pdf", "FAQ.pdf", "Livros.pdf"] docs = [] # Carregar URLs for url in urls: try: loader = WebBaseLoader(url) docs.extend(loader.load()) except Exception as e: print(f"Erro URL {url}: {e}") # Carregar PDFs for pdf in pdf_files: if os.path.exists(pdf): try: loader = PyPDFLoader(pdf) docs.extend(loader.load()) except Exception as e: print(f"Erro PDF {pdf}: {e}") # Processamento de texto text_splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=100) splits = text_splitter.split_documents(docs) # Criar e persistir o banco vectorstore = Chroma.from_documents( documents=splits, embedding=embeddings, persist_directory=DB_DIR ) return vectorstore # Inicializa o banco globalmente vectorstore = inicializar_base_conhecimento() # --- 2. LÓGICA DO CHAT --- def bot_response(message, history): # 1. Busca por similaridade no banco de dados relevant_docs = vectorstore.similarity_search(message, k=3) context = "\n\n".join([doc.page_content for doc in relevant_docs]) # 2. Prompt do Sistema system_message = ( "Você é o Assistente Virtual do Círculo de Meditação de Marília da SRF. " "Seu tom deve ser sempre calmo, gentil, acolhedor e espiritualizado, " "refletindo a paz dos ensinamentos de Paramahansa Yogananda.\n\n" "DIRETRIZES DE RESPOSTA:\n" "1. Se a resposta estiver presente no CONTEXTO abaixo, sinta-se à vontade para ser " "atencioso e explicar com clareza e beleza, usando o máximo de detalhes que o contexto permitir.\n" "2. Se a informação NÃO estiver no contexto, não tente adivinhar. Nesse caso específico, " "seja breve e peça desculpas, orientando o usuário a enviar um e-mail para srf.marilia@yahoo.com.\n" "3. Nunca mencione que está lendo um texto ou que tem um 'contexto fornecido'. Fale de forma natural.\n\n" f"CONTEXTO RELEVANTE:\n{context}" ) # 3. Construção do histórico de mensagens (Versão Segura) messages = [{"role": "system", "content": system_message}] for entry in history: # Se o histórico vier como dicionário (Gradio 5+) if isinstance(entry, dict): messages.append({"role": entry["role"], "content": entry["content"]}) # Se o histórico vier como lista/tupla (Gradio 4-) elif isinstance(entry, (list, tuple)) and len(entry) == 2: user_part, bot_part = entry if user_part: messages.append({"role": "user", "content": user_part}) if bot_part: messages.append({"role": "assistant", "content": bot_part}) # Adiciona a pergunta atual do usuário messages.append({"role": "user", "content": message}) # 4. GERAÇÃO via Stream (Versão ultra-segura) partial_text = "" try: for chunk in client.chat_completion( messages, max_tokens=600, stream=True, temperature=0.10, # Quase determinístico top_p=0.90 # Filtra respostas muito "fora da caixa" ): # Verificamos cuidadosamente se o chunk tem os dados antes de acessar o índice [0] if hasattr(chunk, 'choices') and len(chunk.choices) > 0: token = chunk.choices[0].delta.content if token: partial_text += token yield partial_text except Exception as e: yield f"Houve um erro na comunicação com o modelo: {str(e)}" # --- 3. INTERFACE --- # Removendo o argumento 'theme' que causou o erro demo = gr.ChatInterface( fn=bot_response, title="🧘 Assistente SRF Marília", description="Tire suas dúvidas sobre meditação e os ensinamentos de Paramahansa Yogananda.", examples=["Como começar a meditar?", "Quais os horários das atividades?", "Quem foi Yogananda?"], cache_examples=False ) if __name__ == "__main__": # Removendo server_name e server_port para o Hugging Face gerenciar sozinho demo.launch()