README · by ansango
← Volver al libro

RAG: arquitectura y optimización

Retrieval-Augmented Generation: cómo dar contexto externo al modelo, las arquitecturas más comunes, los algoritmos de retrieval, cómo optimizar la calidad y los patrones para ir más allá del texto

~6 min de lectura
Resumen

Esta nota cubre la primera mitad del capítulo 6: Retrieval-Augmented Generation (RAG), la técnica más común para dar contexto externo a un foundation model sin reentrenarlo. Vemos la arquitectura general, los algoritmos de retrieval (léxico, semántico, híbrido), las técnicas de optimización (chunking, reranking, query rewriting), y cómo extender RAG más allá del texto (imágenes, tablas, código). La segunda mitad del capítulo (agentes) está en Agentes.

Por qué RAG existe

Los foundation models tienen dos limitaciones que RAG ataca directamente:

  1. Conocimiento desactualizado: entrenado hasta una fecha, no sabe lo que pasó después.
  2. Conocimiento privado: no saben sobre los datos internos de tu empresa.

Hay tres formas de darle ese conocimiento al modelo:

RAG es barato, inmediato y siempre actualizado. Para la mayoría de casos prácticos, es la opción correcta.

“RAG es la cinta transportadora del contexto.”

Cada vez que el usuario pregunta, el sistema recoge los trozos relevantes de una base de conocimiento y los pone en el prompt. El modelo responde con ese contexto fresco.

Arquitectura general

Un sistema RAG tiene tres componentes:

1. Indexing pipeline (offline)

Proceso que prepara la base de conocimiento:

  1. Cargar documentos de sus fuentes (PDFs, web, BD, etc.).
  2. Chunkear: dividir en fragmentos de tamaño manejable.
  3. Embedir: convertir cada chunk en un vector.
  4. Almacenar: guardar los vectores en una base de datos vectorial con sus metadatos.

2. Retrieval pipeline (online)

Proceso que responde a una query:

  1. Recibir la query del usuario.
  2. Embedir la query.
  3. Buscar los chunks más similares en la base.
  4. Devolver los top-k chunks como contexto.

3. Generation pipeline (online)

Proceso que genera la respuesta:

  1. Construir el prompt con instrucciones + contexto + query.
  2. Llamar al modelo.
  3. Post-procesar la respuesta (citaciones, validación, etc.).
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Documentos │ -> │  Indexing   │ -> │  Vector DB  │
└─────────────┘    │  (chunking, │    └─────────────┘
                   │  embedding) │
                   └─────────────┘

                          │ (offline)

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│ Usuario /   │ -> │  Retrieval  │ -> │  Contexto   │
│  query      │    │  (top-k)    │    │  + prompt   │
└─────────────┘    └─────────────┘    └─────────────┘


                                        ┌─────────────┐
                                        │   LLM       │
                                        │  genera     │
                                        │ respuesta   │
                                        └─────────────┘

Chunking

El primer paso del indexing es dividir los documentos en chunks. El chunking es más importante de lo que parece: el tamaño y la estrategia afectan directamente a la calidad del retrieval.

Estrategias de chunking

Fixed-size chunking

Dividir en chunks de N tokens con overlap.

def fixed_chunk(text, chunk_size=500, overlap=50):
    chunks = []
    for i in range(0, len(text), chunk_size - overlap):
        chunks.append(text[i:i + chunk_size])
    return chunks

Sentence/paragraph chunking

Dividir en oraciones o párrafos.

Semantic chunking

Agrupar frases por similitud semántica.

Hierarchical chunking

Indexar a múltiples niveles (documento, sección, párrafo).

Tamaño del chunk

El libro recomienda ajustar empíricamente pero da heurísticas:

Overlap es importante

Sin overlap, una frase importante puede quedar partida entre dos chunks y “escaparse” del retrieval. Un overlap del 10-20% es típico.

Embeddings para retrieval

El corazón de RAG moderno es buscar por similitud semántica en lugar de por coincidencia exacta de palabras.

Embedding models

Modelos populares para embeddings:

Cómo elegir

Algoritmos de retrieval

El libro distingue tres familias principales.

1. Retrieval léxico (BM25)

El clásico de la recuperación de información: cuenta términos, los pondera por rareza, los normaliza por longitud del documento.

2. Dense retrieval (semántico)

Busca por similitud de embeddings.

3. Hybrid retrieval

Combina BM25 con dense retrieval. La práctica ganadora.

# Pseudo-código de hybrid retrieval
def hybrid_search(query, k=10):
    bm25_results = bm25_index.search(query, k=k)
    semantic_results = vector_index.search(query_embedding, k=k)
    
    # Combinar con Reciprocal Rank Fusion
    combined = reciprocal_rank_fusion([
        bm25_results, semantic_results
    ])
    return combined[:k]
Hybrid es casi siempre mejor

El libro insiste en que, hoy por hoy, hybrid retrieval gana a cualquier versión pura en la mayoría de benchmarks. La razón: BM25 captura lo que embeddings no capturan y viceversa.

Bases de datos vectoriales

Donde se almacenan los embeddings. Las principales opciones:

Open-source locales

Gestionadas

Cómo elegir

Query rewriting

La query del usuario rara vez es la mejor query para el retrieval. Técnicas para mejorarla:

Query expansion

Generar variantes de la query antes de buscar.

Query original: "¿Cómo cambio la contraseña?"
Variantes:
- "procedimiento para cambiar contraseña"
- "reset password"
- "cambiar clave de acceso"

Multi-query

Hacer varias búsquedas con queries distintas y combinar los resultados.

HyDE (Hypothetical Document Embeddings)

Generar una respuesta hipotética con el LLM, embedirla y buscar con eso. La intuición: la respuesta hipotética está más cerca de los documentos relevantes que la pregunta.

def hyde_search(query, vector_index, llm):
    # 1. Generar respuesta hipotética
    hypothetical = llm.generate(
        f"Responde a esta pregunta de forma informativa: {query}"
    )
    
    # 2. Embedir la hipotética
    hypo_embedding = embed(hypothetical)
    
    # 3. Buscar con la hipotética
    return vector_index.search(hypo_embedding, k=10)

Step-back prompting

Hacer una pregunta más general antes de la específica:

Pregunta: "¿Cuál es la tasa de cancelación del plan Enterprise en 2024?"
Step-back: "¿Cuáles son las métricas clave de negocio?"

Buscar con la pregunta original Y la step-back.

Reranking

El primer retrieval devuelve muchos candidatos. Un reranker los reordena por relevancia.

Two-stage retrieval

  1. Stage 1: retrieval rápido (BM25 o dense) → top-100.
  2. Stage 2: reranker lento pero preciso → top-10.

Rerankers populares

Reranking mejora mucho la calidad

El libro reporta que añadir un reranker típicamente mejora la calidad del retrieval un 10-20%. Es una de las optimizaciones con mejor ratio coste/mejora.

Técnicas de optimización adicionales

Compresión de contexto

Pasar al modelo solo los fragmentos relevantes de los chunks, no el chunk entero.

def compress_chunk(chunk, query):
    """Extrae solo las partes del chunk relacionadas con la query."""
    sentences = chunk.split(". ")
    relevant = []
    for sentence in sentences:
        if is_relevant(sentence, query):
            relevant.append(sentence)
    return ". ".join(relevant)

Generación de citaciones

Para que el usuario pueda verificar, incluye en el prompt instrucciones de citar fuentes:

Responde a la pregunta usando SOLO la información del contexto.
Cita los documentos entre corchetes: [doc_1], [doc_2].

Validación de la respuesta

Después de generar, verificar que la respuesta está respaldada por el contexto:

def verify_answer(answer, context, llm):
    prompt = f"""
    ¿La siguiente respuesta está respaldada por el contexto?
    
    Contexto: {context}
    Respuesta: {answer}
    
    Responde VERDADERO o FALSO con explicación.
    """
    return llm.generate(prompt)

RAG más allá del texto

El libro cierra la sección con extensiones a otros formatos.

RAG multimodal

RAG sobre código

RAG estructurado

Combinar retrieval semántico con filtros SQL:

# Vector search + metadata filter
results = vector_index.search(
    query_embedding,
    filter={"category": "legal", "year": 2024},
    k=10
)

Esto es extremadamente potente en empresas: puedes hacer “búscame documentos jurídicos de 2024 sobre X”.

Cuándo RAG no es la solución

El libro es claro con las contraindicaciones:

RAG vs fine-tuning

La regla práctica: si tu problema es “el modelo no sabe algo”, RAG. Si tu problema es “el modelo no hace algo (formato, estilo, comportamiento)”, fine-tuning. Para la mayoría de productos, RAG es más barato y efectivo. Fine-tuning entra cuando RAG ha llegado a su techo.

Resumen en tres frases

Próximos pasos