Saltar al contenido principal

Capítulo 2: Estrategias de División de Texto

Introducción​

La división de texto (chunking) es crucial para el rendimiento de RAG. El tamaño y la estrategia adecuados de los fragmentos impactan significativamente en la calidad de la recuperación y la precisión de la generación.

Por qué es importante la división en fragmentos​

Problemas sin división en fragmentos:

  • Los documentos completos son demasiado grandes
  • La recuperación devuelve contenido irrelevante
  • Se desperdicia la ventana de contexto del LLM

Parámetros clave​

Tamaño del fragmento​

El número de caracteres/tokens por fragmento:

Tamaño del fragmentoVentajasDesventajas
Pequeño (256)Recuperación precisaPuede perder contexto
Mediano (512-1024)EquilibradoBueno para la mayoría de los casos
Grande (2000+)Más contextoRecuperación menos precisa

Superposición de fragmentos​

Texto compartido entre fragmentos consecutivos:

Chunk 1: [_______________]
Chunk 2: [_______________]
^overlap^

¿Por qué superponer?

  • Evita cortar oraciones a la mitad
  • Preserva el contexto entre los límites de los fragmentos
  • Típico: 10-20% del tamaño del fragmento

Estrategias de división​

1. División recursiva por caracteres​

El enfoque más común: divide por jerarquía de separadores:

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ". ", " ", ""]
)

chunks = splitter.split_documents(documents)

Jerarquía de separadores:

  1. Doble salto de línea (párrafos)
  2. Salto de línea simple
  3. Puntos (oraciones)
  4. Espacios (palabras)
  5. Caracteres (último recurso)

2. División basada en tokens​

Utiliza el conteo real de tokens (importante para los límites de los LLM):

from langchain.text_splitter import TokenTextSplitter

splitter = TokenTextSplitter(
chunk_size=500, # tokens, no caracteres
chunk_overlap=50,
encoding_name="cl100k_base" # Tokenizador de GPT-4
)

3. División por oraciones​

Preserva los límites de las oraciones:

from langchain.text_splitter import SentenceTransformersTokenTextSplitter

splitter = SentenceTransformersTokenTextSplitter(
chunk_overlap=50,
tokens_per_chunk=256
)

4. División semántica​

Divide según la similitud semántica:

from langchain_experimental.text_splitter import SemanticChunker
from langchain.embeddings import OpenAIEmbeddings

splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile"
)

# Fragmentos basados en similitud semántica
semantic_chunks = splitter.split_documents(documents)

5. División por encabezados Markdown​

Respeta la estructura del documento:

from langchain.text_splitter import MarkdownHeaderTextSplitter

headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]

splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on
)

Cómo elegir el tamaño del fragmento​

def optimal_chunk_size(embedding_model, llm_context_window, top_k):
"""
Calculate optimal chunk size
"""
# Leave room for prompt template and response
available_tokens = llm_context_window * 0.6

# Divide by number of chunks to retrieve
chunk_tokens = available_tokens / top_k

# Convert to characters (roughly 4 chars per token)
return int(chunk_tokens * 4)

# Example: GPT-4 with 8K context, retrieving 5 chunks
optimal = optimal_chunk_size(
embedding_model="text-embedding-3-small",
llm_context_window=8192,
top_k=5
)
# Result: ~3900 characters per chunk

Tabla comparativa​

EstrategiaMejor paraCalidad del fragmento
Recursiva por caracteresPropósito generalBuena
Basada en tokensGestión de contexto LLMExcelente
Por oracionesSistemas de preguntas y respuestasBuena
SemánticaContenido variadoExcelente
Encabezados MarkdownDocumentos estructuradosExcelente

Mejores prácticas​

  1. Comience con los valores predeterminados - 1000 caracteres, 200 de superposición
  2. Pruebe diferentes tamaños - Evalúe la calidad de la recuperación
  3. Adapte al contenido - Documentación técnica puede requerir fragmentos más grandes
  4. Considere los embeddings - Algunos modelos tienen límites máximos de tokens
  5. Preserve los metadatos - Registre el origen y la posición del fragmento
# Pipeline de fragmentación para producción
def chunk_documents(documents, chunk_size=1000, overlap=200):
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=overlap,
length_function=len,
add_start_index=True # Registrar la posición
)

chunks = splitter.split_documents(documents)

# Agregar índice de fragmento a los metadatos
for i, chunk in enumerate(chunks):
chunk.metadata["chunk_index"] = i
chunk.metadata["total_chunks"] = len(chunks)

return chunks

Resumen​

  • El tamaño del fragmento afecta la precisión de la recuperación frente al contexto
  • La superposición previene la pérdida de información en los límites
  • La estrategia debe coincidir con el tipo de documento
  • Pruebe e itere para encontrar la configuración óptima

Próximos pasos​

Continúe con Capítulo 3: Tipos de Archivos para aprender a manejar documentos complejos.