# # src/tools/build_vectorstore.py # ESTE CODIGO SE COMENTA PORQUE NO FUNCIONA LA API DE ARXVI ME BLOQUEO LA IP DESCARGUE LOS PDF A MANO # # ===================================================================== # # SCRIPT DE CONSTRUCCIÓN DE LA BASE DE DATOS VECTORIAL # # # # Este script se ejecuta UNA SOLA VEZ desde la terminal con: # # python src/tools/build_vectorstore.py # # # # Lo que hace: # # 1. Descarga papers de arXiv sobre Inteligencia Artificial # # 2. Los trocea en fragmentos pequeños (chunks) # # 3. Convierte cada fragmento en vectores numéricos (embeddings) # # 4. Guarda todo en disco en la carpeta "vectorstore_ia/" # # # # La próxima vez que el usuario pregunte algo, el RAG leerá # # directamente de esa carpeta SIN necesitar llamar a arXiv. # # ===================================================================== # import logging # import os # from langchain_community.document_loaders import ArxivLoader # from langchain_text_splitters import RecursiveCharacterTextSplitter # from langchain_huggingface import HuggingFaceEmbeddings # from langchain_community.vectorstores import FAISS # # Configuramos los logs para ver qué está pasando # logging.basicConfig( # level=logging.INFO, # format="%(asctime)s [%(levelname)s] %(message)s" # ) # logger = logging.getLogger("build_vectorstore") # # ===================================================================== # # CONFIGURACIÓN — aquí defines qué papers descargar # # Puedes añadir más temas simplemente añadiendo más strings a la lista # # ===================================================================== # TEMAS_A_DESCARGAR = [ # "artificial intelligence", # "machine learning", # "deep learning", # "natural language processing", # "transformer neural networks", # ] # # Cuántos papers descargar por tema (2 es suficiente para una PoC) # PAPERS_POR_TEMA = 2 # # Dónde guardar la base de datos vectorial en disco # # os.path.dirname(__file__) → carpeta donde está este script (src/tools/) # # "../.." → subimos dos niveles hasta la raíz del proyecto # RUTA_VECTORSTORE = os.path.join( # os.path.dirname(__file__), "..", "..", "vectorstore_ia" # ) # def descargar_papers(tema: str) -> list: # """ # Descarga papers de arXiv sobre un tema concreto. # Devuelve una lista de documentos de LangChain. # """ # logger.info(f" Descargando papers sobre '{tema}'...") # try: # loader = ArxivLoader( # query=tema, # load_max_docs=PAPERS_POR_TEMA # ) # docs = loader.load() # logger.info(f" ✅ {len(docs)} papers descargados sobre '{tema}'") # for doc in docs: # titulo = doc.metadata.get("Title", "Sin título") # logger.info(f" - {titulo}") # return docs # except Exception as e: # logger.error(f" ❌ Error descargando '{tema}': {str(e)}") # return [] # si falla un tema, continuamos con los demás # def construir_vectorstore(): # """ # Función principal que construye y guarda la base de datos vectorial. # """ # logger.info("=" * 60) # logger.info("INICIANDO CONSTRUCCIÓN DE LA BASE DE DATOS VECTORIAL") # logger.info("=" * 60) # # ------------------------------------------------------------------ # # PASO 1: DESCARGA DE PAPERS # # Descargamos papers de arXiv para cada tema de la lista # # Si un tema falla (rate limit, etc.) continuamos con el siguiente # # ------------------------------------------------------------------ # logger.info(f"PASO 1: Descargando papers de {len(TEMAS_A_DESCARGAR)} temas...") # todos_los_documentos = [] # for tema in TEMAS_A_DESCARGAR: # docs = descargar_papers(tema) # todos_los_documentos.extend(docs) # añadimos a la lista total # logger.info(f"PASO 1 OK: Total de documentos descargados: {len(todos_los_documentos)}") # if not todos_los_documentos: # logger.error("No se descargó ningún documento. Abortando.") # return # # ------------------------------------------------------------------ # # PASO 2: CHUNKING # # Troceamos todos los documentos en fragmentos de 1000 caracteres # # ------------------------------------------------------------------ # logger.info("PASO 2: Troceando documentos en chunks...") # text_splitter = RecursiveCharacterTextSplitter( # chunk_size=1000, # chunk_overlap=150 # ) # chunks = text_splitter.split_documents(todos_los_documentos) # logger.info(f"PASO 2 OK: {len(todos_los_documentos)} documentos → {len(chunks)} chunks") # # ------------------------------------------------------------------ # # PASO 3: EMBEDDINGS # # Convertimos cada chunk en un vector numérico # # La primera vez descarga el modelo (~90MB), las siguientes es rápido # # ------------------------------------------------------------------ # logger.info("PASO 3: Cargando modelo de embeddings...") # logger.info("(La primera vez puede tardar unos minutos descargando el modelo)") # modelo_embeddings = HuggingFaceEmbeddings( # model_name="sentence-transformers/all-MiniLM-L6-v2" # ) # logger.info("PASO 3 OK: Modelo de embeddings listo") # # ------------------------------------------------------------------ # # PASO 4: CREAR Y GUARDAR LA BASE DE DATOS VECTORIAL EN DISCO # # FAISS.from_documents() crea la base de datos en memoria # # .save_local() la guarda en disco para usarla después # # ------------------------------------------------------------------ # logger.info("PASO 4: Creando y guardando la base de datos vectorial...") # vector_store = FAISS.from_documents(chunks, modelo_embeddings) # # Creamos la carpeta si no existe # os.makedirs(RUTA_VECTORSTORE, exist_ok=True) # # Guardamos en disco — esto crea dos archivos: # # - index.faiss (los vectores) # # - index.pkl (los textos originales) # vector_store.save_local(RUTA_VECTORSTORE) # logger.info(f"PASO 4 OK: Base de datos guardada en '{RUTA_VECTORSTORE}'") # logger.info("=" * 60) # logger.info("✅ BASE DE DATOS VECTORIAL CONSTRUIDA CON ÉXITO") # logger.info(f" Documentos: {len(todos_los_documentos)}") # logger.info(f" Chunks: {len(chunks)}") # logger.info(f" Guardada en: vectorstore_ia/") # logger.info("=" * 60) # logger.info("Ahora puedes ejecutar tu app con: streamlit run src/ui/app.py") # # Esto hace que el script se ejecute solo cuando lo llamas directamente # # y no cuando otro archivo lo importa # if __name__ == "__main__": # construir_vectorstore() # src/tools/build_vectorstore.py # ===================================================================== # SCRIPT DE CONSTRUCCIÓN DE LA BASE DE DATOS VECTORIAL # # Este script se ejecuta UNA SOLA VEZ desde la terminal con: # python src/tools/build_vectorstore.py # # Lo que hace: # 1. Lee los PDFs de la carpeta papers_ia/ # 2. Los trocea en fragmentos pequeños (chunks) # 3. Convierte cada fragmento en vectores numéricos (embeddings) # 4. Guarda todo en disco en la carpeta vectorstore_ia/ # ===================================================================== # src/tools/build_vectorstore.py # ===================================================================== # SCRIPT DE CONSTRUCCIÓN DE LA BASE DE DATOS VECTORIAL # # Ejecutar desde la raíz del proyecto (project-ai-llms-V01/) con: # python src/tools/build_vectorstore.py # ===================================================================== import logging import os from langchain_community.document_loaders import PyMuPDFLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_huggingface import HuggingFaceEmbeddings from langchain_community.vectorstores import FAISS logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s" ) logger = logging.getLogger("build_vectorstore") # ===================================================================== # RUTAS # Este script está en: project-ai-llms-V01/src/tools/build_vectorstore.py # Subimos dos niveles con "../.." para llegar a project-ai-llms-V01/ # ===================================================================== RAIZ_PROYECTO = os.path.abspath( os.path.join(os.path.dirname(__file__), "..", "..") ) CARPETA_PDFS = os.path.join(RAIZ_PROYECTO, "papers_ia") RUTA_VECTORSTORE = os.path.join(RAIZ_PROYECTO, "vectorstore_ia") logger.info(f"Raíz del proyecto detectada: {RAIZ_PROYECTO}") logger.info(f"Buscando PDFs en: {CARPETA_PDFS}") logger.info(f"Guardando vectorstore en: {RUTA_VECTORSTORE}") def construir_vectorstore(): """ Función principal que construye y guarda la base de datos vectorial. """ logger.info("=" * 60) logger.info("INICIANDO CONSTRUCCIÓN DE LA BASE DE DATOS VECTORIAL") logger.info("=" * 60) # ------------------------------------------------------------------ # PASO 1: LEER LOS PDFs DEL DISCO # ------------------------------------------------------------------ logger.info("PASO 1: Leyendo PDFs de la carpeta papers_ia/...") # Comprobamos que la carpeta existe if not os.path.exists(CARPETA_PDFS): logger.error(f"No existe la carpeta: {CARPETA_PDFS}") logger.error("Crea la carpeta papers_ia/ dentro de project-ai-llms-V01/ y añade los PDFs") return # ✅ FIX: archivos_pdf se define aquí dentro de construir_vectorstore() # para que esté disponible en todo el resto de la función archivos_pdf = [ f for f in os.listdir(CARPETA_PDFS) if f.endswith(".pdf") ] if not archivos_pdf: logger.error(f"No hay archivos PDF en: {CARPETA_PDFS}") return logger.info(f"Encontrados {len(archivos_pdf)} PDFs:") for nombre in archivos_pdf: logger.info(f" - {nombre}") # Leemos cada PDF con PyMuPDFLoader todos_los_documentos = [] for nombre_pdf in archivos_pdf: ruta_completa = os.path.join(CARPETA_PDFS, nombre_pdf) try: logger.info(f" Leyendo '{nombre_pdf}'...") loader = PyMuPDFLoader(ruta_completa) docs = loader.load() todos_los_documentos.extend(docs) logger.info(f" ✅ '{nombre_pdf}' leído ({len(docs)} páginas)") except Exception as e: logger.error(f" ❌ Error leyendo '{nombre_pdf}': {str(e)}") continue if not todos_los_documentos: logger.error("No se pudo leer ningún documento. Abortando.") return logger.info(f"PASO 1 OK: {len(todos_los_documentos)} páginas leídas en total") # ------------------------------------------------------------------ # PASO 2: CHUNKING # ------------------------------------------------------------------ logger.info("PASO 2: Troceando documentos en chunks...") text_splitter = RecursiveCharacterTextSplitter( chunk_size=1000, chunk_overlap=150 ) chunks = text_splitter.split_documents(todos_los_documentos) logger.info(f"PASO 2 OK: {len(todos_los_documentos)} páginas → {len(chunks)} chunks") # ------------------------------------------------------------------ # PASO 3: EMBEDDINGS # ------------------------------------------------------------------ logger.info("PASO 3: Cargando modelo de embeddings...") logger.info("(La primera vez puede tardar unos minutos descargando el modelo)") try: modelo_embeddings = HuggingFaceEmbeddings( model_name="sentence-transformers/all-MiniLM-L6-v2" ) logger.info("PASO 3 OK: Modelo de embeddings listo") except Exception as e: logger.error(f"PASO 3 ERROR: {str(e)}") return # ------------------------------------------------------------------ # PASO 4: CREAR Y GUARDAR EN DISCO # ------------------------------------------------------------------ logger.info("PASO 4: Creando y guardando la base de datos vectorial...") try: vector_store = FAISS.from_documents(chunks, modelo_embeddings) os.makedirs(RUTA_VECTORSTORE, exist_ok=True) vector_store.save_local(RUTA_VECTORSTORE) logger.info(f"PASO 4 OK: Base de datos guardada en 'vectorstore_ia/'") except Exception as e: logger.error(f"PASO 4 ERROR: {str(e)}") return # ✅ FIX: ahora archivos_pdf sí está definido aquí logger.info("=" * 60) logger.info("✅ BASE DE DATOS VECTORIAL CONSTRUIDA CON ÉXITO") logger.info(f" PDFs procesados: {len(archivos_pdf)}") logger.info(f" Páginas leídas: {len(todos_los_documentos)}") logger.info(f" Chunks generados: {len(chunks)}") logger.info(f" Guardada en: vectorstore_ia/") logger.info("=" * 60) logger.info("Ahora ejecuta: uv run streamlit run src/ui/app.py") if __name__ == "__main__": construir_vectorstore()