¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Preguntas y respuestas a documentos
Vamos a ver el modo en que los LLM gestionan la información de documentos, de modo que podamos preguntar a un modelo acerca de la información contenida en los mismos.
Para ello vamos a dividir este proceso en varias partes que vamos a ver de forma independiente:
- Carga de documentos (información).
- “Troceado” de documentos, para hacer manejable la información.
- Guardado de información en base de datos vectorial con embeddings.
- Retrieval: A partir de una consulta query se extrae de la base de datos vectorial la información requerida. Esto se hace a través de una búsqueda semántica.
- Salida: A través de un prompt, el LLM utiliza la información proporcionada para tener un contexto enriquecido y que permita obtener mejores y más precisas respuestas.
RAG
Hay un concepto muy importante en el campo de la inteligencia artificial generativa, que vamos a ver en este apartado: RAG Retrieval Augmented Generation.
RAG es una técnica que combina modelos generativos de lenguaje (como GPT o Llama) con sistemas de recuperación de información para mejorar la precisión y relevancia de las respuestas generadas.
RAG tiene dos componentes principales, que se corresponden conlas dos últimas fases vistas anteriormente:
- Retrieval: Este componente busca información relevante en una base de datos, un conjunto de documentos o cualquier fuente de conocimiento. Utiliza técnicas de búsqueda, a menudo basadas en embeddings (representaciones vectoriales), para encontrar los fragmentos de texto más relevantes en respuesta a una consulta.
- Modelo generativo (Generator): Una vez que el módulo de recuperación obtiene información relevante, se alimenta al modelo generativo. El modelo utiliza esta información como contexto para generar respuestas más precisas y específicas.
De este modo, la técnica RAG permite buscar recuperar información de fuentes de datos concretas, de odo que obtenemos respuestas más “controladas” y precisas, disminuyendo el riesgo de alucinación, y pudiendo desarrollar aplicaciones que requieran conocimientos más especializados.
Carga de información
En primer lugar debemos recopilar información de las fuentes que necesitemos.
Estas fuentes pueden ser bases de datos, documentos PDF, documentos JSON, CSV, videos de youtube (en LLMs multimodales), etc.
Para cargar la información, langchain proporciona Loaders para cada tipo de archivo o formato. Langchain cuenta actualmente con más de 80 loaders, que pueden consultarse en su página web.
Estos loaders pueden cargar tanto información no estructurada (entradas de wikipedia, tweets de X, archivos ePub, documentos de Notion, etc), estructurados (documentos JSON, CSV, datasets de Hugging Face, hojas de cálculo Excel, etc.), si disponemos de un LLM con capacidad speech to text (por ejemplo API de whisper de OpenAI) podemos incluso obtener transcripciones de videos de youtube, .
Cuando usamos un loader, éste devuelve una lista de objetos tipo “document” (por eso se dice “carga de documentos”, aunque realmente sea “información” y no un determinado tipo de documento). Este tipo “document” contiene el contenido (texto) y metadatos.
A continuación vemos un ejemplo en el que cargaremos PDFs con PyPDFLoader:
# Vamos a cargar información a través de Loaders para PDF # Dependencias from langchain_ollama import ChatOllama from langchain_community.document_loaders import PyPDFLoader # Cargamos el modelo LLM llm = ChatOllama( model = "llama3.2", temperature = 0.0, verbose = True ) # Cargamos un documento PDF #loader = PyPDFLoader("sentencia.pdf") loader = PyPDFLoader("ADM.pdf") # Cargamos las páginas, de modo que tenemos una lista de páginas, cada una con su contenido y metadatos paginas = loader.load() print(len(paginas)) pagina = paginas[7] print(pagina.page_content[0:500]) print(pagina.metadata)
Troceado de documentos
La información almacenada debe “trocearse” para poder trabajar con ella sin problemas de tener ventanas de contexto demasiado altas, que reduzcan el desempeño del LLM.
Puede resultar sencillo conceptualmente, pero la forma de trocear la información tendrá un alto impacto en que el modelo sea capaz de encontrar la información deseada y realice su función adecuadamente.
Es importante que cada “trozo” de información contenga ideas o datos completos, de forma que se pueda acceder a la información de forma adecuada. Si quedase parte de una información concreta dividida en dos partes diferentes, el acceso a la misma no sería adecuado y el LLM no daría una respuesta correcta.
Cuando especificamos el text splitter no sólo indicamos el tamaño del “trozo” de información, si no un tamaño de “overlap”, es decir, solapamiento. Hay una parte solapada de información, es decir, el trozo siguiente tiene una cantidad determinada de caracteres (o tokens, lo que corresponda) del trozo anterior, con el objetivo de no perder la integridad semántica de la información.
El tamaño de las partes puede ser definidas a partir de tokens o de caracteres. Es importante que mantengan una cohesión de metadatos, de forma que partes específicas tengan metadatos específicos, lo que ayudará en la búsqueda de información.
A continuación algunos tipos de text splitters proporcionados por Langchain en langchain.text_splitter.:
- CharacterTextSplitter()
- MarkdownHeaderTextSplitter
- TokenTextSplitter()
- SentenceTransformersTokenTextSplitter()
- RecursiveCharacterTextSplitter()
- Language
- NLTKTextSplitter()
- SpacyTextSplitter()
En el siguiente ejemplo vamos a usar 2 text splitters comunes, recursive character text splitter y character text splitter:
Guardado de información
Retrieval
Salida
Embeddings
Los modelos de lenguaje, o LLM, sólo pueden examinar unos pocos miles de palabras de una vez, por lo que tenemos que buscar una solución para documentos que sean largos.
Para almacenar la información de los documentos usamos embeddings. Los embeddings crean representaciones numéricas de partes de texto. Estas representaciones, o vectores numéricos, capturan de alguna manera el significado de estas partes de texto, de forma que textos con contenidos similares, tendrán vectores similares.
En el ejemplo de la imagen, los dos primeros casos hablan de mascotas, mientras que el tercero habla de un coche, por lo que los vectores entre los dos primeros son similares (cerca espacialmente, dado que son vectores), mientras que el tercero es muy diferente. Ya intuimos que esto se usa para realizar búsquedas de información.
Base de datos de vectores
A la base de datos de vectores le pasamos los vectores resultantes de aplicar los embeddings al texto de nuestros documentos.
Si los documentos son muy grandes, hay que “trocearlos” en partes más pequeñas para ayudar al modelo a procesar la información (tienen una capacidad limitada de cantidad de tokens a procesar de una vez). Se pasarán al modelo los “trozos” de información más relevantes para la consulta realizada. Estos trozos de información, son habitualmente denominados “documentos”.
Para pasar al modelo las partes más importantes del texto que requiera la consulta, necesitaremos crear un índice index que busque entre los vectores de las diferentes porciones de información.
La consulta se pasa a un vector a través de un embedding, y se compara con los diferentes vectores de cada porción de información. Se devuelven al modelo los más parecidos para poder devolver la respuesta definitiva.
Métodos de extracción de información
Hay varios métodos a partir de los cuales se puede extraer la información más importante (la más similar vectorialmente) de las diferentes partes en que se dividió la información.
Stuff
Este método es el más sencillo y consiste en pasarle TODA la información a la consulta como contexto y de ahí al modelo.
- Pros: Una única llamada al LLM. Se tiene acceso a todos los datos.
- Contras: Si hay muchos datos, puedes salirte de la ventana de contexto.
Es el método más usado, junto con Map reduce.
Map reduce
Cada trozo de texto se pasa junto con la consulta a un LLM que da respuestas individuales, y cada una de estas pasa a otro LLM que resume todas las respuestas y da una respuesta final.
Es muy potente y permite la inspección de muchos “documentos” (porciones de información), pero e hacen muchas llamadas a LLM. Además, no siempre se puede tratar a los “documentos” de forma independiente.
Es el método más usado, después de Stuff. También viene bien para realizar resúmenes.
Refine
Va construyendo la respuesta en función del “documento” (porción de información) anterior.
Muy bueno para combinar información e ir construyendo una respuesta en el tiempo.
Da lugar a respuestas largas, pero es muy lento.










