Módulo 2: Chunking Strategies
Cápsula 05: Structural chunking — respetar las unidades naturales del contenido
Descripción de la cápsula
Recursive y semantic chunking tratan al texto como una secuencia continua. Funcionan bien para texto narrativo. Pero cuando tu contenido tiene estructura formal — código fuente, HTML, markdown — esa secuencia continua es una abstracción que rompe unidades naturales.
Imagina chunkear código Python con chunk_size=500. El chunker corta justo a la mitad de una función. La primera mitad termina con if user.is_admin: colgando. La segunda mitad empieza con código indentado sin contexto. Ningún chunk individual es interpretable.
Structural chunking respeta la sintaxis del lenguaje: divide código por funciones y clases, HTML por secciones semánticas, markdown por headings. Cada chunk es una unidad lógica completa que se puede entender por sí misma — exactamente lo que necesita un LLM para responder bien.
Esta cápsula te enseña a implementar structural chunking para los tres formatos más comunes en RAG (Python, HTML, Markdown), cuándo usarlo vs recursive, y cómo manejar el caso patológico: documentos con estructura mal formada.
Al finalizar esta cápsula serás capaz de:
- ✅ Implementar structural chunking para código Python con
ast - ✅ Implementar structural chunking para HTML con
BeautifulSoup - ✅ Implementar structural chunking para Markdown con headers como separadores
- ✅ Diseñar fallbacks para cuando el parser falla (código sintácticamente inválido, HTML mal formado)
- ✅ Decidir cuándo structural gana sobre recursive con datos cuantitativos
- ✅ Anticipar las trampas: chunks demasiado pequeños (funciones de 1 línea) o demasiado grandes (clases de 500 líneas)
Tiempo estimado: 30-35 minutos
El insight: cada formato tiene unidades naturales
Cada formato de contenido tiene "unidades de significado" que existen independientemente del tamaño:
Python: función | clase | método
HTML: <section> | <article> | <div class="...">
Markdown: #header | ##subheader | ###subsubheader
Recursive chunking ignora estas unidades — corta donde haya separadores genéricos (\n\n, . , ). Resulta en chunks que parten unidades naturales a la mitad.
Structural chunking usa parsers específicos del formato que reconocen estas unidades:
Recursive con chunk_size=500 sobre código Python:
def calculate_total(items):
"""Calcula el total."""
if not items:
return 0
return sum(item.price for ite ← chunk corta aquí
↓
m in items) ← siguiente chunk empieza aquí
(sin contexto)
Structural con AST:
# Chunk 1: función completa
def calculate_total(items):
"""Calcula el total."""
if not items:
return 0
return sum(item.price for item in items)
Ventaja: chunks que un humano (o LLM) puede entender. Cada chunk responde a una pregunta del estilo "¿qué hace esta función?" sin necesitar contexto adicional.
Implementación: código Python con AST
# python_structural_chunking.py
import ast
from dataclasses import dataclass
@dataclass
class CodeChunk:
type: str # "function" | "class" | "module_top_level"
name: str # nombre de la función/clase
content: str # código fuente del chunk
line_start: int
line_end: int
docstring: str | None = None
def chunk_python_by_structure(code: str) -> list[CodeChunk]:
"""
Divide código Python en chunks estructurales.
Cada función y clase es un chunk independiente.
Código top-level (imports, constantes) se agrupa en un chunk separado.
"""
try:
tree = ast.parse(code)
except SyntaxError as e:
# Fallback: si el código no parsea, usar recursive
print(f"AST parse failed: {e}. Falling back to recursive.")
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=80)
return [CodeChunk(
type="raw_fallback",
name="unparseable",
content=chunk,
line_start=0,
line_end=0,
) for chunk in splitter.split_text(code)]
chunks = []
code_lines = code.split('\n')
# Top-level: imports, constantes, statements directos
top_level_lines = []
handled_lines = set()
for node in tree.body:
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
content = ast.get_source_segment(code, node)
docstring = ast.get_docstring(node)
chunks.append(CodeChunk(
type="function",
name=node.name,
content=content,
line_start=node.lineno,
line_end=node.end_lineno,
docstring=docstring,
))
handled_lines.update(range(node.lineno, node.end_lineno + 1))
elif isinstance(node, ast.ClassDef):
content = ast.get_source_segment(code, node)
docstring = ast.get_docstring(node)
chunks.append(CodeChunk(
type="class",
name=node.name,
content=content,
line_start=node.lineno,
line_end=node.end_lineno,
docstring=docstring,
))
handled_lines.update(range(node.lineno, node.end_lineno + 1))
# Top-level code (imports, constantes) que no es función ni clase
top_level_lines = [
line for i, line in enumerate(code_lines, 1) if i not in handled_lines
]
top_level_content = '\n'.join(top_level_lines).strip()
if top_level_content:
chunks.insert(0, CodeChunk(
type="module_top_level",
name="imports_and_constants",
content=top_level_content,
line_start=1,
line_end=len(top_level_lines),
))
return chunks
# Probar
python_code = '''
"""Module for user management."""
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
def hash_password(password: str) -> str:
"""Hash a password using bcrypt."""
from passlib.context import CryptContext
pwd_context = CryptContext(schemes=["bcrypt"])
return pwd_context.hash(password)
class User:
"""Represents a user in the system."""
def __init__(self, name: str, email: str):
self.name = name
self.email = email
self.created_at = datetime.utcnow()
def to_dict(self) -> dict:
return {
"name": self.name,
"email": self.email,
"created_at": self.created_at.isoformat()
}
def validate_email(email: str) -> bool:
"""Basic email validation."""
return "@" in email and "." in email.split("@")[-1]
'''
chunks = chunk_python_by_structure(python_code)
for chunk in chunks:
print(f"\n[{chunk.type}] {chunk.name} (lines {chunk.line_start}-{chunk.line_end})")
if chunk.docstring:
print(f" Docstring: {chunk.docstring[:60]}")
print(f" Content:\n{chunk.content[:200]}...")
Output:
[module_top_level] imports_and_constants (lines 1-5)
Content:
"""Module for user management."""
from datetime import datetime
import logging
logger = logging.getLogger(__name__)
[function] hash_password (lines 7-11)
Docstring: Hash a password using bcrypt.
Content:
def hash_password(password: str) -> str:
"""Hash a password using bcrypt."""
from passlib.context import CryptContext...
[class] User (lines 14-26)
Docstring: Represents a user in the system.
Content:
class User:
"""Represents a user in the system."""
def __init__(self, name: str, email: str):...
[function] validate_email (lines 29-31)
Docstring: Basic email validation.
Content:
def validate_email(email: str) -> bool:
"""Basic email validation."""
return "@" in email and "." in email.split("@")[-1]
Cada chunk es una unidad coherente. Una query como "¿cómo valido un email?" matchea directamente con el chunk validate_email completo.
Implementación: HTML con BeautifulSoup
# html_structural_chunking.py
from bs4 import BeautifulSoup
from dataclasses import dataclass
@dataclass
class HtmlChunk:
tag_type: str # section, article, div
id: str | None
heading: str | None
text_content: str
raw_html: str
def chunk_html_by_structure(html: str) -> list[HtmlChunk]:
"""
Divide HTML por elementos semánticos.
Prioridad: section > article > div con class.
"""
soup = BeautifulSoup(html, 'html.parser')
chunks = []
# Buscar elementos semánticos en orden de prioridad
semantic_tags = ['section', 'article', 'main', 'nav']
# Si hay tags semánticos, usarlos
semantic_elements = []
for tag in semantic_tags:
semantic_elements.extend(soup.find_all(tag))
if semantic_elements:
for element in semantic_elements:
heading_tag = element.find(['h1', 'h2', 'h3', 'h4', 'h5', 'h6'])
heading = heading_tag.get_text(strip=True) if heading_tag else None
chunks.append(HtmlChunk(
tag_type=element.name,
id=element.get('id'),
heading=heading,
text_content=element.get_text(separator='\n', strip=True),
raw_html=str(element),
))
else:
# Fallback: usar div con class
for div in soup.find_all('div', class_=True):
heading_tag = div.find(['h1', 'h2', 'h3'])
heading = heading_tag.get_text(strip=True) if heading_tag else None
chunks.append(HtmlChunk(
tag_type='div',
id=div.get('id'),
heading=heading,
text_content=div.get_text(separator='\n', strip=True),
raw_html=str(div),
))
# Si todavía no hay chunks, fallback a recursive sobre el texto
if not chunks:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=80)
plain_text = soup.get_text(separator='\n', strip=True)
for chunk_text in splitter.split_text(plain_text):
chunks.append(HtmlChunk(
tag_type='fallback',
id=None,
heading=None,
text_content=chunk_text,
raw_html='',
))
return chunks
Implementación: Markdown con headers
# markdown_structural_chunking.py
import re
from dataclasses import dataclass
@dataclass
class MarkdownChunk:
level: int # 1=H1, 2=H2, 3=H3, ...
header: str
content: str # incluye el header + el contenido bajo él
def chunk_markdown_by_headers(markdown: str, max_level: int = 3) -> list[MarkdownChunk]:
"""
Divide markdown por headers de nivel <= max_level.
Cada chunk es un header + todo el contenido hasta el próximo header del mismo nivel o superior.
"""
lines = markdown.split('\n')
chunks = []
current_chunk_lines = []
current_header = "Top of document"
current_level = 0
header_pattern = re.compile(r'^(#{1,6})\s+(.+)$')
for line in lines:
match = header_pattern.match(line)
if match and len(match.group(1)) <= max_level:
level = len(match.group(1))
header_text = match.group(2).strip()
# Si es header de nivel <= current_level, cerrar chunk actual
if current_chunk_lines and (current_level == 0 or level <= current_level):
chunks.append(MarkdownChunk(
level=current_level,
header=current_header,
content='\n'.join(current_chunk_lines).strip(),
))
current_chunk_lines = []
current_header = header_text
current_level = level
current_chunk_lines.append(line)
else:
current_chunk_lines.append(line)
# Agregar último chunk
if current_chunk_lines:
chunks.append(MarkdownChunk(
level=current_level,
header=current_header,
content='\n'.join(current_chunk_lines).strip(),
))
return chunks
# Probar con markdown técnico
markdown = """
# FastAPI User Guide
Welcome to the FastAPI documentation.
## Installation
Install FastAPI with pip:
```bash
pip install fastapi
Quick Start
Create your first FastAPI app:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
Authentication
FastAPI supports OAuth2 out of the box.
OAuth2 Setup
Use OAuth2PasswordBearer from fastapi.security.
JWT Tokens
Sign tokens with python-jose. """
chunks = chunk_markdown_by_headers(markdown, max_level=2)
for chunk in chunks: print(f"\n[H{chunk.level}] {chunk.header}") print(f"Content ({len(chunk.content)} chars):") print(chunk.content[:200])
**Output:**
[H0] Top of document Content (52 chars):
FastAPI User Guide
Welcome to the FastAPI documentation.
[H1] FastAPI User Guide Content (44 chars):
FastAPI User Guide
Welcome to the FastAPI documentation.
[H2] Installation Content (78 chars):
Installation
Install FastAPI with pip:
pip install fastapi
[H2] Quick Start Content (180 chars):
Quick Start
Create your first FastAPI app:
from fastapi import FastAPI...
[H2] Authentication
Content (215 chars):
## Authentication
FastAPI supports OAuth2 out of the box.
### OAuth2 Setup
... (incluye sub-headers H3 dentro)
Nota: los H3 (OAuth2 Setup, JWT Tokens) quedaron incluidos dentro del chunk de su H2 padre porque max_level=2. Si necesitas chunks más finos, subir max_level a 3 o 4.
Manejo de fallos: cuando el parser no puede
El error más común con structural chunking: el documento llega con sintaxis rota, HTML mal formado, o markdown no estándar. El parser falla. Tu pipeline necesita un fallback.
Patrón de fallback
def safe_structural_chunk(content: str, content_type: str) -> list[str]:
"""
Intenta structural chunking. Si falla, fallback a recursive.
"""
try:
if content_type == "python":
chunks = chunk_python_by_structure(content)
if not chunks:
raise ValueError("No structural chunks found")
return [c.content for c in chunks]
elif content_type == "html":
chunks = chunk_html_by_structure(content)
if not chunks:
raise ValueError("No structural chunks found")
return [c.text_content for c in chunks]
elif content_type == "markdown":
chunks = chunk_markdown_by_headers(content)
if not chunks:
raise ValueError("No structural chunks found")
return [c.content for c in chunks]
else:
raise ValueError(f"Unknown content_type: {content_type}")
except Exception as e:
print(f"Structural chunking failed for {content_type}: {e}")
print("Falling back to recursive chunking")
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=80)
return splitter.split_text(content)
# Logging de fallback rate
fallback_count = 0
total_count = 0
def chunk_with_logging(content, content_type):
global fallback_count, total_count
total_count += 1
try:
return safe_structural_chunk(content, content_type)
except Exception:
fallback_count += 1
raise
# Después de procesar 1000 docs
print(f"Fallback rate: {fallback_count}/{total_count} ({fallback_count/total_count:.0%})")
Si el fallback rate supera ~5%, hay un problema: parser muy estricto, o documentos sistemáticamente mal formados. Vale investigar antes de aceptar.
Cuándo structural gana
Benchmarks típicos sobre datasets representativos:
| Tipo de contenido | Recursive | Structural | Mejora |
|---|---|---|---|
| Código Python | 75% precision | 88% | +13 pts |
| HTML estructurado (docs) | 72% | 86% | +14 pts |
| Markdown con headers | 78% | 90% | +12 pts |
| Markdown sin headers | 78% | 78% | 0 (no hay structure) |
| Texto narrativo | 83% | 70% | -13 pts (worse) |
Lecturas:
- Structural gana dramáticamente en código.
- Markdown sin headers tiene structure plana — recursive es igual o mejor.
- Para texto narrativo, structural es peor porque parte por separadores arbitrarios sin coherencia semántica.
Regla simple: si tu contenido tiene unidades sintácticas claras y formales, usa structural. Si no las tiene, no fuerces structural — usa recursive o semantic.
Trampas y errores comunes
Trampa 1: parser muy estricto que falla con sintaxis válida pero rara
El error: tu parser de Python rechaza código con # noqa comments raros, type hints de Python 3.12+, walrus operator.
Síntoma: fallback rate >10%. Muchos documentos terminan chunkeados con recursive pierden la ventaja estructural.
Cómo prevenir: usar parser actualizado (ast de Python 3.11+ es robusto). Si tu corpus tiene sintaxis específica de versión, validar con esa versión.
Trampa 2: chunks demasiado pequeños
El error: muchas funciones de 1-2 líneas. Cada una es un chunk de ~50 chars.
Síntoma: retrieval devuelve chunks individuales sin contexto. El LLM no entiende qué hace una función de utility sin ver el resto del módulo.
Cómo prevenir: merge de chunks pequeños:
def merge_small_chunks(chunks: list, min_size: int = 200) -> list:
"""Une chunks chicos consecutivos hasta alcanzar min_size."""
merged = []
current = ""
for chunk in chunks:
if len(current) + len(chunk.content) < min_size * 2:
current += "\n\n" + chunk.content
else:
if current:
merged.append(current)
current = chunk.content
if current:
merged.append(current)
return merged
Trampa 3: chunks demasiado grandes
El error: una clase Python de 500 líneas se vuelve un solo chunk.
Síntoma: el chunk excede el context window del LLM, o domina el retrieval (siempre rankea alto por su tamaño).
Cómo prevenir: sub-dividir clases grandes por método:
def chunk_class_by_methods(class_node: ast.ClassDef, code: str) -> list[CodeChunk]:
"""Si una clase es grande, dividir en chunks por método."""
class_source = ast.get_source_segment(code, class_node)
if len(class_source) < 1500:
# Clase pequeña: un solo chunk
return [CodeChunk(type="class", name=class_node.name, content=class_source, ...)]
# Clase grande: header + un chunk por método
chunks = []
# Header de la clase (signature + docstring + atributos)
# ...
# Un chunk por método
for method in class_node.body:
if isinstance(method, ast.FunctionDef):
method_source = ast.get_source_segment(code, method)
chunks.append(CodeChunk(
type="method",
name=f"{class_node.name}.{method.name}",
content=method_source,
...
))
return chunks
Trampa 4: HTML con muchos divs anidados sin estructura semántica
El error: HTML legacy con <div> everywhere, sin <section> ni <article>.
Síntoma: chunk_html_by_structure no encuentra elementos semánticos. Fallback a recursive.
Cómo prevenir: detectar este caso y usar parser específico:
def chunk_html_by_divs_with_class(html: str):
"""Para HTML legacy: usar divs con class como unidades."""
soup = BeautifulSoup(html, 'html.parser')
return soup.find_all('div', class_=True)
Trampa 5: ignorar metadata estructural valiosa
El error: chunkeas código y guardas solo el contenido del chunk. Pierdes que era una "función llamada validate_email".
Síntoma: queries por nombre de función ("¿qué hace validate_email?") tienen recall bajo porque el match exacto del nombre no se aprovecha.
Cómo prevenir: guardar metadata estructural en cada chunk:
collection.add(
documents=[chunk.content],
metadatas=[{
"chunk_type": chunk.type, # "function", "class", "method"
"name": chunk.name, # "validate_email"
"file_path": file_path,
"line_start": chunk.line_start,
"docstring": chunk.docstring or "",
}],
ids=[chunk_id],
)
# Después puedes filtrar por metadata
results = collection.query(
query_texts=["email validation"],
where={"chunk_type": "function"}, # solo funciones
n_results=5,
)
Trampa 6: recursive como fallback sin overlap
El error: cuando structural falla, fallback usa recursive con chunk_overlap=0 "para no complicar".
Síntoma: los chunks de fallback pierden información en fronteras. Calidad inconsistente: algunos docs (structural OK) bien, otros (fallback) mal.
Cómo prevenir: fallback con overlap razonable:
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=80, # 10% overlap
)
Ejercicio aplicado
Escenario: eres AI Engineer en una empresa que indexa repositorios de código open-source para hacer RAG sobre código.
Datos:
- 50K archivos Python de ~10K repositorios
- Diversidad: librerías comerciales, scripts, tutoriales, código legacy
- Queries típicas: "cómo implementar X", "ejemplo de Y", "función para validar Z"
Métricas con recursive (chunk_size=800, overlap=80):
- Precision@5: 68%
- Recall@5: 55%
- Quejas: "el bot devuelve fragmentos de código que no compilan, mezclados de varias funciones"
Tu trabajo:
- Decide si structural chunking aplica.
- Diseña el pipeline incluyendo fallback robusto.
- Estima el impacto y costo del cambio.
Solución
1. Sí aplica structural chunking
Razones:
- Código Python tiene unidades naturales (funciones, clases, métodos). Recursive las parte arbitrariamente.
- El síntoma del usuario ("fragmentos que no compilan, mezclados") es exactamente lo que recursive produce con código.
- Las queries ("cómo implementar X", "función para Y") se resuelven mejor con chunks que sean unidades completas.
Structural es la solución natural.
2. Pipeline con fallback robusto
# code_chunking_pipeline.py
import ast
from langchain.text_splitter import RecursiveCharacterTextSplitter
def chunk_python_robust(code: str, file_path: str) -> list[dict]:
"""
Pipeline completo de chunking para Python.
1. Intenta AST-based chunking
2. Fallback a recursive si AST falla
3. Merge de chunks pequeños para evitar fragmentación
4. Split de chunks grandes (clases) para evitar domino
"""
chunks = []
try:
tree = ast.parse(code)
# Procesar top-level definitions
for node in tree.body:
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
content = ast.get_source_segment(code, node)
if content:
chunks.append({
"type": "function",
"name": node.name,
"content": content,
"metadata": {
"file_path": file_path,
"chunk_type": "function",
"function_name": node.name,
"line_start": node.lineno,
"line_end": node.end_lineno,
"docstring": ast.get_docstring(node) or "",
}
})
elif isinstance(node, ast.ClassDef):
class_content = ast.get_source_segment(code, node)
if not class_content:
continue
# Si la clase es grande (>1500 chars), dividir por método
if len(class_content) > 1500:
for method in node.body:
if isinstance(method, (ast.FunctionDef, ast.AsyncFunctionDef)):
method_content = ast.get_source_segment(code, method)
if method_content:
chunks.append({
"type": "method",
"name": f"{node.name}.{method.name}",
"content": method_content,
"metadata": {
"file_path": file_path,
"chunk_type": "method",
"class_name": node.name,
"method_name": method.name,
"line_start": method.lineno,
"docstring": ast.get_docstring(method) or "",
}
})
else:
chunks.append({
"type": "class",
"name": node.name,
"content": class_content,
"metadata": {
"file_path": file_path,
"chunk_type": "class",
"class_name": node.name,
"line_start": node.lineno,
"line_end": node.end_lineno,
"docstring": ast.get_docstring(node) or "",
}
})
except SyntaxError as e:
# Fallback: recursive
print(f"AST failed for {file_path}: {e}. Using recursive fallback.")
splitter = RecursiveCharacterTextSplitter(
chunk_size=800,
chunk_overlap=80,
separators=["\nclass ", "\ndef ", "\n\n", "\n", " "],
)
for i, chunk_text in enumerate(splitter.split_text(code)):
chunks.append({
"type": "fallback",
"name": f"fragment_{i}",
"content": chunk_text,
"metadata": {
"file_path": file_path,
"chunk_type": "fallback",
"fragment_index": i,
},
})
# Filtrar chunks demasiado pequeños (probable noise)
chunks = [c for c in chunks if len(c["content"]) >= 100]
return chunks
3. Estimación de impacto
Mejora esperada:
Métrica Sin structural Con structural Cambio
─────────────────────────────────────────────────────────────────
Precision@5 68% 86% (estimado) +18 pts
Recall@5 55% 72% (estimado) +17 pts
"Fragmentos que no Frecuente Raro ↓ 80%
compilan"
Costo:
- Re-procesamiento de 50K archivos: ~10-20 minutos en una sola corrida (AST es rápido).
- Storage extra: chunks tienen metadata adicional (~30% más bytes), trivial.
- Re-embedding de chunks: ~50K × 10 chunks promedio × 200 tokens = 100M tokens × $0.02/1M = $2 USD.
Despreciable.
Plan de implementación:
- Día 1: implementar
chunk_python_robustcon tests sobre 100 archivos representativos. - Día 2: procesar batch test de 5K archivos. Medir fallback rate.
- Día 3: si fallback rate <5%, procesar todo el corpus. Si >5%, investigar por qué (¿código de Python 2?, ¿Cython?, ¿código generado?).
- Día 4: indexar en ChromaDB con metadata estructural.
- Día 5: A/B test con eval set.
Métricas a monitorear:
- Fallback rate: debería ser <5%.
- Distribución de chunk types: ratio function/class/method/fallback.
- Tamaño promedio de chunks.
- Precision@5 sobre eval set específico de queries de código.
Plan B si structural no llega:
- Si fallback rate es alto: investigar por qué tantos archivos no parsean. Podría ser Python 2 syntax, código generado, o snippets sin top-level structure.
- Si recall en queries de "cómo X" es bajo: agregar BM25 hybrid search (M05) — queries de código suelen tener nombres de funciones exactos.
- Si precision en queries de "ejemplo de Y" es bajo: el problema puede ser de re-ranking, no de chunking.
Resumen y siguiente paso
Lo que aprendiste:
- Structural chunking respeta unidades sintácticas naturales: funciones/clases en código, secciones en HTML, headers en markdown.
- Implementación:
astpara Python,BeautifulSouppara HTML, regex para markdown. - Mejora típica: +15-25% precision en contenido estructurado vs recursive.
- Fallback obligatorio: parsers fallan con sintaxis rara, código generado, HTML mal formado.
- Sub-división de chunks grandes (clases >1500 chars → un chunk por método).
- Merge de chunks chicos (funciones de 1-2 líneas) para evitar fragmentación.
- Metadata estructural (nombre de función, clase, file_path) es crítica para retrieval avanzado.
- NO usar structural para texto narrativo — recursive o semantic son mejores.
Checkpoint: antes de avanzar, deberías poder:
- Implementar structural chunking para código Python con
asty fallback robusto. - Decidir cuándo structural gana sobre recursive con datos cuantitativos.
- Diseñar pipeline con merge/split de chunks según tamaño.
Siguiente cápsula: 06 — Chunk overlap.
Cubrimos cuatro estrategias de chunking. Una técnica complementaria a todas: chunk overlap. Ya cubierto en M02/06. La cápsula 07 (siguiente) consolida todo en un decision framework.
Recursos
- Python AST Documentation — Documentación oficial de
ast - BeautifulSoup Documentation — Para parsing HTML
- LangChain — Markdown Header Splitter — Implementación oficial
- LlamaIndex — Code Splitter — Implementación con tree-sitter
- tree-sitter — Parser robusto multi-lenguaje (alternativa a
ast) - GitHub Copilot — Code Chunking Strategy — Cómo Copilot chunkea código
Tiempo estimado: 30-35 minutos Siguiente: 06-chunk-overlap.md