Fragmentación e Ingesta
La ingesta transforma documentos sin procesar en fragmentos buscables con embeddings. El tamaño del fragmento, la superposición y los metadatos afectan directamente la calidad de la recuperación de RAG.
Receta
from langchain_text_splitters import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
chunks = splitter.split_documents(documents)Cuándo usar esto: para indexar PDFs, wikis, repositorios de código o cualquier corpus de documentos para RAG.
Ejemplo de Trabajo
"""chunking_ingestion.py - limpiar, fragmentar, incrustar e indexar documentos."""
from __future__ import annotations
import re
import chromadb
from openai import OpenAI
client = OpenAI()
RAW_DOCS = [
{"id": "doc1", "source": "handbook", "text": "# Pruebas con Python\n\nUsa pytest para pruebas unitarias. " * 20},
{"id": "doc2", "source": "handbook", "text": "# FastAPI\n\nCrea APIs con type hints. " * 20},
]
def clean_text(text: str) -> str:
text = re.sub(r"\s+", " ", text).strip()
return text
def chunk_text(text: str, size: int = 400, overlap: int = 80) -> list[str]:
chunks = []
start = 0
while start < len(text):
chunks.append(text[start:start + size])
start += size - overlap
return chunks
chroma = chromadb.Client()
collection = chroma.get_or_create_collection("handbook")
for doc in RAW_DOCS:
cleaned = clean_text(doc["text"])
for i, chunk in enumerate(chunk_text(cleaned)):
chunk_id = f"{doc['id']}_chunk_{i}"
emb = client.embeddings.create(model="text-embedding-3-small", input=[chunk])
collection.add(
ids=[chunk_id],
documents=[chunk],
embeddings=[emb.data[0].embedding],
metadatas=[{"source": doc["source"], "doc_id": doc["id"], "chunk_index": i}],
)
print(f"indexados {collection.count()} fragmentos")Lo que esto demuestra: limpieza de texto, fragmentación de tamaño fijo con superposición, metadatos por fragmento y embedding en el momento de la ingesta.
Profundización
Directrices de Tamaño de Fragmento
| Tipo de Contenido | Tamaño del Fragmento | Superposición |
|---|---|---|
| Prosa técnica | 500-1000 caracteres | 10-20% |
| Código | Nivel de función/clase | Mínimo |
| Legal/contratos | 200-500 tokens | 20% |
| Registros de chat | Turno completo o párrafo | 0-10% |
Errores Comunes
- Fragmentos demasiado grandes - la recuperación devuelve secciones irrelevantes. Solución: fragmentos más pequeños con buena superposición.
- Fragmentos demasiado pequeños - pierden contexto para la generación. Solución: aumentar el tamaño o añadir recuperación de documentos padre.
- Sin metadatos - no se pueden filtrar ni citar fuentes. Solución: almacenar fuente, página, título con cada fragmento.
- HTML sucio - la navegación y los anuncios contaminan los embeddings. Solución: eliminar texto repetitivo antes de fragmentar.
- Re-embedding de documentos sin cambios - desperdicia llamadas a la API. Solución: hashear el contenido; omitir si no ha cambiado.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Fragmentos de tamaño fijo | Prosa general | Documentos estructurados con secciones claras |
| Divisor recursivo | Contenido mixto | Código con límites estrictos |
| Fragmentación semántica | Longitudes de sección variables | Documentos simples y uniformes |
| Fragmentos padre-hijo | Necesidad de precisión y contexto | Corpora pequeños |
Preguntas Frecuentes
¿Con qué tamaño de fragmento debo empezar?
- 500-1000 caracteres para texto general.
- Evaluar la recuperación de la búsqueda en 20 consultas de prueba.
- Ajustar según si los fragmentos contienen suficiente contexto.
¿Cómo fragmento PDFs?
- Extraer texto con
pymupdfopdfplumber. - Conservar los números de página en los metadatos.
- Manejar diseños multicolumna con cuidado.
¿Cómo fragmento código?
- Dividir por límites de función/clase usando análisis AST.
- Incluir la ruta del archivo y el idioma en los metadatos.
¿Debo fragmentar en la ingesta o en el momento de la consulta?
- Siempre en la ingesta; el momento de la consulta solo incrusta la pregunta.
¿Cómo manejo las actualizaciones de documentos?
- Eliminar fragmentos antiguos por
doc_id; re-ingestar secciones modificadas. - Rastrear el hash del contenido para detectar cambios.
¿Qué es la recuperación de documentos padre?
- Almacenar fragmentos pequeños para la búsqueda; recuperar la sección padre para la generación.
- Lo mejor de la precisión y el contexto.
¿Cómo ingesto markdown?
- Dividir por encabezados (
#,##) para secciones semánticas. - Conservar la jerarquía de encabezados en los metadatos.
¿Cuántos fragmentos por documento?
- Depende de la longitud del documento.
- Monitorear el número promedio de fragmentos por tipo de fuente.
¿Debo desduplicar fragmentos?
- Sí; los fragmentos casi duplicados desperdician espacio en el índice.
- Hashear el texto normalizado; omitir duplicados.
¿Cómo hago lotes de embeddings en la ingesta?
- Incrustar 100-500 fragmentos por llamada a la API.
- OpenAI acepta hasta 2048 entradas por solicitud.
¿Qué metadatos debo almacenar?
- fuente,
doc_id, título, página, sección,created_at,content_hash.
¿Cómo pruebo la calidad de la fragmentación?
- Revisión manual de 20 fragmentos aleatorios.
- Comprobar la recuperación de la búsqueda en pares de consulta-fragmento etiquetados.
Relacionado
- Conceptos básicos de RAG - pipeline de extremo a extremo
- Embeddings y Similitud - modelos de embeddings
- Bases de Datos Vectoriales - almacenamiento
- Recuperación y Re-ranking - calidad de búsqueda
- LlamaIndex - framework de datos
Versiones de la pila: Esta página se escribió para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ y uv 0.6+.