# -*- coding: utf-8 -*- import os import torch import gradio as gr from pypdf import PdfReader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain.schema import Document from langchain.vectorstores import Chroma from langchain.embeddings import HuggingFaceEmbeddings from langchain.prompts import PromptTemplate from transformers import pipeline, AutoModelForCausalLM, AutoTokenizer CAMINHO_PDF = "Regimento.pdf" CAMINHO_DB = "db" # ===================================== # 1. Carregar PDF e criar Vetor Store # ===================================== def carregar_pdf(caminho): reader = PdfReader(caminho) textos = [] for i, pagina in enumerate(reader.pages): texto = pagina.extract_text() if texto: textos.append(Document(page_content=texto, metadata={"page": i + 1})) return textos def dividir_em_chunks(documentos): splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200) chunks = splitter.split_documents(documentos) print(f"Dividido em {len(chunks)} chunks") return chunks def vetorizar(chunks): embeddings = HuggingFaceEmbeddings( model_name="intfloat/multilingual-e5-small", model_kwargs={"device": "cpu"}, encode_kwargs={"batch_size": 32}, ) db = Chroma( collection_name="regimento", embedding_function=embeddings, persist_directory=CAMINHO_DB ) db.add_documents(chunks) return db def criar_db(caminho_pdf): documentos = carregar_pdf(caminho_pdf) chunks = dividir_em_chunks(documentos) db = vetorizar(chunks) return db # ===================================== # 2. Criar DB se não existir # ===================================== if not os.path.exists(CAMINHO_DB): print("DB não encontrado. Criando...") criar_db(CAMINHO_PDF) else: print("DB encontrado. Usando existente.") # ===================================== # 3. Carregar Vetor Store + Modelo LLM # ===================================== _emb = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-small") _db = Chroma( collection_name="regimento", persist_directory=CAMINHO_DB, embedding_function=_emb ) MODEL = "Qwen/Qwen2.5-0.5B-Instruct" tok = AutoTokenizer.from_pretrained(MODEL) mdl = AutoModelForCausalLM.from_pretrained( MODEL, device_map="auto", torch_dtype=torch.float32, ) generator = pipeline( "text-generation", model=mdl, tokenizer=tok, max_new_tokens=350, temperature=0.2, do_sample=False, pad_token_id=tok.eos_token_id, return_full_text=False ) prompt_template = """ Primeiramente, inicie a resposta com "Oi, querido!". Depois responda a pergunta do usuário: {pergunta} Com base SOMENTE nas informações abaixo: {base_conhecimento} Caso não encontre a resposta, diga: "não sei te dizer isso". """ _prompt = PromptTemplate( template=prompt_template, input_variables=["pergunta", "base_conhecimento"] ) def _listar_fontes(resultados): pags = [] for doc, score in resultados: p = doc.metadata.get("page") if p and p not in pags: pags.append(p) return ", ".join([f"p.{p}" for p in pags]) def rag_chat(user_msg, history): resultados = _db.similarity_search_with_relevance_scores(user_msg, k=3) if not resultados or resultados[0][1] < 0.7: resp = "Oi, querido! Não encontrei informação relevante para responder." return history + [(user_msg, resp)] textos = [f"(p.{doc.metadata.get('page')}) {doc.page_content}" for doc, score in resultados] base_conhecimento = "\n\n----\n\n".join(textos) mensagem = _prompt.format(pergunta=user_msg, base_conhecimento=base_conhecimento) messages = [ {"role": "system", "content": "Você responde sempre fiel ao documento."}, {"role": "user", "content": mensagem}, ] prompt_chat = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True) out = generator(prompt_chat)[0]["generated_text"].strip() fontes = _listar_fontes(resultados) if fontes: out += f"\n\nFontes: {fontes}" return history + [(user_msg, out)] # ===================================== # 4. Interface Gradio # ===================================== with gr.Blocks(title="CHAT IEPG") as demo: gr.Markdown("