Módulo 2: Chunking Strategies
Cápsula 07: Decision framework — qué chunking strategy elegir
Descripción de la cápsula
Cubrimos cuatro estrategias de chunking en este módulo: fixed-size, recursive, semantic, structural. Cada una con sus trade-offs, sus casos de uso, sus modos de falla. La pregunta operativa que cierra el módulo: ¿cuál eliges para un proyecto nuevo?
Esta cápsula consolida lo aprendido en un decision framework reproducible. Vas a aprender a tomar la decisión en menos de 10 minutos basándote en cinco preguntas, y a justificarla con datos cuantitativos cuando un Tech Lead te pregunte. Es la cápsula que vas a consultar cada vez que empiezas un nuevo proyecto RAG.
Al finalizar esta cápsula serás capaz de:
- ✅ Comparar las cuatro estrategias sobre seis dimensiones: precision, recall, latencia, costo, coherencia, complejidad
- ✅ Aplicar un flowchart de 5 preguntas para elegir strategy en proyectos nuevos
- ✅ Diferenciar cuándo conviene una strategy híbrida (mezclar dos para distintas partes del corpus)
- ✅ Calcular el TCO (total cost of ownership) de cada strategy para un volumen dado
- ✅ Anticipar cuándo migrar de una strategy a otra mientras el producto evoluciona
- ✅ Identificar el error costoso: elegir semantic chunking "porque es lo más nuevo" sin justificarlo
Tiempo estimado: 25-30 minutos
Benchmark consolidado de las cuatro estrategias
Calidad y performance
| Strategy | Precision@5 (típico) | Recall@50 | Coherence Score | Latency de indexing |
|---|---|---|---|---|
| Fixed-size | 68-72% | 50-55% | 0.62 (baja) | Instantáneo |
| Recursive | 78-82% | 57-62% | 0.89 (alta) | +20% sobre fixed |
| Semantic | 83-87% | 62-68% | 0.96 (muy alta) | +600% (cuesta dinero) |
| Structural | 85-90% (en code/HTML) | 67-72% | 0.94 (alta) | +12% sobre fixed |
Lectura clave:
- Fixed-size es el baseline. Casi siempre subóptimo, solo justificable para prototipos.
- Recursive es el sweet spot calidad/costo para texto narrativo. Default razonable.
- Semantic gana en coherencia pero cuesta dinero (LLM o embeddings extra) y tiempo.
- Structural gana cuando el documento tiene estructura clara (código, HTML, markdown).
Costo y operación
| Strategy | Costo de indexing por 100K docs | Mantenimiento | Vendor lock-in |
|---|---|---|---|
| Fixed-size | $0 | 0 | Ninguno |
| Recursive | $0 | 0 | Ninguno (LangChain o equivalente) |
| Semantic | $1-3 (embeddings extra para boundary detection) | Bajo | Bajo |
| Structural | $0 | Medio (mantener parsers por tipo de doc) | Ninguno |
Decision framework: las 5 preguntas
┌──────────────────────────────────┐
│ 1. ¿Es prototipo rápido o MVP │
│ donde "funciona" alcanza? │
└──────────────┬───────────────────┘
│
┌────────────────┴────────────────┐
│ SÍ │ NO
▼ ▼
┌──────────────────────┐ ┌──────────────────────────┐
│ Fixed-size. │ │ 2. ¿Tu corpus tiene │
│ Es subóptimo pero │ │ estructura formal │
│ vas a iterar ya. │ │ (código, HTML, MD)? │
└──────────────────────┘ └──────────┬───────────────┘
│
┌────────────────┴────────────┐
│ SÍ │ NO
▼ ▼
┌──────────────────────┐ ┌────────────────────────┐
│ Structural chunking. │ │ 3. ¿Es texto narrativo │
│ Respeta funciones, │ │ largo donde la │
│ headers, secciones. │ │ coherencia importa? │
└──────────────────────┘ │ (legal, médico, │
│ papers académicos) │
└──────────┬─────────────┘
│
┌────────────────────┴───────────┐
│ SÍ │ NO
▼ ▼
┌──────────────────────┐ ┌──────────────────────────┐
│ 4. ¿Presupuesto │ │ Recursive chunking. │
│ para indexing y │ │ Default para 80% de │
│ tolera latencia │ │ casos. Empieza acá. │
│ de re-index? │ └──────────────────────────┘
└──────────┬───────────┘
│
┌─────────────┴────────────┐
│ SÍ │ NO
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ Semantic chunking. │ │ Recursive chunking. │
│ Mejor coherencia, │ │ Buena coherencia sin │
│ vale el costo. │ │ costo extra. │
└──────────────────────┘ └──────────────────────┘
Aplicado a casos reales
| Proyecto | Strategy elegida | Por qué |
|---|---|---|
| MVP de chatbot de soporte (1 semana de dev) | Fixed-size | Iterar rápido; cambiar después |
| Buscador interno de Stack Overflow | Structural (por bloques de código) + Recursive (texto) | Híbrida — código y texto necesitan tratamientos distintos |
| Sistema RAG sobre documentación FastAPI | Recursive con chunk_size=500, overlap=50 | Mezcla narrative + code blocks; recursive maneja los dos OK |
| RAG sobre jurisprudencia legal en español | Semantic | Argumentos largos, coherencia crítica, presupuesto disponible |
| Asistente de papers académicos | Semantic | Hipótesis y conclusiones referencian premisas previas; coherencia paga |
| RAG sobre código fuente Python | Structural (por funciones/clases) | El código tiene unidades naturales |
| RAG genérico empresa SaaS, presupuesto bajo | Recursive | Default, gratis, suficiente |
Strategies híbridas: cuando un solo chunking no alcanza
A veces tu corpus tiene tipos de documentos muy distintos. En ese caso, una sola strategy no es óptima — usa chunking diferenciado por tipo de documento.
# hybrid_chunking.py
from langchain_text_splitters import RecursiveCharacterTextSplitter, MarkdownHeaderTextSplitter
import re
def chunk_by_doc_type(document: str, doc_type: str) -> list[str]:
"""
Aplica strategy distinta según el tipo de documento.
"""
if doc_type == "code":
return chunk_code_structurally(document)
elif doc_type == "markdown":
# Respeta headers de markdown
splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[("#", "h1"), ("##", "h2"), ("###", "h3")]
)
return [section.page_content for section in splitter.split_text(document)]
elif doc_type == "html":
return chunk_html_structurally(document)
elif doc_type == "legal":
# Texto narrativo largo con coherencia crítica → semantic
return semantic_chunk(document, threshold=0.7)
else: # narrative general
# Recursive es el default razonable
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", ". ", " ", ""]
)
return splitter.split_text(document)
def chunk_code_structurally(code: str) -> list[str]:
"""Divide código por funciones y clases (parser-based)."""
# Implementación simplificada usando AST
import ast
chunks = []
try:
tree = ast.parse(code)
for node in ast.walk(tree):
if isinstance(node, (ast.FunctionDef, ast.ClassDef)):
chunk = ast.get_source_segment(code, node)
if chunk:
chunks.append(chunk)
except SyntaxError:
# Fallback a recursive si no parsea
return RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50).split_text(code)
return chunks
def detect_doc_type(content: str, filename: str = "") -> str:
"""Heurística simple para detectar tipo de documento."""
if filename.endswith(('.py', '.js', '.ts', '.java', '.go')):
return "code"
if filename.endswith(('.md', '.markdown')):
return "markdown"
if filename.endswith(('.html', '.htm')):
return "html"
if re.search(r'^(SECTION|ARTICLE|WHEREAS)', content, re.MULTILINE):
return "legal"
return "narrative"
Cuándo vale la pena chunking híbrido
- Corpus mixto significativo: si >20% de tus docs son de un tipo distinto al dominante, vale el esfuerzo.
- Calidad crítica por tipo: si los docs de un tipo (ej: código) son los más consultados, optimizarlos paga.
- Equipo con bandwidth: mantener múltiples chunkers tiene costo de ingeniería.
Cuándo NO vale
- Corpus 90%+ del mismo tipo (un solo chunker basta).
- MVP donde "funciona" alcanza.
- Equipo pequeño sin bandwidth para mantener parsers múltiples.
Cálculo de TCO (total cost of ownership)
Antes de elegir, haz el cálculo concreto. Asume 100K documentos a indexar, dataset que crece 10K docs/mes:
# tco_calculator.py
DOCS_INITIAL = 100_000
DOCS_PER_MONTH_NEW = 10_000
AVG_TOKENS_PER_DOC = 800
# Costos por strategy
def calculate_tco(strategy: str, months: int = 12) -> dict:
"""Calcula TCO para una strategy específica a 12 meses."""
# Costos de indexing inicial
if strategy == "fixed":
initial_cost = 0 # gratis
recurring_monthly = 0
elif strategy == "recursive":
initial_cost = 0
recurring_monthly = 0
elif strategy == "semantic":
# Semantic requiere embebir cada oración para detectar boundaries
# ~3x los tokens del documento
sentences_factor = 3
initial_cost = (DOCS_INITIAL * AVG_TOKENS_PER_DOC * sentences_factor / 1_000_000) * 0.02
recurring_monthly = (DOCS_PER_MONTH_NEW * AVG_TOKENS_PER_DOC * sentences_factor / 1_000_000) * 0.02
elif strategy == "structural":
initial_cost = 0
recurring_monthly = 0
elif strategy == "hybrid_recursive_structural":
initial_cost = 0
recurring_monthly = 0 # solo costo es de ingeniería al setup
# Costo de ingeniería al setup (estimado)
if strategy == "fixed":
eng_setup_hours = 1
elif strategy == "recursive":
eng_setup_hours = 4
elif strategy == "semantic":
eng_setup_hours = 16
elif strategy == "structural":
eng_setup_hours = 24 # parsers por tipo de doc
elif strategy == "hybrid_recursive_structural":
eng_setup_hours = 32
eng_cost = eng_setup_hours * 80 # $80/hr
total_recurring = recurring_monthly * months
total_cost = initial_cost + total_recurring + eng_cost
return {
"strategy": strategy,
"initial_cost_usd": initial_cost,
"monthly_recurring_usd": recurring_monthly,
"engineering_setup_usd": eng_cost,
"total_12_months_usd": total_cost,
}
# Comparar
strategies = ["fixed", "recursive", "semantic", "structural", "hybrid_recursive_structural"]
for s in strategies:
tco = calculate_tco(s, months=12)
print(f"\n{s.upper()}:")
for k, v in tco.items():
if isinstance(v, (int, float)):
print(f" {k}: ${v:.2f}")
else:
print(f" {k}: {v}")
Output típico:
FIXED:
initial_cost_usd: $0.00
monthly_recurring_usd: $0.00
engineering_setup_usd: $80.00
total_12_months_usd: $80.00
RECURSIVE:
initial_cost_usd: $0.00
monthly_recurring_usd: $0.00
engineering_setup_usd: $320.00
total_12_months_usd: $320.00
SEMANTIC:
initial_cost_usd: $4.80
monthly_recurring_usd: $0.48
engineering_setup_usd: $1280.00
total_12_months_usd: $1290.56
STRUCTURAL:
initial_cost_usd: $0.00
monthly_recurring_usd: $0.00
engineering_setup_usd: $1920.00
total_12_months_usd: $1920.00
HYBRID_RECURSIVE_STRUCTURAL:
initial_cost_usd: $0.00
monthly_recurring_usd: $0.00
engineering_setup_usd: $2560.00
total_12_months_usd: $2560.00
Lecturas:
- El costo dominante NO es el costo de embeddings sino el de ingeniería al setup.
- Semantic, structural e híbridas son notablemente más caras de implementar.
- Si la mejora de calidad sobre recursive es <5%, posiblemente no justifica la inversión.
- Para que semantic chunking valga $1290 sobre $320 de recursive, la mejora tiene que ser sustancial.
Cuándo migrar entre strategies
Tu primer chunking no es necesariamente el final. Señales para migrar:
De fixed-size a recursive:
- Empezaste con fixed para MVP. El sistema funciona pero tickets de soporte mencionan "respuestas cortadas a la mitad".
- Inversión: ~4 horas de ingeniería + re-indexar todo el corpus.
- Mejora típica esperada: +10 puntos de precision.
De recursive a structural:
- Tu corpus tiene 30%+ de código o HTML estructurado.
- Recursive parte funciones a la mitad o ignora headers de markdown.
- Inversión: ~16-24 horas + parsers por tipo + re-indexar.
- Mejora típica: +5-10 puntos en queries que tocan los tipos estructurados.
De recursive a semantic:
- Tu corpus es texto narrativo largo con argumentos extensos (legal, médico, papers).
- Recursive parte argumentos justo en transiciones críticas.
- Inversión: ~16 horas + costo recurrente de embeddings extra.
- Mejora típica: +5-8 puntos pero solo si tu dominio realmente necesita coherencia narrativa.
De cualquiera a híbrida:
- Identificaste que tipos específicos de docs subperforman vs el resto.
- Considera optimizar solo esos tipos en lugar de migrar todo.
El error costoso: elegir lo más nuevo en vez de lo apropiado
El antipatrón: ves un blog post sobre "semantic chunking is the new state-of-the-art", lo eliges sin medir contra recursive.
Por qué es trampa:
- Costo de implementación: semantic toma 4x más esfuerzo que recursive.
- Costo recurrente: cada re-index cuesta dinero (embeddings extra).
- Mejora marginal en muchos casos: si tu corpus no tiene argumentos largos, semantic mejora ~2-3% sobre recursive — no justifica el costo.
Cómo evitarlo:
- Empieza con recursive. Funciona en 80% de casos.
- Construye eval set robusto.
- Si el sistema no llega a tu target de calidad, diagnostica la causa antes de cambiar chunking.
- A veces el problema no es chunking sino otra cosa (embeddings, rerank, queries).
# Diagnóstico: ¿es realmente chunking el problema?
def diagnose_chunking_impact(eval_set, current_strategy="recursive"):
"""
Mide si el problema de calidad viene del chunking o de otro componente.
"""
issues = []
for item in eval_set:
# Recuperar con strategy actual
chunks_retrieved = retrieve(item["query"], strategy=current_strategy)
# ¿La info correcta existe en algún chunk?
relevant_in_top_k = any(item["expected_text"] in c for c in chunks_retrieved)
# ¿La info correcta existe en algún chunk del corpus, en cualquier ranking?
relevant_in_corpus = check_full_corpus(item["expected_text"])
if not relevant_in_corpus:
issues.append(("missing_data", item["query"])) # no es chunking
elif not relevant_in_top_k:
# La info está pero no se rankea — chunking puede ser, pero también retrieval
issues.append(("retrieval_or_chunking", item["query"]))
# Si está en top-K, no es problema de chunking
return issues
Trampas y errores comunes
Trampa 1: cambiar strategy sin re-indexar
El error: modificas el código del chunker, deployas. Los chunks viejos siguen igual.
Síntoma: queries sobre docs viejos siguen fallando como antes; queries sobre docs nuevos mejoran. Resultados inconsistentes.
Cómo prevenir: cuando cambias chunking, re-procesar todo el corpus. No es opcional.
Trampa 2: migrar a semantic sin medir el ROI
El error: "el blog dice que semantic chunking mejora 15%". Migras. Tu mejora real es 3% pero gastaste $1500 en ingeniería.
Cómo prevenir: medir mejora esperada sobre tu eval set antes de invertir.
Trampa 3: structural sin fallback
El error: structural chunker falla cuando el documento tiene formato roto. No hay fallback. Esos docs no se chunkean — quedan fuera del corpus indexado.
Síntoma: algunos docs nunca aparecen en queries.
Cómo prevenir: structural con fallback a recursive cuando el parser falla.
Trampa 4: chunk_size copiado sin entender
El error: copias chunk_size=500 de un tutorial. Tu corpus es código (donde 500 chars rara vez son una función completa).
Síntoma: funciones partidas a la mitad. Chunks que terminan en medio de un loop.
Cómo prevenir: ajustar chunk_size al tipo de contenido. Para código, chunk_size=800-1500. Para conversaciones, chunk_size=200-400.
Trampa 5: ignorar overlap al elegir strategy
El error: eliges structural chunking. Los chunks son funciones (cerradas). Asumes que no necesitas overlap.
Síntoma: referencias entre funciones (clase con métodos que dependen entre sí) se pierden.
Cómo prevenir: incluso en structural, agregar 1-2 oraciones de overlap entre chunks consecutivos si hay referencias cruzadas.
Trampa 6: no monitorear quality de chunking en producción
El error: eliges strategy en setup. Nunca vuelves a verificar.
Síntoma: después de 6 meses, el corpus cambió (nuevo tipo de doc agregado), pero el chunker sigue como antes. Calidad degrada gradualmente.
Cómo prevenir: dashboard de quality del chunking — promedio de chars por chunk, distribución de tamaños, % de chunks que terminan en mitad de oración. Alertar si métricas se desvían.
Ejercicio aplicado
Escenario: eres AI Engineer en una startup de educación online. Tu corpus son materiales de cursos:
- 40% texto narrativo (lecciones explicativas)
- 30% código fuente Python con explicaciones inline
- 20% transcripciones de video lecturas
- 10% diagramas (texto OCR de imágenes)
Volumen: 50K documentos. Crece 5K/mes. Equipo: 2 ingenieros, sin MLE dedicado.
Tu pipeline actual usa fixed-size con chunk_size=500. Métricas:
- Precision@5: 65%
- Recall@5: 58%
- Quejas frecuentes: "el bot corta el código al medio", "las explicaciones están descontextualizadas"
Tu trabajo:
- Aplica el framework de 5 preguntas. ¿Qué strategy(s) eliges?
- Justifica considerando el corpus mixto.
- Estima el costo de migración y el impacto esperado.
Solución
1. Aplicación del framework:
- Pregunta 1: ¿prototipo? No, está en producción con quejas reales.
- Pregunta 2: ¿estructura formal? 30% del corpus es código — sí parcialmente.
- Pregunta 3: ¿narrativo largo crítico? 40% es narrativo educativo, importante pero no "crítico" como legal.
- Pregunta 4: ¿presupuesto + tolera latencia de re-index? Equipo pequeño, asumo presupuesto modesto.
Decisión: chunking híbrido (recursive + structural).
- Texto narrativo + transcripciones + OCR (70%): recursive con
chunk_size=500, overlap=50. - Código (30%): structural (por funciones/clases) con fallback a recursive si el parser falla.
2. Justificación detallada
El corpus es mixto pero los problemas principales son:
- "El bot corta el código al medio" → fixed-size parte funciones. Structural lo resuelve.
- "Las explicaciones están descontextualizadas" → falta de overlap. Recursive con overlap lo resuelve.
Semantic chunking sería overkill: el corpus educativo no tiene argumentos legales/médicos largos donde la coherencia narrativa sea crítica. El costo de ingeniería + costo recurrente de embeddings no se justifica.
3. Costo estimado y plan
# Plan de migración
phases = {
"phase_1_recursive_for_text": {
"effort": "8 horas (1 día)",
"covers": "70% del corpus (narrativo + transcripciones + OCR)",
"expected_improvement": "+10-12 puntos precision en queries narrativas",
"eng_cost": "$640",
},
"phase_2_structural_for_code": {
"effort": "24 horas (3 días — parser Python + tests)",
"covers": "30% del corpus (código)",
"expected_improvement": "+15-20 puntos precision en queries de código",
"eng_cost": "$1920",
},
"phase_3_validation": {
"effort": "8 horas",
"covers": "construir eval set de 100 queries (mezcla código/narrativo) + medición A/B",
"eng_cost": "$640",
},
}
total_cost = sum(p["eng_cost"].lstrip("$").replace("$", "") for p in phases.values())
print(f"Total ingeniería: $3,200")
print(f"Costos recurrentes: $0 (no hay LLM extra)")
print(f"Tiempo total: 5 días de trabajo distribuidos")
Impacto esperado:
- Precision@5: 65% → 80-83% (+15-18 puntos, ponderado por % del corpus)
- Recall@5: 58% → 72-75% (+14-17 puntos)
- Quejas de "código cortado" deberían desaparecer.
- Quejas de "explicaciones descontextualizadas" deberían reducir 70-80%.
Plan de validación:
- Día 1-2: Implementar phase 1 (recursive para texto). Tests unitarios.
- Día 3-5: Implementar phase 2 (structural para código). Parser robusto con fallback.
- Día 6: Construir eval set + medir antes/después.
- Si mejora significativa, deployar con feature flag.
Riesgo principal: detectar correctamente qué documento es código vs texto. Solución: heurística simple (extension del archivo, si existe; si no, detectar palabras clave de Python como def, class, import).
Plan B si structural no llega: considerar semantic chunking solo para código (vs structural). Más caro pero más robusto cuando el código tiene formato no-estándar.
Resumen y siguiente paso
Lo que aprendiste:
- Fixed-size es baseline solo para prototipos. Recursive es el default para 80% de casos.
- Structural es óptima cuando el corpus tiene estructura formal (código, HTML, markdown).
- Semantic gana en coherencia pero cuesta dinero y tiempo. Solo justificable en dominios narrativos críticos.
- Strategies híbridas son válidas para corpus mixtos significativos. Costo de ingeniería extra, pero mejor calidad por tipo de documento.
- TCO de chunking incluye más que costo de embeddings — el costo dominante suele ser ingeniería al setup.
- Migrar entre strategies requiere re-indexar todo el corpus. No es solo cambio de código.
- Antipatrón clave: elegir semantic "porque es lo más nuevo" sin medir ROI sobre tu eval set.
Checkpoint: antes de avanzar, deberías poder:
- Aplicar el framework de 5 preguntas a un proyecto nuevo y elegir strategy en <10 min.
- Calcular TCO aproximado para distintas strategies en tu volumen.
- Diseñar un chunking híbrido cuando el corpus es mixto.
Siguiente cápsula: 08 — Proyecto Chunking Optimizer.
Cierre del módulo: vas a construir un sistema que toma un corpus, prueba múltiples strategies sobre el mismo eval set, y produce un reporte comparativo que permite tomar la decisión basada en datos. Es la herramienta que usarías el día 1 de cualquier proyecto RAG nuevo.
Recursos
- LangChain — Text Splitters Comparison — Comparación de splitters
- LlamaIndex — Node Parsers — Implementaciones alternativas
- Greg Kamradt — 5 Levels of Chunking — Tutorial visual de strategies
- Pinecone — Chunking Strategies — Comparación con benchmarks
- Anthropic — Contextual Retrieval — Técnica complementaria al chunking
- LangChain — Markdown Header Splitter — Para chunking estructural en MD
Tiempo estimado: 25-30 minutos Siguiente: 08-project-chunking-optimizer.md