volver al blog

Chunking para RAG: dónde cortar los documentos

ragia-generativaembeddings

El fallo casi nunca está en el embedding

Cuando un RAG devuelve basura, el primer sospechoso siempre es el mismo: el modelo de embeddings. Se cambia por uno más caro, la recuperación mejora un poco y el problema se da por resuelto. Pero la causa real suele estar un paso antes, en un sitio del que nadie presume: cómo troceaste los documentos.

El chunking es la decisión de ingeniería más infravalorada de un RAG. El embedding solo puede representar lo que le das. Si el fragmento parte una idea por la mitad, une dos temas que no tienen nada que ver o llega sin contexto, ningún modelo lo va a arreglar después. Basura dentro, basura fuera, con vectores de por medio.

El chunk es la unidad que de verdad recuperas

Conviene tenerlo claro: en un RAG no recuperas documentos, recuperas chunks. Ese fragmento es lo que se convierte en vector, lo que compara la búsqueda y lo que acaba en el contexto del modelo. Todo lo que pase después depende de esa unidad.

De ahí salen dos tensiones que gobiernan cualquier estrategia. Un chunk pequeño es preciso pero puede quedarse sin contexto: recuperas la frase exacta, pero sin el párrafo que la explica. Un chunk grande arrastra contexto pero diluye la señal: el embedding promedia tantos temas que deja de parecerse a ninguna pregunta concreta. No hay un tamaño ganador; hay un equilibrio que depende de tus documentos y de tus preguntas.

El tamaño no es el debate importante

En internet circula un número mágico, “512 tokens”, como si fuera la respuesta. No lo es. El tamaño importa, pero es la segunda pregunta. La primera es por dónde cortas, y ahí es donde se gana o se pierde de verdad.

Cortar cada N caracteres a ciegas es la forma más rápida de destrozar el sentido. Parte frases, separa una tabla de su título y deja definiciones huérfanas. Da igual que el tamaño sea perfecto: si el corte cae en mitad de una idea, el chunk no sirve. Antes de tocar el tamaño, respeta la estructura del documento: párrafos, encabezados, listas, secciones. El texto ya viene troceado por su autor; empieza por ahí.

Cuatro estrategias, cuándo usar cada una

No hay una estrategia universal. Hay una que encaja con tus datos. Esta es la que sigo para elegir:

EstrategiaCómo cortaCuándo brillaSu punto débil
Tamaño fijoCada N tokens, con solapeTexto plano y homogéneo, prototiposIgnora la estructura, parte ideas
Recursiva por separadoresIntenta párrafo → frase → palabraCaso general, buen punto de partidaRequiere ajustar separadores por formato
Por estructuraSigue encabezados, secciones, MarkdownDocumentación, manuales, contratosDepende de que el documento esté bien estructurado
SemánticaCorta donde cambia el tema (por similitud)Texto denso sin estructura claraMás lenta y cara de calcular

Mi orden por defecto: empieza por recursiva, que respeta jerarquía de separadores sin complicarte. Si tus documentos tienen estructura fuerte (Markdown, HTML, PDF con secciones), pasa a por estructura y notarás el salto. Deja la semántica para cuando el texto sea un muro sin formato y las otras dos se queden cortas. No pagues su coste antes de necesitarlo.

El solape existe para no perder las fronteras

Cortes limpios crean un problema nuevo: la información que vive justo en el borde entre dos chunks. Una frase que empieza en uno y termina en otro se pierde para ambos. El solape (overlap) es el remedio: repites un trozo del final de cada chunk al principio del siguiente.

Un solape del 10 al 20 % del tamaño del chunk suele bastar. Menos, y sigues perdiendo fronteras; más, y llenas el índice de duplicados que compiten entre sí en la búsqueda. Es un seguro barato contra los cortes desafortunados, no una excusa para no pensar dónde cortas.

Código real: recursivo y con metadatos

El chunking recursivo es el mejor punto de partida porque intenta cortar por el separador más “grande” que quepa antes de bajar al siguiente. Lo importante no es solo el corte: es guardar de dónde viene cada fragmento.

from langchain_text_splitters import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,        # en caracteres, no en tokens: mide con tu tokenizador si el coste importa
    chunk_overlap=120,     # ~15 %: cubre las fronteras sin inflar el índice
    # El orden es la clave: intenta párrafo, luego frase, luego palabra.
    separators=["\n\n", "\n", ". ", " ", ""],
)

texto = documento.read_text(encoding="utf-8")
chunks = splitter.split_text(texto)

# El chunk desnudo no basta: sin origen, no puedes citar ni depurar.
registros = [
    {
        "contenido": chunk,
        "fuente": documento.name,
        "posicion": i,          # para reconstruir el orden original
        "titulo": "Manual de instalación",  # contexto que el embedding no ve
    }
    for i, chunk in enumerate(chunks)
]

Dos detalles que marcan la diferencia en producción. El primero: guarda metadatos con cada chunk. La fuente y la posición te dejan citar la respuesta y depurar cuando algo falle; sin ellos, tienes vectores anónimos imposibles de auditar. El segundo: añade al texto del chunk una pista de contexto que el embedding no vería por sí solo, como el título de la sección. Un fragmento que empieza por “Manual de instalación — ” se recupera mejor para preguntas sobre instalar que el mismo texto a secas.

Cómo saber si tu chunking funciona

No ajustes el chunking a ojo. Móntate un conjunto pequeño de preguntas reales con la respuesta que esperas, y mide dos cosas cada vez que cambies algo. Primero, si el chunk correcto aparece entre los recuperados (recall). Segundo, si aparece arriba y no en el puesto veinte (precisión de ranking). Con veinte o treinta preguntas bien elegidas ya ves tendencias claras.

La señal de que el chunking está mal casi siempre es la misma: el modelo responde con evasivas o se inventa datos aunque la información esté en tus documentos. Antes de tocar el prompt o cambiar de modelo, mira qué chunks llegaron al contexto. Muchas veces la respuesta estaba partida entre dos fragmentos y ninguno llegó entero.

Por dónde empezar

Si montas un RAG esta semana, no persigas la estrategia perfecta desde el minuto cero. Arranca con chunking recursivo, chunks de unos 800 caracteres y un 15 % de solape, guardando siempre la fuente y la sección de cada fragmento. Móntate ese conjunto de preguntas de prueba antes de escribir el resto del sistema. A partir de ahí, cambia una sola variable cada vez y mídela.

El chunking no es un preproceso que despachas y olvidas. Es el punto donde decides qué puede llegar a saber tu sistema. Trátalo como lo que es: la primera decisión de calidad, no la última.