Skip to content

Chunking y embeddings

Una vez los documentos estan limpios, hay que dividirlos en fragmentos (chunks) y convertirlos en vectores (embeddings). El chunking define que contexto recupera el sistema; los embeddings definen que tan bien se encuentra ese contexto ante una pregunta.

Por que hacer chunking

Los LLM tienen limite de contexto y los vector stores funcionan mejor con unidades pequenas y coherentes. Un documento entero de 50 paginas rara vez es el fragmento ideal para una pregunta concreta.

Objetivos del chunking:

  • Fragmentos con significado autocontenido.
  • Tamano compatible con el modelo de embedding y el LLM.
  • Solapamiento que evite cortar ideas a la mitad.

Estrategias de chunking

1. Por tamano fijo (caracteres o tokens)

python
def chunk_by_size(text: str, size: int = 800, overlap: int = 120) -> list[str]:
    chunks: list[str] = []
    start = 0
    while start < len(text):
        end = start + size
        chunks.append(text[start:end].strip())
        start = end - overlap
    return [c for c in chunks if c]

Simple pero puede cortar frases o parrafos a mitad.

2. Por parrafos o secciones

Ideal para Markdown y documentacion tecnica:

python
def chunk_by_paragraphs(text: str, max_chars: int = 1200) -> list[str]:
    blocks = [b.strip() for b in text.split("\n\n") if b.strip()]
    chunks: list[str] = []
    current = ""

    for block in blocks:
        if len(current) + len(block) + 2 <= max_chars:
            current = f"{current}\n\n{block}".strip()
        else:
            if current:
                chunks.append(current)
            current = block

    if current:
        chunks.append(current)

    return chunks

3. Por estructura (titulos)

Respeta secciones ## y ###. Mejor para manuales con jerarquia clara.

4. Semantico (avanzado)

Agrupa oraciones por similitud antes de cortar. Mas costoso pero util en textos largos sin estructura.

Parametros orientativos

Tipo de contenidoTamano chunkOverlap
FAQ / soporte300–500 tokens10–15%
Documentacion tecnica500–1000 tokens15–20%
Legal / contratos800–1500 tokens20%
CodigoPor funcion o bloqueContexto del archivo

Empieza con 800 caracteres y overlap 120; ajusta segun evaluacion.

Metadatos por chunk

Cada chunk debe heredar y ampliar metadatos del documento:

json
{
  "chunk_id": "postgresql-backup#3",
  "document_id": "postgresql-backup",
  "source": "bases-de-datos/postgresql/09-administracion-backup-y-restore.md",
  "section": "Backup con pg_dump",
  "chunk_index": 3,
  "text": "..."
}

Incluir section o heading mejora citas y depuracion.

Que son los embeddings

Un embedding es una representacion numerica del significado de un texto. Textos semanticamente cercanos tienen vectores cercanos en el espacio.

txt
"como hago backup en postgres"  ~  "restaurar base de datos postgresql"
"receta de tarta de manzana"    X  (muy lejos en el espacio vectorial)

Modelos habituales

Modelo / familiaUsoNotas
text-embedding-3-small (OpenAI)General, APIRapido, de pago
nomic-embed-textLocal con OllamaBuen equilibrio
bge-small, e5-baseOpen sourceMuy usados en RAG
Modelos multilinguesEspanol + inglesElegir uno entrenado para tu idioma

Usa el mismo modelo para indexar documentos y para embedir preguntas.

Ejemplo con API compatible OpenAI

python
from openai import OpenAI

client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

def embed_texts(texts: list[str], model: str = "nomic-embed-text") -> list[list[float]]:
    response = client.embeddings.create(model=model, input=texts)
    return [item.embedding for item in response.data]

Pipeline completo

python
def build_chunks(documents: list[dict]) -> list[dict]:
    chunks: list[dict] = []
    for doc in documents:
        for index, text in enumerate(chunk_by_paragraphs(doc["text"])):
            chunks.append({
                **doc,
                "chunk_id": f"{doc['document_id']}#{index}",
                "chunk_index": index,
                "text": text,
            })
    return chunks

def index_chunks(chunks: list[dict]) -> list[dict]:
    vectors = embed_texts([c["text"] for c in chunks])
    for chunk, vector in zip(chunks, vectors):
        chunk["embedding"] = vector
    return chunks

Normalizacion y dimensiones

  • Verifica la dimension del vector (768, 1024, 1536…) y configurala en el indice.
  • Algunos indices requieren vectores normalizados (L2); consulta la documentacion del vector store.
  • No mezcles embeddings de modelos distintos en el mismo indice.

Buenas practicas

  • Evalua varios tamanos de chunk con un conjunto fijo de preguntas.
  • Prefiere chunking por estructura cuando el formato lo permita.
  • Anade el titulo de seccion al texto del chunk (# Backup\n\n...) para mas contexto.
  • Versiona el modelo de embedding (embedding_model=v1).
  • Guarda chunks intermedios en disco o object storage para reindexar sin reparsear.

Errores habituales

  • Chunks enormes que diluyen la relevancia.
  • Chunks minusculos sin contexto (una sola frase suelta).
  • Cambiar de modelo de embedding sin reindexar.
  • No incluir metadatos para filtrar o citar.
  • Embedir texto con HTML o markdown sin limpiar.

Ejercicio guiado

  1. Toma 3 Markdown del repositorio ya limpios.
  2. Implementa chunk_by_paragraphs con max_chars=1000.
  3. Genera embeddings locales o con API.
  4. Compara manualmente si una pregunta de prueba recuperaria el chunk correcto.
  5. Prueba reducir o aumentar el tamano y anota diferencias.

Siguiente paso

En el capitulo 4 veras como almacenar esos vectores y recuperar los fragmentos mas relevantes para cada pregunta.