Spaces:
Build error
Build error
| #!/usr/bin/env python3 | |
| """ | |
| RAG Builder usando LangChain + HuggingFaceEmbeddings (CPU) | |
| Versão CORRIGIDA com logging detalhado para debug | |
| """ | |
| import os | |
| import sys | |
| import json | |
| import argparse | |
| from pathlib import Path | |
| from typing import List, Dict | |
| import logging | |
| import traceback | |
| from langchain.docstore.document import Document | |
| from langchain_community.embeddings import HuggingFaceEmbeddings | |
| from langchain_community.vectorstores import FAISS | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(levelname)s - %(message)s' | |
| ) | |
| logger = logging.getLogger(__name__) | |
| def load_jsonl(filepath: str) -> List[Dict]: | |
| """Carrega registros de arquivo JSONL""" | |
| records = [] | |
| try: | |
| logger.info(f"📂 Abrindo arquivo: {filepath}") | |
| if not os.path.exists(filepath): | |
| raise FileNotFoundError(f"Arquivo não encontrado: {filepath}") | |
| file_size = os.path.getsize(filepath) | |
| logger.info(f"📊 Tamanho do arquivo: {file_size:,} bytes") | |
| with open(filepath, 'r', encoding='utf-8') as f: | |
| for i, line in enumerate(f, 1): | |
| if line.strip(): | |
| try: | |
| records.append(json.loads(line)) | |
| except json.JSONDecodeError as e: | |
| logger.warning(f"⚠️ Linha {i} inválida: {e}") | |
| continue | |
| if i % 50000 == 0: | |
| logger.info(f" Lidas {i:,} linhas...") | |
| logger.info(f"✅ {len(records):,} registros carregados") | |
| return records | |
| except Exception as e: | |
| logger.error(f"❌ Erro ao carregar JSONL: {type(e).__name__}: {e}") | |
| raise | |
| def create_documents(records: List[Dict]) -> List[Document]: | |
| """Converte registros JSONL em Documents do LangChain""" | |
| documents = [] | |
| skipped = 0 | |
| logger.info("📄 Criando Documents do LangChain...") | |
| for i, record in enumerate(records, 1): | |
| doc_id = record.get('id', f'unknown_{i}') | |
| ementa = record.get('ementa', '') | |
| if not ementa or not ementa.strip(): | |
| skipped += 1 | |
| continue | |
| doc = Document( | |
| page_content=ementa, | |
| metadata={ | |
| 'id': str(doc_id), | |
| 'source': 'tjpr_jurisprudencia' | |
| } | |
| ) | |
| documents.append(doc) | |
| if i % 50000 == 0: | |
| logger.info(f" Processados {i:,}/{len(records):,} registros...") | |
| logger.info(f"✅ {len(documents):,} documentos criados") | |
| if skipped > 0: | |
| logger.info(f"⚠️ {skipped:,} registros sem ementa (ignorados)") | |
| return documents | |
| def build_vectorstore( | |
| input_file: str, | |
| output_dir: str = '/app/faiss_index', | |
| model_name: str = 'sentence-transformers/all-MiniLM-L6-v2', | |
| batch_size: int = 16 | |
| ): | |
| """ | |
| Constrói FAISS vector store com logging detalhado | |
| """ | |
| try: | |
| logger.info("="*80) | |
| logger.info("🚀 RAG Builder - LangChain + FAISS (CPU)") | |
| logger.info("="*80) | |
| logger.info(f"📅 Início: {__import__('datetime').datetime.now()}") | |
| logger.info("") | |
| # ==================================================================== | |
| # PASSO 1: Carregar JSONL | |
| # ==================================================================== | |
| logger.info("PASSO 1/5: Carregando arquivo JSONL") | |
| logger.info("-"*80) | |
| records = load_jsonl(input_file) | |
| if not records: | |
| raise ValueError("❌ Nenhum registro encontrado no arquivo JSONL!") | |
| logger.info("") | |
| # ==================================================================== | |
| # PASSO 2: Criar Documents | |
| # ==================================================================== | |
| logger.info("PASSO 2/5: Convertendo para Documents do LangChain") | |
| logger.info("-"*80) | |
| documents = create_documents(records) | |
| if not documents: | |
| raise ValueError("❌ Nenhum documento válido criado!") | |
| logger.info("") | |
| # ==================================================================== | |
| # PASSO 3: Inicializar Embeddings | |
| # ==================================================================== | |
| logger.info("PASSO 3/5: Inicializando HuggingFaceEmbeddings") | |
| logger.info("-"*80) | |
| logger.info(f" 🤖 Modelo: {model_name}") | |
| logger.info(f" 💻 Device: CPU") | |
| logger.info(f" 📦 Batch size: {batch_size}") | |
| logger.info(f" 📏 Dimensões: 384") | |
| logger.info("") | |
| logger.info(" Baixando modelo do HuggingFace Hub...") | |
| logger.info(" (Primeira vez pode demorar ~1min)") | |
| embeddings = HuggingFaceEmbeddings( | |
| model_name=model_name, | |
| model_kwargs={'device': 'cpu'}, | |
| encode_kwargs={ | |
| 'batch_size': batch_size, | |
| 'show_progress_bar': True, | |
| 'normalize_embeddings': True | |
| } | |
| ) | |
| logger.info("✅ Embeddings inicializados com sucesso") | |
| logger.info("") | |
| # ==================================================================== | |
| # PASSO 4: Construir FAISS Index | |
| # ==================================================================== | |
| logger.info("PASSO 4/5: Construindo FAISS vector store") | |
| logger.info("-"*80) | |
| logger.info(f" 📊 Total de documentos: {len(documents):,}") | |
| logger.info(f" ⏱️ Tempo estimado: ~{len(documents) // 500:.0f} minutos") | |
| logger.info("") | |
| logger.info(" Processando embeddings (isso vai demorar)...") | |
| import time | |
| start_time = time.time() | |
| vectorstore = FAISS.from_documents(documents, embeddings) | |
| elapsed = time.time() - start_time | |
| logger.info(f"✅ FAISS index construído em {elapsed:.1f} segundos") | |
| logger.info(f" 📈 Velocidade: {len(documents) / elapsed:.0f} docs/seg") | |
| logger.info("") | |
| # ==================================================================== | |
| # PASSO 5: Salvar Index | |
| # ==================================================================== | |
| logger.info("PASSO 5/5: Salvando FAISS index") | |
| logger.info("-"*80) | |
| logger.info(f" 💾 Diretório: {output_dir}") | |
| os.makedirs(output_dir, exist_ok=True) | |
| vectorstore.save_local(output_dir) | |
| # Verificar tamanho do index salvo | |
| index_files = list(Path(output_dir).glob('*')) | |
| total_size = sum(f.stat().st_size for f in index_files if f.is_file()) | |
| logger.info(f"✅ Index salvo com sucesso") | |
| logger.info(f" 📁 Arquivos: {len(index_files)}") | |
| logger.info(f" 📊 Tamanho total: {total_size / 1024 / 1024:.1f} MB") | |
| logger.info("") | |
| # ==================================================================== | |
| # RESUMO FINAL | |
| # ==================================================================== | |
| logger.info("="*80) | |
| logger.info("✅ BUILD COMPLETO!") | |
| logger.info("="*80) | |
| logger.info(f"📊 Documentos indexados: {len(documents):,}") | |
| logger.info(f"📁 Index salvo em: {output_dir}") | |
| logger.info(f"🤖 Modelo: {model_name}") | |
| logger.info(f"📏 Dimensões: 384") | |
| logger.info(f"⏱️ Tempo total: {time.time() - start_time:.1f}s") | |
| logger.info("="*80) | |
| return vectorstore | |
| except ImportError as e: | |
| logger.error("") | |
| logger.error("="*80) | |
| logger.error("❌ ERRO DE IMPORTAÇÃO") | |
| logger.error("="*80) | |
| logger.error(f"Módulo faltando: {e}") | |
| logger.error("") | |
| logger.error("SOLUÇÃO:") | |
| logger.error(" Verificar se requirements.txt inclui:") | |
| logger.error(" - langchain==0.1.11") | |
| logger.error(" - langchain-community==0.0.24") | |
| logger.error(" - sentence-transformers==2.5.1") | |
| logger.error(" - torch==2.2.0") | |
| logger.error(" - transformers==4.37.2") | |
| logger.error(" - faiss-cpu==1.8.0") | |
| logger.error("="*80) | |
| raise | |
| except FileNotFoundError as e: | |
| logger.error("") | |
| logger.error("="*80) | |
| logger.error("❌ ARQUIVO NÃO ENCONTRADO") | |
| logger.error("="*80) | |
| logger.error(f"{e}") | |
| logger.error("") | |
| logger.error("SOLUÇÃO:") | |
| logger.error(" Verificar se setup.py criou /tmp/filtered.jsonl") | |
| logger.error(" Verificar se filter_fields.py foi executado") | |
| logger.error("="*80) | |
| raise | |
| except Exception as e: | |
| logger.error("") | |
| logger.error("="*80) | |
| logger.error("❌ ERRO DURANTE BUILD FAISS") | |
| logger.error("="*80) | |
| logger.error(f"Tipo: {type(e).__name__}") | |
| logger.error(f"Mensagem: {str(e)}") | |
| logger.error("") | |
| logger.error("Traceback completo:") | |
| logger.error("-"*80) | |
| logger.error(traceback.format_exc()) | |
| logger.error("="*80) | |
| raise | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description='Build FAISS vector store from JSONL' | |
| ) | |
| parser.add_argument('--input', required=True, help='Input JSONL file') | |
| parser.add_argument('--output', default='/app/faiss_index', help='Output directory') | |
| parser.add_argument( | |
| '--model', | |
| default='sentence-transformers/all-MiniLM-L6-v2', | |
| help='HuggingFace model name' | |
| ) | |
| parser.add_argument('--batch-size', type=int, default=16, help='Batch size') | |
| args = parser.parse_args() | |
| build_vectorstore( | |
| input_file=args.input, | |
| output_dir=args.output, | |
| model_name=args.model, | |
| batch_size=args.batch_size | |
| ) | |
| if __name__ == '__main__': | |
| main() | |