Cuando trabajas con documentos extensos que superan la ventana de contexto de tu modelo de lenguaje, necesitas una estrategia inteligente para no perder información relevante ni generar errores. La solución está en crear una jerarquía de fragmentos que combine especificidad y contexto, utilizando lo que se conoce como Parent Document Retriever.
¿Qué son los documentos padre y por qué los necesitas?
El problema es claro: si tus documentos originales son demasiado grandes, no caben en la ventana de contexto y el modelo simplemente no puede procesarlos. Pero tampoco quieres quedarte solo con fragmentos diminutos que pierden contexto.
La idea es crear documentos padre, que son fragmentos grandes pero manejables, a medio camino entre el documento completo y los fragmentos hijos. Los documentos hijos se enfocan en la especificidad, mientras que los padres ofrecen un contexto más amplio sin ser el documento gigante original.
Para implementarlo se define una función llamada Parent Text Splitter [0:24], que parte los documentos enormes en piezas más pequeñas pero con mayor ventana de contexto que los hijos.
¿Cómo se configura el tamaño de los fragmentos padre?
El parámetro clave es el chunk size. No puede ser igual al de los hijos, porque existe una jerarquía entre padre e hijo [0:52]. En el ejemplo práctico:
- Los documentos padre tienen un tamaño de 400 tokens con 40 de overlap.
- Los documentos hijos tienen un tamaño de 100 tokens.
- Esto genera una relación de uno a cuatro: los padres son cuatro veces más grandes que los hijos.
Este overlap de 40 tokens asegura que no se pierda información en los bordes de cada fragmento, manteniendo coherencia entre chunks consecutivos.
¿Qué componentes se necesitan para armar el retriever?
La configuración requiere los mismos elementos base [1:16]:
- Un child splitter para los fragmentos pequeños.
- Una vector store (en este caso la colección se llamó big fragments para diferenciarla).
- Una store en memoria para los documentos.
- El Parent Document Retriever, al que ahora también se le especifica el parent splitter.
Una vez creado, se agregan los documentos a la vector store y se espera a que el proceso termine.
¿Qué resultados produce este enfoque de fragmentación?
Al verificar la longitud de documentos en el store, el número cambia drásticamente [1:44]. Si antes se tenían alrededor de 900 documentos completos, ahora hay 6,481 elementos. Esto tiene sentido: al cortar en chunks de 400 tokens, cada documento original genera múltiples fragmentos padre.
La prueba definitiva está en las queries [2:08]:
- Al ejecutar una consulta directamente contra la vector store, se obtienen 4 documentos.
- Al ejecutarla con el retriever, se obtienen 3 documentos.
Estos 3 documentos son un subconjunto de los 4 que devuelve la vector store directamente. La diferencia es que el retriever ya aplica la lógica de combinar especificidad (a través de los hijos) con contexto (a través de los padres).
El resultado final es un sistema que balancea precisión y amplitud de contexto, permitiendo que el modelo de lenguaje reciba fragmentos que realmente puede procesar sin sacrificar la calidad de la información recuperada. Si estás implementando este tipo de sistemas, ¿qué relación de tamaño entre padre e hijo te ha funcionado mejor? Comparte tu experiencia.