Contenido del curso

Introducción a LangChain

Chats y memoria con LangChain

Cómo partir documentos JSONL para Chroma

Resumen

Construir un sistema de retrieval augmented generation empieza por algo más humilde de lo que parece: cargar bien tus documentos y partirlos en pedazos del tamaño correcto. Aquí aprenderás a transformar un archivo JSONL con la documentación de Hugging Face en documents de LangChain listos para indexar en Chroma, usando un text splitter recursivo y embeddings.

¿Qué pipeline sigue un sistema RAG con LangChain y Chroma?

Antes de escribir código, conviene tener clara la ruta. La idea es ir del archivo crudo al índice vectorial paso a paso, sin saltarte transformaciones.

  • Cargar el JSONL y convertir cada línea en un document de LangChain.
  • Partir esos documents en fragmentos más pequeños con un text splitter.
  • Convertir cada fragmento en vectores numéricos usando embeddings de Hugging Face.
  • Guardar los vectores en una base de datos vectorial de Chroma para crear el índice.
  • Hacer consultas desde un modelo de chat que recupera los fragmentos más parecidos.

Con ese mapa en mente, el primer tramo es el que cubrimos hoy: cargar y partir [0:30].

¿Cómo crear la función load_documents en Python?

El punto de entrada es un archivo nuevo, por ejemplo conversation_ai.py, donde defines una función main que orquesta todo y una función load_documents que recibe un file path tipo string [1:30].

Dentro de load_documents se usa el loader personalizado docs_jsonl_loader, que ya viene definido en utils y se encarga de transformar cada línea del JSONL en un document de LangChain. La importación se hace con from utils import docs_jsonl_loader, y al instanciar el loader se llama al método load() para guardar todo en una variable data.

¿Qué es un document en LangChain? Es una estructura con dos partes: page_content, que contiene el texto, y metadata, que guarda información extra como autor, título o fuente. Es el formato estándar que esperan los componentes de LangChain.

Así, en pocas líneas pasas de un archivo plano a una colección de objetos manipulables.

¿Por qué usar RecursiveCharacterTextSplitter y cómo configurarlo?

Los modelos de embeddings tienen un límite de tokens por entrada. Si mandas un document gigante, lo trunca o lo rechaza. Por eso necesitas dividirlo en chunks del tamaño adecuado.

La importación es from langchain.text_splitter import RecursiveCharacterTextSplitter [2:50]. Este splitter corta el texto buscando separadores naturales como saltos de párrafo, luego saltos de línea, luego espacios, hasta lograr el tamaño objetivo sin romper frases a la mitad.

Los dos parámetros clave son:

  • chunk_size: tamaño máximo de cada fragmento. En el ejemplo se usan 1600 caracteres medidos con la función len, pensando en embeddings grandes como los de OpenAI.
  • chunk_overlap: cuántos caracteres se repiten entre un fragmento y el siguiente. Aquí se elige el 10% de 1600, es decir 160, para que cada chunk mantenga contexto del anterior.

¿Para qué sirve el chunk overlap? Evita que una idea quede cortada justo en el borde entre dos fragmentos. Al solapar caracteres, el modelo de búsqueda puede recuperar el contexto completo aunque la respuesta esté a caballo entre dos chunks.

La función termina con return text_splitter.split_documents(data), que devuelve la lista de documents ya partidos y listos para vectorizar.

¿Cómo obtener el file path correcto desde utils?

En la función main se importa get_file_path desde utils. Esa función resuelve automáticamente la ruta hacia los documentos descargados, así no tienes que hardcodear paths que se rompan al cambiar de máquina o de sistema operativo.

¿Qué resultado obtienes al correr el código con Poetry?

Al ejecutar poetry run python conversation_ai.py, el script carga el JSONL, lo convierte y lo parte. Si imprimes solo documents, la salida es ilegible, así que conviene refinar el print [4:40].

Dos prints útiles son: el total de documentos generados y el contenido del primer fragmento. Con eso confirmas dos cosas en segundos.

  • El conteo final: pasas de alrededor de 300 documentos originales a 1807 fragmentos después del split. Es la señal de que el splitter está haciendo su trabajo.
  • La estructura del primer document: en page_content aparece el texto del fragmento, y en metadata se conservan campos como el creador del repo, el título (por ejemplo Accelerate) y la fuente (la documentación de Transformers).

Ese salto de 300 a 1807 es justo lo que quieres: piezas pequeñas, con metadata intacta, listas para entrar al modelo de embeddings en la siguiente etapa y terminar indexadas en Chroma.

¿Qué chunk_size usarías tú según el modelo de embeddings que tengas en mente? Cuéntalo en los comentarios.