Estrategias de chunking para RAG (y Contextual Retrieval de Anthropic): la guia del ingeniero — Cesar Ayala
← Todos los artículos

Estrategias de chunking para RAG (y Contextual Retrieval de Anthropic): la guia del ingeniero

Empieza con chunks recursivos de 512 tokens (RecursiveCharacterTextSplitter) y 10-20% de solape, y ajusta el tamano al tipo de consulta. Para el mayor salto de calidad usa Contextual Retrieval de Anthropic: antepon a cada chunk un contexto de 50-100 tokens generado por Claude antes de embeder e indexar con BM25; con reranking reduce los fallos de recuperación hasta un 67%.

Empieza recursivo, luego contextualiza

Empieza con chunks recursivos de 512 tokens usando RecursiveCharacterTextSplitter y 10-20% de solape, y ajusta el tamaño al tipo de consulta que vas a servir. Para el mayor salto de calidad, aplica Contextual Retrieval de Anthropic: antepón a cada chunk un contexto de 50-100 tokens generado por Claude antes de generar los embeddings e indexar con BM25. Con Contextual BM25 y reranking encima, reduces los fallos de recuperación hasta un 67%. Esa es la respuesta corta; el resto de la guía es el porqué y el cómo lo pones en producción sin romper el presupuesto.

El chunking es la palanca aguas arriba que nada aguas abajo puede recuperar

El chunking decide qué puede encontrar tu sistema RAG, punto. Si partes un documento por el lugar equivocado, ningún reranker, ninguna base de datos vectorial mejor y ningún modelo de embeddings más caro aguas abajo recuperan la información que quedó cortada. La pérdida es permanente: ocurre en el momento en que escribes en el índice.

Por eso lo trato como la primera decisión de ingeniería del pipeline, no como un detalle de preprocesamiento. Antes de invertir en reranking y búsqueda híbrida, arregla el chunking. Si aún no tienes el pipeline base montado, empieza por construir un sistema RAG con Claude; y si dudas de que RAG sea siquiera el enfoque correcto, revisa RAG vs fine-tuning antes de seguir.

Las estrategias: tamaño fijo, recursivo, semántico y por estructura

Hay cinco estrategias que valen la pena, ordenadas de menos a más sofisticadas. No necesitas todas; necesitas la más simple que pase tus métricas.

  • Tamaño fijo: partes cada N tokens con una ventana deslizante, sin semántica. Sirve para prototipar y para texto homogéneo.
  • Recursivo (RecursiveCharacterTextSplitter): parte sobre una jerarquía de separadores (párrafo, luego oración, luego palabra). Es el baseline y tu caballo de batalla.
  • Semántico: parte donde cambia el significado, usando similitud de embeddings entre segmentos adyacentes para detectar fronteras de tema. Mayor calidad, pero cerca de 14x más lento que el conteo por tokens.
  • Por estructura: parte según la estructura propia del documento (encabezados Markdown, secciones).
  • Contextual: la técnica de Anthropic que cubro más abajo, y el mayor salto de calidad del post.
  1. Tamaño fijoN tokens con ventana deslizante, sin semántica. Prototipos y texto homogéneo.
  2. RecursivoJerarquía de separadores (párrafo, oración, palabra). El baseline.
  3. SemánticoFronteras por similitud de embeddings. ~14x más lento.
  4. Por estructuraCorta por encabezados y secciones del documento.
  5. ContextualAntepone contexto generado por Claude. El mayor salto de calidad.

Empieza por el recursivo, y gradúa a otra estrategia solo cuando tus métricas de recuperación justifiquen el costo extra. Este es el baseline del que debe partir todo sistema RAG. El splitter recursivo intenta partir por párrafos primero; si un fragmento sigue siendo muy grande, baja a oraciones, y luego a palabras. Así respeta las fronteras naturales del texto en vez de cortar a media oración.

import tiktoken
from langchain_text_splitters import RecursiveCharacterTextSplitter

# Token-accurate length so chunk_size means TOKENS, not characters.
enc = tiktoken.get_encoding("cl100k_base")

def token_len(text: str) -> int:
    return len(enc.encode(text))

splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,          # baseline: 512 tokens
    chunk_overlap=64,        # ~12.5% overlap, inside the 10-20% band
    length_function=token_len,
    separators=["\n\n", "\n", ". ", " ", ""],  # paragraph -> line -> sentence -> word
)

chunks = splitter.split_text(document_text)
print(f"{len(chunks)} chunks, avg {sum(token_len(c) for c in chunks) // len(chunks)} tokens")

Nota el length_function: cuentas por tokens, no por caracteres. Un chunk de 512 caracteres y uno de 512 tokens no son lo mismo, y tu modelo de embeddings piensa en tokens (revisa el tokenizador de Voyage AI para tu proveedor). Contar por caracteres te deja chunks de tamaño impredecible que revientan la ventana del embedder o desperdician espacio.

Tamaño de chunk y solape: 512 de base, rango 512-1024, solape del 10-20%

Arranca con chunks recursivos de 512 tokens. Un benchmark de febrero de 2026 colocó al recursivo de 512 tokens en primer lugar, y coincide con lo que veo en producción. El rango defendible es 512-1024 tokens: por debajo pierdes contexto, por arriba diluyes la relevancia y el chunk trae ruido que confunde al recuperador.

El solape es un margen de seguridad. Con 10-20% de solape, la información que cae cerca de una frontera aparece en ambos chunks vecinos, lo que recupera cerca del 60-70% de la pérdida por frontera a un costo de almacenamiento mínimo. Con 512 tokens de chunk, eso son unos 51-102 tokens de solape.

Tamaño base512 tokens
Rango defendible512-1024 tokens
Solape10-20%
Conteopor tokens, no caracteres

La regla del solape ya no aplica: ajusta el tamaño al tipo de consulta

Durante años el consejo fue “siempre agrega solape”. Ya no es seguro asumirlo. La elección de chunking por sí sola puede mover el recall hasta un ~9% sobre el mismo corpus, y el solape óptimo depende de tus consultas, no de una regla universal.

La heurística que uso: ajusta el tamaño del chunk al tipo de consulta. Preguntas puntuales sobre hechos aislados favorecen chunks pequeños y precisos. Preguntas que exigen síntesis a lo largo de varios párrafos favorecen chunks más grandes que preserven el hilo del razonamiento. No adivines: mide con una evaluación mínima de agentes IA sobre consultas reales, y deja que el recall@k decida el tamaño. Gradúa a semántico, por estructura o contextual solo cuando tus métricas de recuperación justifiquen el costo extra.

Contextual Retrieval de Anthropic: el problema que un chunk desnudo no resuelve

Aquí está el mayor salto de calidad, y es un problema que ninguna afinación de tamaño resuelve. Considera este chunk sacado de un reporte financiero:

“The company’s revenue grew by 3% over the previous quarter.”

¿Qué empresa? ¿Qué trimestre? Al partirlo del documento, el chunk pierde exactamente la información que un usuario necesitaría para encontrarlo. Un embedding de esa oración no sabe que habla de Acme Corp en el Q2 de 2023, así que una búsqueda por “crecimiento de ingresos de Acme en el segundo trimestre” puede no recuperarlo nunca.

La solución de Anthropic, Contextual Retrieval, es directa: antes de indexar, antepón a cada chunk un contexto específico (normalmente 50-100 tokens) generado por Claude que sitúa ese fragmento dentro del documento completo. Luego generas los embeddings E indexas con BM25 el chunk contextualizado, no el desnudo.

Son dos variantes que se usan juntas. Contextual Embeddings: generas el embedding del chunk con su contexto antepuesto. Contextual BM25: indexas ese mismo texto contextualizado también en un índice léxico BM25, para capturar coincidencias exactas de términos que los embeddings a veces pierden. El detalle crítico es el prompt caching: cargas el documento completo en caché una sola vez y reutilizas esa caché para contextualizar cada chunk, en vez de reenviar el documento en cada llamada. El prompt de Claude que genera el contexto es este:

import anthropic

client = anthropic.Anthropic()

DOC_CONTEXT_PROMPT = "<document>\n{doc}\n</document>"

CHUNK_CONTEXT_PROMPT = """Here is the chunk we want to situate within the whole document
<chunk>
{chunk}
</chunk>
Please give a short succinct context to situate this chunk within the overall document for the purposes of improving search retrieval of the chunk. Answer only with the succinct context and nothing else."""

def contextualize(document: str, chunk: str) -> str:
    resp = client.messages.create(
        model="claude-haiku-4-5",
        max_tokens=200,
        messages=[{
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": DOC_CONTEXT_PROMPT.format(doc=document),
                    "cache_control": {"type": "ephemeral"},  # cache the whole doc once
                },
                {
                    "type": "text",
                    "text": CHUNK_CONTEXT_PROMPT.format(chunk=chunk),
                },
            ],
        }],
    )
    return resp.content[0].text

# Prepend the generated context, THEN embed and BM25-index the result.
contextualized = [f"{contextualize(document_text, c)}\n\n{c}" for c in chunks]

El marcador cache_control sobre el bloque del documento es lo que abarata todo: el primer chunk paga por escribir el documento en la caché y cada chunk posterior lo lee a una fracción del precio. Si no conoces la mecánica del SDK, revisa cómo usar la API de Claude.

Los números verificados: de 35% a 49% a 67% menos fallos de recuperación

Estos son los números reportados por Anthropic, sobre un baseline de 5.7% de tasa de fallo en recuperación (top-20):

  • Contextual Embeddings solo: reduce los fallos un 35% (de 5.7% a 3.7%).
  • Añadiendo Contextual BM25: reduce los fallos un 49% (de 5.7% a 2.9%).
  • Añadiendo reranking encima: reduce los fallos un 67% (de 5.7% a 1.9%).

El paso de reranking es simple de describir: recuperas los top-150 chunks, los reordenas con el reranker, y te quedas con los top-20 que pasan al modelo generador.

Contextual Embeddings-35%
+ Contextual BM25-49%
+ reranking (top-150 → top-20)-67%

Ese contexto extra en cada chunk es lo que también sostiene un chatbot de IA sin alucinaciones: si recuperas el fragmento correcto con su contexto, el modelo se apoya en él en vez de inventar.

La objeción obvia es el costo: ¿una llamada a Claude por cada chunk? Sale barato precisamente por el prompt caching. En lugar de reenviar el documento de referencia en cada llamada, lo cargas en caché una vez y generas el contexto de todos sus chunks contra esa caché. Anthropic reporta un costo único de $1.02 por millón de tokens de documento para generar los chunks contextualizados. Es un gasto de indexación de una sola vez, no un costo por consulta: pagas $1.02 por millón de tokens al ingerir el corpus y luego aprovechas la mejora en cada búsqueda a partir de ahí. La mecánica de la caché está en la documentación de prompt caching de Anthropic.

Guía de decisión: empieza simple y sube de nivel solo cuando tus métricas lo justifiquen

No saltes directo a Contextual Retrieval. Sube de nivel por evidencia, no por entusiasmo.

Empieza recursivo

  • Chunks de 512 tokens, 10-20% de solape
  • Conteo por tokens con tiktoken
  • Suficiente para corpus homogéneos
  • Costo de indexación casi nulo

Gradúa a Contextual Retrieval

  • Chunks que pierden entidad/fecha al separarse
  • Corpus grande y heterogéneo
  • Fallos de recuperación medibles en tu eval
  • Justificas el gasto único de $1.02/M tokens

La regla operativa: monta el baseline recursivo, mídelo con tu harness de evaluación, y solo cuando veas fallos de recuperación reales inviertes en Contextual Retrieval, luego en Contextual BM25, luego en reranking. Cada nivel tiene un costo; que tus métricas lo paguen.

Un mapa rápido para elegir la estrategia sin releer todo:

  • Tamaño fijo: prototipos, benchmarks internos, texto homogéneo sin estructura clara.
  • Recursivo: el default para casi todo. Empieza aquí siempre.
  • Semántico: documentos donde los temas cambian dentro de un mismo párrafo y el tamaño fijo mezcla ideas; acepta el ~14x de costo solo si tu eval lo premia.
  • Por estructura: Markdown, documentación técnica, contratos, cualquier corpus con encabezados fiables.
  • Contextual: corpus grandes y heterogéneos donde los chunks pierden su referente (empresa, fecha, sección). El mayor retorno.

La estrategia de chunking también depende de dónde vives: tu elección de base de datos vectorial determina si puedes correr BM25 léxico junto a la búsqueda vectorial sin montar un segundo sistema.

Siguiente paso: reranking, búsqueda híbrida y cómo medir todo esto

El chunking es la palanca aguas arriba, pero es solo la primera. Una vez que tus chunks son buenos, la siguiente ganancia está en el reranking y la búsqueda híbrida, y luego en producir salidas estructuradas de Claude para que el consumo de lo recuperado sea confiable.

Nada de esto importa si no lo mides. Antes de tocar cualquier parámetro, arma un conjunto de consultas reales con las respuestas correctas y observa recall@k y hit-rate en cada cambio. Para el paso que sigue, revisa el post hermano sobre reranking y búsqueda híbrida, medidos, y explora el resto del hub de agentes de IA para ver cómo encajan las piezas. Empieza simple, mide todo, y sube de nivel solo cuando el número lo justifique.