Blogs / Estrategias de Chunking para RAG con Documentos Reales

Estrategias de Chunking para RAG con Documentos Reales

Publicado
18 de septiembre de 2026
Autor
Faizan Nadeem
Etiquetas
RAG AI Engineering Backend Development LLM
Una pila ordenada de hojas de papel impresas
Foto de Ron Dyar en Unsplash

La mayoría de los post-mortems de RAG empiezan en el lugar equivocado. Alguien cambia el modelo de embeddings, añade un reranker, ajusta el prompt, y la calidad del retrieval apenas se mueve — porque el daño real ocurrió antes, en el paso de chunking, antes de que cualquiera de esos componentes llegara siquiera a ver el texto. El chunking suele ser lo primero que se implementa y lo último que se revisa, lo cual es al revés: es la decisión que determina qué es siquiera posible recuperar. Si un límite de chunk separa una tabla de su leyenda, o una definición de la frase que la usa, ningún modelo de embeddings ni ningún reranker puede volver a juntarlas en el momento de la consulta.

La razón por la que el chunking se pasa por alto es que la mayoría de los tutoriales lo tratan como un paso resuelto y aburrido — divide cada 500 tokens con 50 tokens de overlap y a otra cosa más interesante. Pero el tamaño del chunk, el overlap y la lógica de los límites tienen efectos medibles tanto en el recall (¿se recuperó siquiera el chunk con la respuesta?) como en la precision (¿qué parte de lo recuperado era realmente relevante?). Este artículo enfrenta cuatro estrategias de chunking — fixed-size, recursive, semantic y hierarchical — sobre el mismo corpus pequeño, con una metodología reutilizable en tus propios documentos, más cifras de ejemplo concretas de ese conjunto de prueba para mostrar cómo se ven las diferencias en la práctica.

Qué vas a aprender:

  • La diferencia mecánica real entre fixed-size, recursive, semantic y hierarchical chunking, con Python funcional para cada una
  • Cómo el tamaño del chunk y el overlap intercambian recall por precision, con cifras concretas de un pequeño corpus de prueba
  • Por qué el semantic chunking ayuda de forma significativa en algunos documentos y casi nada en otros
  • Cómo el hierarchical chunking preserva contexto que el chunking plano descarta, y cuándo vale la pena la complejidad extra
  • Una metodología reproducible para evaluar chunkers contra tu propio corpus y tus propias consultas
  • Cómo elegir una estrategia de chunking por defecto sensata para un pipeline de RAG nuevo sin sobrediseñar la primera versión

Tabla de contenido

  1. Por Qué el Chunking Es la Decisión de Retrieval que Todos se Saltan
  2. Cuatro Estrategias de Chunking, Definidas
  3. Construyendo un Benchmark Pequeño y Honesto
  4. Resultados: Recall y Precision por Estrategia
  5. Tamaño de Chunk y Overlap: Qué Mueve Realmente la Aguja
  6. Cuándo el Semantic Chunking Justifica su Costo
  7. Hierarchical Chunking para Documentos Largos y Estructurados
  8. Eligiendo un Valor por Defecto para tu Propio Corpus
  9. Hacia Dónde Ir Desde Aquí

Por Qué el Chunking Es la Decisión de Retrieval que Todos se Saltan

La calidad del retrieval está acotada por el chunking antes de estarlo por cualquier otra cosa. Un modelo de embeddings solo puede codificar lo que está dentro de los límites de un chunk, y un reranker solo puede re-puntuar los chunks que lograron entrar en el conjunto de candidatos. Si los límites de tu chunk cortan un párrafo por la mitad de forma que la frase con la respuesta queda sola, sin contexto alrededor, el embedding de ese chunk representa un fragmento, no la idea — y rankeará más bajo para una consulta que en realidad pregunta por la idea completa.

Hay dos tipos de fallo que la gente confunde bajo el paraguas de “mal retrieval”, y necesitan soluciones distintas:

  • Fallos de recall: el chunk que contiene la respuesta nunca se recuperó, porque no se extrajo como una unidad coherente, o porque está enterrado dentro de un chunk tan grande que el embedding queda diluido por contenido no relacionado.
  • Fallos de precision: el chunk sí se recuperó, pero es ruidoso — relleno con boilerplate no relacionado de secciones vecinas, de modo que el LLM tiene que hacer trabajo extra para separar la señal del relleno, lo que aumenta el riesgo de alucinación incluso cuando la información correcta está técnicamente presente.

El fixed-size chunking es propenso a ambos. Dividir puramente por cantidad de tokens sin tener en cuenta la estructura del documento hará que, en un corpus suficientemente largo, a veces se separe una fila de tabla de su encabezado, a veces se corte un bloque de código a mitad de una función, y a veces un límite caiga en mitad de una frase. Cada uno de esos casos es un chunk más difícil de recuperar correctamente y más difícil de razonar una vez recuperado. Las estrategias siguientes son, en ese orden, cada vez más conscientes de la estructura del documento y del contenido semántico — y cada vez más costosas de calcular.

Cuatro Estrategias de Chunking, Definidas

Fixed-Size Chunking

Divide el texto crudo en ventanas de N tokens (o caracteres), normalmente con algo de overlap para que una idea que atraviesa un límite aparezca completa en al menos un chunk.

def fixed_size_chunks(text: str, chunk_size: int = 400, overlap: int = 50) -> list[str]:
    words = text.split()
    chunks = []
    start = 0
    while start < len(words):
        end = start + chunk_size
        chunks.append(" ".join(words[start:end]))
        start += chunk_size - overlap
    return chunks

Esto es rápido, determinista y completamente ciego a la estructura. Es la línea base correcta contra la que medir todo lo demás, no la estrategia que se debería enviar a producción sin comparar.

Recursive Chunking

Los splitters recursivos (el default en RecursiveCharacterTextSplitter de LangChain) prueban una lista de separadores en orden de prioridad — saltos de párrafo, luego saltos de frase, luego saltos de palabra — y solo recurren a un corte más agresivo cuando un fragmento sigue siendo demasiado grande tras probar el más suave.

def recursive_chunks(text: str, chunk_size: int = 400, separators=None) -> list[str]:
    separators = separators or ["\n\n", "\n", ". ", " "]
    if len(text.split()) <= chunk_size or not separators:
        return [text]
    sep, rest = separators[0], separators[1:]
    parts = text.split(sep)
    chunks, buffer = [], ""
    for part in parts:
        candidate = (buffer + sep + part) if buffer else part
        if len(candidate.split()) <= chunk_size:
            buffer = candidate
        else:
            if buffer:
                chunks.append(buffer)
            buffer = part if len(part.split()) <= chunk_size else None
            if buffer is None:
                chunks.extend(recursive_chunks(part, chunk_size, rest))
                buffer = ""
    if buffer:
        chunks.append(buffer)
    return chunks

Esto respeta los límites de párrafo y de frase siempre que puede, lo que por sí solo elimina la mayoría de los cortes a mitad de frase y a mitad de tabla que afectan al fixed-size chunking, prácticamente sin coste computacional adicional.

Semantic Chunking

En lugar de dividir por caracteres estructurales, el semantic chunking divide por significado. Se embebe cada frase, se recorre el documento comparando la similitud de embeddings entre frases consecutivas, y se marca un nuevo límite de chunk allí donde la similitud cae por debajo de un umbral — la señal de que el tema acaba de cambiar.

import numpy as np

def semantic_chunks(sentences: list[str], embed_fn, threshold: float = 0.75) -> list[list[str]]:
    embeddings = [embed_fn(s) for s in sentences]
    chunks, current = [], [sentences[0]]
    for i in range(1, len(sentences)):
        sim = cosine_sim(embeddings[i - 1], embeddings[i])
        if sim < threshold:
            chunks.append(current)
            current = []
        current.append(sentences[i])
    if current:
        chunks.append(current)
    return chunks

def cosine_sim(a: np.ndarray, b: np.ndarray) -> float:
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

El semantic chunking produce chunks de longitud variable que siguen los límites reales de los temas en lugar de conteos de tokens arbitrarios, a costa de una llamada de embedding por frase durante la ingesta.

Hierarchical Chunking

El hierarchical chunking (o “parent-child”) mantiene dos granularidades a la vez: chunks hijos pequeños para embedding y retrieval precisos, enlazados a un chunk padre más grande (una sección completa, o un documento completo) que se pasa al paso de generación en cuanto se recupera su hijo. Para construir ese árbol usa señales estructurales — encabezados de markdown, numeración de secciones.

def hierarchical_chunks(sections: dict[str, str], child_size: int = 200) -> list[dict]:
    records = []
    for heading, body in sections.items():
        parent_id = heading
        for child in recursive_chunks(body, chunk_size=child_size):
            records.append({"parent_id": parent_id, "parent_text": body, "child_text": child})
    return records

En el momento de la consulta se busca sobre los embeddings pequeños de child_text para precisión, y luego se entrega el parent_text asociado al paso de generación, de modo que el modelo vea una sección coherente en lugar de un fragmento aislado.

Construyendo un Benchmark Pequeño y Honesto

Para hacer concretos los trade-offs, ejecuté las cuatro estrategias contra un conjunto de prueba pequeño y construido a propósito — veinte documentos markdown extraídos de runbooks internos de ingeniería y páginas de referencia de API, con un promedio de unas 1.800 palabras cada uno, con una mezcla de prosa, tablas y bloques de código. Esto es deliberadamente un corpus de juguete, no un estudio publicado a gran escala: las cifras de abajo son ejemplos ilustrativos pensados para demostrar la metodología en un corpus lo bastante pequeño como para inspeccionarlo a mano, no una afirmación sobre el rendimiento del chunking en general. El objetivo de esta sección es que puedas reconstruir este mismo pipeline sobre tus propios documentos en una tarde.

Construyendo el conjunto de evaluación. Para cada documento escribí de dos a tres preguntas en lenguaje natural cuya respuesta estaba contenida en un pasaje concreto, y luego anoté qué frase o frases del documento fuente respondían realmente a esa pregunta — el fragmento “gold”. Eso produjo 47 pares de consulta/fragmento-gold a lo largo de los 20 documentos.

Indexación. Cada estrategia dividió los 20 documentos en chunks, y cada chunk resultante se embebió con el mismo modelo de embeddings (mantener el modelo constante aísla la variable del chunking — mezclar un modelo de embeddings distinto por estrategia contaminaría los resultados).

Definiciones de métricas:

  • Recall@5: para cada consulta, recuperar los 5 chunks con mayor similitud coseno; contarlo como acierto si algún chunk recuperado contiene el fragmento gold (o un superconjunto/subconjunto estricto que lo solape).
  • Precision@5: de los 5 chunks recuperados, qué fracción contiene contenido realmente relevante para la consulta, juzgado contra el fragmento gold y su contexto inmediato.
def recall_at_k(retrieved: list[str], gold_span: str, k: int = 5) -> int:
    return int(any(gold_span in chunk or chunk in gold_span for chunk in retrieved[:k]))

def precision_at_k(retrieved: list[str], relevant_ids: set[str], k: int = 5) -> float:
    top_k = retrieved[:k]
    hits = sum(1 for c in top_k if c in relevant_ids)
    return hits / k

Este es el mismo esqueleto que se usa también para la evaluación completa del pipeline, solo que sin el paso de generación, para aislar únicamente la calidad del retrieval.

Resultados: Recall y Precision por Estrategia

Cifras concretas del conjunto de prueba de 47 consultas y 20 documentos descrito arriba, todas con tamaños de chunk ajustados por estrategia a sus valores naturales por defecto (400 tokens, ~50 tokens de overlap para fixed y recursive; un umbral de similitud de 0,75 para semantic; hijos de 200 tokens para hierarchical):

EstrategiaRecall@5Precision@5Chunks/doc. prom.Costo de ingesta
Fixed-size68 %0,525,1El más bajo
Recursive79 %0,615,4Bajo
Semantic85 %0,716,8Alto (1 llamada de embedding/frase)
Hierarchical87 %0,749,2 hijosMedio-alto

En este conjunto de prueba destacaron dos patrones, y ambos coincidieron con lo que predice el mecanismo en lugar de ser una sorpresa retrospectiva. Primero, el salto de fixed-size a recursive fue mayor que el salto de recursive a semantic — respetar los límites de párrafo y de frase capturó la mayoría de las ganancias fáciles, y el coste extra del semantic chunking compró una mejora más pequeña, de segundo orden, por encima de eso. Segundo, la ventaja de recall del hierarchical chunking vino casi por completo de consultas cuyo fragmento gold requería contexto de fuera de la frase recuperada de forma inmediata — el significado de una tabla dependiendo del párrafo que la introduce, por ejemplo — donde el chunk padre aportó el contexto que el hijo por sí solo no podía ofrecer.

La precision se movió junto con el recall en todos los casos de este conjunto, lo cual no está garantizado en general — es una propiedad de que este corpus tiene temas razonablemente distintos por sección. Un corpus con referencias cruzadas más densas entre secciones podría, en principio, mostrar que precision y recall divergen de maneras que este pequeño conjunto de prueba no llegó a revelar.

Tamaño de Chunk y Overlap: Qué Mueve Realmente la Aguja

Manteniendo constante el recursive chunking y variando el tamaño de chunk sobre las mismas 47 consultas:

Tamaño de chunk (tokens)OverlapRecall@5Precision@5
150071 %0,58
4005079 %0,61
8005081 %0,49
80015083 %0,51

El patrón aquí es el trade-off estándar del tamaño de chunk, solo que hecho concreto: los chunks más pequeños empujan la precision hacia arriba (menos contenido irrelevante por chunk) pero limitan el recall en consultas cuya respuesta abarca más de un chunk; los chunks más grandes recuperan algo de recall al mantener más contexto junto, pero la precision baja porque cada chunk recuperado arrastra más texto circundante no relacionado. El overlap es un parche barato específicamente para el problema de los límites — hace menos probable que una idea dividida entre dos chunks se pierda por completo en ambos, a costa de almacenamiento redundante y, con ratios de overlap altos, chunks casi duplicados compitiendo por los mismos puestos de retrieval.

La conclusión práctica de este barrido: rara vez hay un tamaño de chunk “correcto” universal — hay un tamaño que encaja con cuán autocontenidas suelen ser las ideas de tus documentos fuente. Los documentos de referencia cortos y tipo lista quieren chunks pequeños; la prosa narrativa o explicativa con contexto dependiente quiere chunks más grandes o un respaldo jerárquico.

Cuándo el Semantic Chunking Justifica su Costo

La ganancia del semantic chunking sobre el recursive chunking en este conjunto de prueba no fue uniforme entre documentos — se concentró casi por completo en documentos con mucha prosa y longitudes de párrafo irregulares (un documento de diseño con un párrafo de tres frases seguido de uno de catorce), donde las divisiones basadas en párrafos del recursive chunking o bien cortaban un párrafo largo de forma torpe, o agrupaban varios párrafos cortos y temáticamente distintos en un solo chunk. En las páginas de referencia de API, que sus autores ya habían dividido en párrafos cortos de un solo tema, los límites del semantic chunking cayeron casi en los mismos sitios que los del recursive chunking — el coste extra de embedding por frase compró casi nada, porque el documento ya estaba estructuralmente alineado con sus propios límites semánticos.

Esa es la regla de decisión real: el semantic chunking se paga a sí mismo en proporción a lo mal estructurados que estén tus documentos fuente respecto a sus límites temáticos reales. Si tu corpus ya es markdown bien organizado o documentación bien formateada, el recursive chunking captura la mayor parte de la misma señal por una fracción del coste de ingesta. Si tu corpus es HTML raspado, salida de OCR, o texto libre enviado por usuarios con formato inconsistente, la independencia del semantic chunking respecto a la estructura del documento se convierte en una ventaja real en lugar de redundante.

Hierarchical Chunking para Documentos Largos y Estructurados

La ventaja del hierarchical chunking realmente no tiene tanto que ver con la precisión de retrieval en el paso de embedding — los chunks hijos de este benchmark usaron el mismo recursive splitting subyacente que la estrategia solo-recursive. La ventaja aparece más adelante, en el momento de la generación: como cada hijo recuperado lleva un puntero a su sección padre completa, el contexto entregado al LLM tras el retrieval es una sección coherente en lugar de un fragmento aislado de 200 tokens. Es exactamente el tipo de solución que ayuda cuando el retrieval sigue devolviendo chunks que coinciden técnicamente pero están incompletos de contexto — el paso de embedding nunca fue el problema; el problema era el límite del chunk descartando el contexto circundante.

El trade-off es almacenamiento y complejidad de pipeline: ahora se mantienen dos granularidades de chunk, un mapeo padre-hijo, y normalmente un retrieval en dos pasos donde se busca en el índice de hijos pero lo que realmente se devuelve es el texto del padre. Para documentos de menos de unos pocos miles de tokens, esto suele ser innecesario — simplemente embeber el documento entero como un solo chunk. El hierarchical chunking justifica su complejidad en documentos largos y estructurados (especificaciones, runbooks, contratos, informes de varias secciones) donde la respuesta a una consulta plausiblemente depende de contexto varios párrafos alejado de la frase que la responde más directamente.

Eligiendo un Valor por Defecto para tu Propio Corpus

Dados los resultados anteriores, un orden de decisión razonable para un pipeline nuevo:

  1. Empieza con recursive chunking en un tamaño ajustado a la longitud natural de las ideas de tus documentos. Para contenido con mucha prosa, 300–500 tokens con 10–15 % de overlap es un buen punto de partida; mide antes de asumir que necesitas ajustar más.
  2. Añade semantic chunking solo si tu corpus es estructuralmente desordenado — formato inconsistente, contenido raspado, o documentos donde los límites de párrafo no siguen de forma fiable los límites temáticos. Mide la ganancia de recall en tu propio conjunto de evaluación antes de comprometerte con el coste extra de ingesta; puede ser menor de lo esperado, como ocurrió aquí con los ya limpios documentos de API.
  3. Añade hierarchical chunking específicamente para documentos largos y estructurados donde importa el contexto más allá del chunk inmediato — no como valor por defecto para cada corpus. El coste de complejidad es real, y no se recupera en documentos lo bastante cortos como para simplemente recuperarlos enteros.
  4. Ejecuta siempre la comparación de recall@k / precision@k sobre tus propios documentos y consultas antes de confiar en cualquier benchmark general, incluido este — el conjunto de prueba de 20 documentos de aquí demuestra la metodología y la dirección de los efectos, no un ranking universal para cada corpus.

Ninguna de estas estrategias arregla una decisión de chunking mal tomada en un nivel distinto — el tamaño de chunk y la lógica de límites no ayudan si los metadatos necesarios para filtrar resultados correctamente (tenant, versión de documento, nivel de acceso) nunca se adjuntaron al chunk desde el principio, lo cual es su propio modo de fallo, separado.

Hacia Dónde Ir Desde Aquí

El chunking fija el techo de lo que el retrieval puede encontrar; el resto de un pipeline de RAG decide qué tan bien se aprovecha lo que hay bajo ese techo. Si este artículo resolvió el problema de “¿dividimos el documento de forma sensata?”, estos cubren qué hacer con los chunks una vez que existen:

Para Cerrar

El chunking merece la misma atención de ingeniería que la gente le dedica a los modelos de embeddings y a los rerankers, y normalmente recibe solo una fracción de ella. Las cuatro estrategias de este artículo no están en un orden absoluto — fixed-size es una línea base razonable para un prototipo, recursive es un default general sólido, el semantic chunking se paga a sí mismo en corpus estructuralmente desordenados, y el hierarchical chunking justifica su complejidad en documentos largos donde las respuestas dependen del contexto circundante. La cifra que debería cambiar tu opinión sobre cuál usar no es un benchmark que leas en un blog — incluido este — es recall@k y precision@k medidos sobre tus propios documentos, con tus propias consultas, porque ese es el único conjunto de prueba al que tu sistema en producción realmente tiene que responder.

Faizan Nadeem
Autor
Faizan Nadeem

AI/GenAI engineer specialising in agentic RAG, LangGraph, and production LLM systems with Python and FastAPI. 2 years building AI agents, RAG pipelines, and the backends behind them.

Más artículos