Spaces:
Sleeping
Sleeping
import gradio as gr | |
import torch | |
from sentence_transformers import SentenceTransformer, util | |
from transformers import T5ForConditionalGeneration, T5Tokenizer | |
from pypdf import PdfReader | |
import os | |
# --- 1. Carregamento dos Modelos (faça isso apenas uma vez) --- | |
# Esta parte não muda. Usaremos os mesmos modelos eficientes. | |
print("Carregando o modelo de recuperação (Sentence Transformer)...") | |
retriever_model = SentenceTransformer('all-MiniLM-L6-v2') | |
print("Carregando o modelo de geração (Flan-T5)...") | |
generator_tokenizer = T5Tokenizer.from_pretrained('google/flan-t5-base') | |
generator_model = T5ForConditionalGeneration.from_pretrained('google/flan-t5-base') | |
print("Modelos carregados com sucesso!") | |
# --- 2. Função para Processar Arquivos Enviados --- | |
def process_files(files): | |
""" | |
Lê arquivos .pdf e .txt, extrai o texto e o divide em blocos. | |
Retorna os blocos de texto e seus embeddings correspondentes. | |
""" | |
if not files: | |
return None, "Por favor, envie um ou mais arquivos." | |
knowledge_text = "" | |
for file in files: | |
file_path = file.name | |
# Extrai texto de arquivos PDF | |
if file_path.endswith(".pdf"): | |
try: | |
reader = PdfReader(file_path) | |
for page in reader.pages: | |
page_text = page.extract_text() | |
if page_text: | |
knowledge_text += page_text + "\n" | |
except Exception as e: | |
return None, f"Erro ao ler o arquivo PDF {os.path.basename(file_path)}: {e}" | |
# Extrai texto de arquivos TXT | |
elif file_path.endswith(".txt"): | |
try: | |
with open(file_path, 'r', encoding='utf-8') as f: | |
knowledge_text += f.read() + "\n" | |
except Exception as e: | |
return None, f"Erro ao ler o arquivo TXT {os.path.basename(file_path)}: {e}" | |
if not knowledge_text.strip(): | |
return None, "Não foi possível extrair texto dos arquivos fornecidos." | |
# Divide o texto em blocos menores (parágrafos) para uma melhor recuperação | |
text_chunks = [chunk.strip() for chunk in knowledge_text.split('\n\n') if chunk.strip()] | |
if not text_chunks: | |
return None, "O texto extraído não continha blocos de texto válidos para processamento." | |
# Cria os embeddings para a base de conhecimento extraída | |
print(f"Processando {len(text_chunks)} blocos de texto dos arquivos...") | |
knowledge_base_embeddings = retriever_model.encode(text_chunks, convert_to_tensor=True, show_progress_bar=True) | |
print("Base de conhecimento criada a partir dos arquivos.") | |
# Retorna a base de conhecimento (blocos e embeddings) e uma mensagem de sucesso | |
return (text_chunks, knowledge_base_embeddings), f"✅ Sucesso! {len(files)} arquivo(s) processado(s), gerando {len(text_chunks)} blocos de texto." | |
# --- 3. A Função Principal do RAG --- | |
def answer_question(question, knowledge_state): | |
""" | |
Recebe uma pergunta e o estado da base de conhecimento (texto e embeddings) | |
e gera uma resposta. | |
""" | |
if not question: | |
return "Por favor, insira uma pergunta." | |
if not knowledge_state or not knowledge_state[0] or knowledge_state[1] is None: | |
return "⚠️ A base de conhecimento está vazia. Por favor, processe alguns arquivos primeiro." | |
knowledge_base, knowledge_base_embeddings = knowledge_state | |
# Etapa de Recuperação (Retrieval) | |
question_embedding = retriever_model.encode(question, convert_to_tensor=True) | |
cosine_scores = util.cos_sim(question_embedding, knowledge_base_embeddings) | |
# Encontra o bloco de texto mais relevante | |
best_doc_index = torch.argmax(cosine_scores) | |
retrieved_context = knowledge_base[best_doc_index] | |
print(f"\n--- Nova Pergunta de Auditoria ---") | |
print(f"Pergunta: {question}") | |
print(f"Contexto Recuperado: {retrieved_context}") | |
# Etapa de Geração (Generation) | |
prompt = f""" | |
Contexto: {retrieved_context} | |
Pergunta: {question} | |
Com base estritamente no contexto do documento fornecido, responda à pergunta do auditor. | |
Resposta: | |
""" | |
input_ids = generator_tokenizer(prompt, return_tensors="pt").input_ids | |
outputs = generator_model.generate( | |
input_ids, | |
max_length=256, # Aumentado para respostas potencialmente mais longas | |
num_beams=5, | |
early_stopping=True | |
) | |
answer = generator_tokenizer.decode(outputs[0], skip_special_tokens=True) | |
return answer | |
# --- 4. Criação da Interface com Gradio Blocks --- | |
with gr.Blocks(theme=gr.themes.Soft()) as interface: | |
# Estado para armazenar a base de conhecimento processada | |
knowledge_state = gr.State() | |
gr.Markdown( | |
""" | |
# 🤖 RAG - Auditor de Documentos | |
**1. Carregue seus arquivos**: Envie um ou mais certificados ou documentos nos formatos `.pdf` ou `.txt`. | |
**2. Processe os arquivos**: Clique no botão para criar a base de conhecimento. | |
**3. Faça perguntas**: Após o processamento, faça perguntas sobre o conteúdo dos documentos. | |
""" | |
) | |
with gr.Row(): | |
with gr.Column(scale=1): | |
file_uploader = gr.File( | |
label="Carregar Certificados (.pdf, .txt)", | |
file_count="multiple", | |
file_types=[".pdf", ".txt"] | |
) | |
process_button = gr.Button("Processar Arquivos", variant="primary") | |
status_box = gr.Textbox(label="Status do Processamento", interactive=False) | |
with gr.Column(scale=2): | |
question_box = gr.Textbox(label="Faça sua pergunta aqui", placeholder="Ex: Qual o resultado da calibração do instrumento PI-101?") | |
submit_button = gr.Button("Obter Resposta", variant="primary") | |
answer_box = gr.Textbox(label="Resposta Baseada nos Documentos", interactive=False, lines=5) | |
# Conecta os componentes às funções | |
process_button.click( | |
fn=process_files, | |
inputs=[file_uploader], | |
outputs=[knowledge_state, status_box] | |
) | |
submit_button.click( | |
fn=answer_question, | |
inputs=[question_box, knowledge_state], | |
outputs=[answer_box] | |
) | |
# --- 5. Lançamento do App --- | |
if __name__ == "__main__": | |
interface.launch() | |