Spaces:
Sleeping
Sleeping
| import google.generativeai as genai | |
| from sentence_transformers import SentenceTransformer | |
| import numpy as np | |
| from typing import List, Dict, Optional | |
| import faiss | |
| import pickle | |
| import os | |
| from datetime import datetime | |
| import pdfplumber | |
| from pathlib import Path | |
| # Configuração inicial do Gemini | |
| genai.configure(api_key="AIzaSyClWplmEF8_sDgmSbhg0h6xkAoFQcLU4p4") | |
| class MetrologyGlossary: | |
| """Glossário interno de termos metrológicos para melhorar recuperação e respostas.""" | |
| def __init__(self): | |
| self.terms = { | |
| "incerteza": "Medida da dispersão associada ao resultado de uma medição.", | |
| "calibração": "Comparação de um instrumento com um padrão de referência.", | |
| "traceability": "Propriedade de um resultado de medição que pode ser relacionado a um padrão nacional ou internacional.", | |
| "iso/iec 17025": "Norma internacional para laboratórios de ensaio e calibração.", | |
| # Adicione mais termos conforme necessário | |
| } | |
| def enhance_query(self, query: str) -> str: | |
| """Adiciona definições ou sinônimos à consulta para maior precisão.""" | |
| for term, definition in self.terms.items(): | |
| if term.lower() in query.lower(): | |
| query += f" ({definition})" | |
| return query | |
| class DocumentParser: | |
| """Extrai texto e tabelas de arquivos PDF, com foco em metrologia.""" | |
| def parse_pdf(self, file_path: str) -> Dict: | |
| """Extrai texto e tabelas de um único PDF.""" | |
| try: | |
| with pdfplumber.open(file_path) as pdf: | |
| text = "" | |
| tables = [] | |
| for page in pdf.pages: | |
| # Extrai texto | |
| page_text = page.extract_text() or "" | |
| text += page_text + "\n" | |
| # Extrai tabelas | |
| page_tables = page.extract_tables() | |
| for table in page_tables: | |
| tables.append(table) | |
| # Converte tabelas em texto estruturado | |
| table_text = "" | |
| for idx, table in enumerate(tables): | |
| table_text += f"Tabela {idx + 1}:\n" | |
| for row in table: | |
| table_text += " | ".join([str(cell) or "" for cell in row]) + "\n" | |
| return { | |
| "content": (text + "\n" + table_text).strip(), | |
| "metadata": { | |
| "file_name": os.path.basename(file_path), | |
| "path": file_path, | |
| "num_pages": len(pdf.pages), | |
| "has_tables": len(tables) > 0 | |
| } | |
| } | |
| except Exception as e: | |
| print(f"Erro ao processar {file_path}: {str(e)}") | |
| return {"content": "", "metadata": {}} | |
| def parse_multiple_pdfs(self, pdf_paths: List[str]) -> List[Dict]: | |
| """Extrai texto e tabelas de múltiplos PDFs.""" | |
| documents = [] | |
| for path in pdf_paths: | |
| doc = self.parse_pdf(path) | |
| if doc["content"]: | |
| documents.append(doc) | |
| return documents | |
| class KnowledgeBase: | |
| """Gerencia a base de conhecimento metrológico.""" | |
| def __init__(self): | |
| self.documents: List[Dict] = [] | |
| def add_document(self, content: str, metadata: Optional[Dict] = None): | |
| doc = {"content": content, "metadata": metadata or {}, "id": len(self.documents)} | |
| self.documents.append(doc) | |
| def add_documents(self, documents: List[Dict]): | |
| for doc in documents: | |
| self.add_document(doc["content"], doc["metadata"]) | |
| def get_document(self, doc_id: int) -> Dict: | |
| return self.documents[doc_id] | |
| def get_all_contents(self) -> List[str]: | |
| return [doc["content"] for doc in self.documents] | |
| class EmbeddingGenerator: | |
| """Gera embeddings para textos.""" | |
| def __init__(self, model_name: str = "all-MiniLM-L6-v2"): | |
| self.model = SentenceTransformer(model_name) | |
| def generate(self, texts: List[str]) -> np.ndarray: | |
| return self.model.encode(texts, convert_to_numpy=True) | |
| class VectorStore: | |
| """Armazena e busca embeddings usando FAISS.""" | |
| def __init__(self, dimension: int): | |
| self.index = faiss.IndexFlatL2(dimension) | |
| self.doc_ids = [] | |
| def add_vectors(self, embeddings: np.ndarray, doc_ids: List[int]): | |
| self.index.add(embeddings) | |
| self.doc_ids.extend(doc_ids) | |
| def search(self, query_embedding: np.ndarray, k: int = 5) -> List[int]: | |
| distances, indices = self.index.search(query_embedding, k) | |
| return [self.doc_ids[idx] for idx in indices[0]] | |
| class Retriever: | |
| """Recupera documentos relevantes para uma consulta.""" | |
| def __init__(self, knowledge_base: KnowledgeBase, vector_store: VectorStore, embedding_generator: EmbeddingGenerator): | |
| self.knowledge_base = knowledge_base | |
| self.vector_store = vector_store | |
| self.embedding_generator = embedding_generator | |
| def retrieve(self, query: str, k: int = 5) -> List[Dict]: | |
| query_embedding = self.embedding_generator.generate([query]) | |
| doc_ids = self.vector_store.search(query_embedding, k) | |
| return [self.knowledge_base.get_document(doc_id) for doc_id in doc_ids] | |
| class ResponseGenerator: | |
| """Gera respostas técnicas para perguntas metrológicas.""" | |
| def __init__(self, model_name: str = "gemini-2.0-flash-thinking-exp-1219"): | |
| self.model = genai.GenerativeModel(model_name) | |
| def generate(self, query: str, retrieved_docs: List[Dict]) -> str: | |
| context = "\n".join([doc["content"] for doc in retrieved_docs]) | |
| prompt = ( | |
| "Você é um especialista em metrologia, com conhecimento em normas como ISO/IEC 17025, incerteza de medição, " | |
| "calibração e rastreabilidade. Com base no contexto fornecido, responda à pergunta de forma técnica, precisa e clara:\n\n" | |
| f"Contexto:\n{context}\n\nPergunta: {query}\n\nResposta:" | |
| ) | |
| try: | |
| response = self.model.generate_content(prompt) | |
| return response.text if response else "Desculpe, não consegui gerar uma resposta." | |
| except Exception as e: | |
| return f"Erro ao gerar resposta: {str(e)}" | |
| class CacheManager: | |
| """Gerencia cache de respostas.""" | |
| def __init__(self, cache_file: str = "metrology_cache.pkl"): | |
| self.cache_file = cache_file | |
| self.cache = self._load_cache() | |
| def _load_cache(self) -> Dict: | |
| if os.path.exists(self.cache_file): | |
| with open(self.cache_file, "rb") as f: | |
| return pickle.load(f) | |
| return {} | |
| def _save_cache(self): | |
| with open(self.cache_file, "wb") as f: | |
| pickle.dump(self.cache, f) | |
| def get(self, query: str) -> Optional[str]: | |
| return self.cache.get(query) | |
| def set(self, query: str, response: str): | |
| self.cache[query] = {"response": response, "timestamp": datetime.now()} | |
| self._save_cache() | |
| class QueryProcessor: | |
| """Pré-processa consultas com foco em metrologia.""" | |
| def __init__(self): | |
| self.glossary = MetrologyGlossary() | |
| def process(self, query: str) -> str: | |
| query = query.strip().lower() | |
| return self.glossary.enhance_query(query) | |
| class MetrologyRAGPipeline: | |
| """Orquestra o agente de metrologia avançado.""" | |
| def __init__(self): | |
| self.knowledge_base = KnowledgeBase() | |
| self.embedding_generator = EmbeddingGenerator() | |
| self.vector_store = VectorStore(dimension=384) | |
| self.retriever = Retriever(self.knowledge_base, self.vector_store, self.embedding_generator) | |
| self.response_generator = ResponseGenerator() | |
| self.cache_manager = CacheManager() | |
| self.query_processor = QueryProcessor() | |
| self.document_parser = DocumentParser() | |
| def load_pdfs(self, pdf_paths: List[str] = None, pdf_folder: Optional[str] = None): | |
| """Carrega N arquivos PDF de uma lista de caminhos ou pasta.""" | |
| if pdf_paths and pdf_folder: | |
| raise ValueError("Forneça apenas pdf_paths ou pdf_folder, não ambos.") | |
| if pdf_folder: | |
| pdf_paths = [str(p) for p in Path(pdf_folder).glob("*.pdf")] | |
| if not pdf_paths: | |
| print("Nenhum arquivo PDF fornecido ou encontrado.") | |
| return | |
| print(f"Carregando {len(pdf_paths)} arquivos PDF...") | |
| documents = self.document_parser.parse_multiple_pdfs(pdf_paths) | |
| if documents: | |
| self.knowledge_base.add_documents(documents) | |
| self._index_documents() | |
| print(f"{len(documents)} documentos indexados com sucesso.") | |
| else: | |
| print("Nenhum documento válido foi extraído dos PDFs.") | |
| def _index_documents(self): | |
| contents = self.knowledge_base.get_all_contents() | |
| if not contents: | |
| return | |
| embeddings = self.embedding_generator.generate(contents) | |
| doc_ids = list(range(len(contents))) | |
| self.vector_store.add_vectors(embeddings, doc_ids) | |
| def query(self, query: str, k: int = 5) -> str: | |
| processed_query = self.query_processor.process(query) | |
| cached_response = self.cache_manager.get(processed_query) | |
| if cached_response: | |
| return f"[Resposta do cache] {cached_response}" | |
| retrieved_docs = self.retriever.retrieve(processed_query, k) | |
| response = self.response_generator.generate(processed_query, retrieved_docs) | |
| self.cache_manager.set(processed_query, response) | |
| return response | |
| # # Exemplo de uso | |
| if __name__ == "__main__": | |
| # Inicializa o pipeline | |
| rag = MetrologyRAGPipeline() | |
| # Carrega N arquivos PDF de uma pasta | |
| pdf_folder = "/content/" # Substitua pelo caminho real | |
| rag.load_pdfs(pdf_folder=pdf_folder) | |
| # Alternativamente, carrega PDFs específicos | |
| pdf_paths = [ | |
| # "caminho/para/manual_calibrador.pdf", | |
| # "caminho/para/iso_17025.pdf", | |
| # Adicione mais caminhos | |
| ] | |
| # rag.load_pdfs(pdf_paths=pdf_paths) | |
| # Faz uma consulta técnica | |
| pergunta = "faça uma avaliação sobre o documento CERTIFICADO DE CALIBRAÇÃO N RBC 25/0018" | |
| resposta = rag.query(pergunta) | |
| print("Agente de Metrologia:", resposta) |