· 12 min de lectura

Embeddings y RAG: cómo conectar un LLM con tus propios datos

¿Te gusto este contenido? Únete a la comunidad de Indie Builders y descubre las mejores formas de crear un producto digital. clic aquí

tl;dr Un LLM no conoce tus documentos y su conocimiento tiene fecha de corte. RAG (Retrieval-Augmented Generation) resuelve eso en dos pasos: convertir texto en vectores que capturan significado (embeddings), y recuperar los fragmentos relevantes para inyectarlos en el prompt antes de generar. Aquí desarmamos toda la tubería: cómo se entrena un embedding, cómo funciona la búsqueda vectorial, cómo montar el pipeline con Claude, y los errores que hacen que el 80% de los RAG en producción funcionen mal.

En los dos artículos anteriores de esta serie recorrimos la cadena desde la neurona hasta el LLM y la maquinaria interna de la IA generativa. En ambos apareció la misma palabra como mitigación de alucinaciones, casi de pasada: RAG. Le debíamos un artículo entero, porque es probablemente la técnica con mejor relación costo/beneficio de todo el ecosistema LLM — y también una de las más fáciles de implementar mal.

Manifiesto de ética de siempre: RAG reduce las alucinaciones porque ancla al modelo en material real, pero no las elimina — el modelo sigue siendo un sistema estocástico que puede malinterpretar o mezclar lo recuperado. Diseña con citas verificables y verifica con fuentes de rigor. Referencias al final, como corresponde.


El problema: tu LLM no conoce tus datos

Un LLM como Claude sale del entrenamiento con dos limitaciones estructurales:

  1. Corte de conocimiento: sus parámetros se congelaron en una fecha. Todo lo posterior — tu última release, la reorganización de tu empresa, el precio actualizado — simplemente no existe para él.
  2. Conocimiento privado: tus contratos, tu base de conocimiento interna, tu historial de tickets nunca estuvieron en el corpus de entrenamiento (y mejor así).

Y hay una tercera limitación, más sutil: aunque el conocimiento esté en los parámetros, está almacenado de forma difusa y estadística. Cuando le pides un dato exacto, el modelo genera lo plausible, no lo verificado — ya vimos por qué en el artículo de IA generativa.

La solución ingenua es pegar todos tus documentos en el prompt. A veces funciona — las ventanas de contexto modernas admiten cientos de miles de tokens — pero no escala: pagas todos esos tokens en cada llamada, la latencia crece, y la recuperación de información dentro de contextos enormes degrada en las posiciones intermedias (el efecto lost in the middle).

La solución de ingeniería es recuperar solo lo relevante y dárselo al modelo justo antes de generar. Para eso necesitamos que una máquina entienda qué significa “relevante”. Y eso nos lleva a los embeddings.

Embeddings: convertir significado en geometría

Un embedding es un vector de números reales — típicamente entre 256 y 3.072 dimensiones — que representa el significado de un texto. La propiedad clave es geométrica:

Textos con significado similar producen vectores cercanos en el espacio.

“¿Cómo reseteo mi contraseña?” y “olvidé mi clave de acceso” no comparten casi ninguna palabra, pero sus embeddings quedan a poca distancia. Eso es lo que la búsqueda por palabras clave nunca pudo hacer bien.

La cercanía se mide normalmente con similitud de coseno: el coseno del ángulo entre dos vectores.

sim(a,b)=abab\text{sim}(a, b) = \frac{a \cdot b}{\|a\| \, \|b\|}

Vale 1 si apuntan en la misma dirección (significado casi idéntico), 0 si son ortogonales (sin relación), y valores intermedios para todo lo demás.

¿De dónde sale esa geometría? Del mismo backpropagation de siempre

Aquí conecta todo con los fundamentos: un modelo de embeddings es una red neuronal (hoy en día, un Transformer tipo encoder) entrenada con descenso de gradiente y backpropagation, igual que cualquier otra. Lo que cambia es la función de pérdida.

La técnica dominante es el aprendizaje contrastivo: se le muestran al modelo pares de textos que significan lo mismo (positivos) y textos sin relación (negativos), y la pérdida lo castiga si los positivos quedan lejos o los negativos quedan cerca. Millones de pares después, el espacio vectorial es un mapa del significado — no porque alguien lo diseñara, sino porque era la única forma de minimizar la pérdida. Sentence-BERT popularizó este enfoque y sigue siendo la referencia conceptual.

En la práctica no entrenas tu propio modelo de embeddings: usas uno ya entrenado vía API o local. Por ejemplo, con Voyage AI (el proveedor que recomienda la documentación de Anthropic):

import voyageai
import numpy as np

vo = voyageai.Client()  # lee VOYAGE_API_KEY del entorno

textos = [
    "¿Cómo reseteo mi contraseña?",
    "Olvidé mi clave de acceso al sistema",
    "El menú del restaurante incluye ceviche",
]

result = vo.embed(textos, model="voyage-3.5", input_type="document")
a, b, c = (np.array(e) for e in result.embeddings)

def cos(u, v):
    return u @ v / (np.linalg.norm(u) * np.linalg.norm(v))

print(cos(a, b))  # ~0.85 — mismo significado, distintas palabras
print(cos(a, c))  # ~0.30 — sin relación

Dos detalles prácticos que importan más de lo que parecen: usa el mismo modelo para indexar y para consultar (los espacios de modelos distintos no son comparables), y revisa si el modelo distingue input_type de consulta vs. documento — los buenos lo hacen, porque una pregunta y su respuesta no se redactan igual.

Búsqueda semántica: troceado e índices vectoriales

Con embeddings, buscar se convierte en un problema geométrico: dame los kk vectores más cercanos a la consulta. Para llegar ahí hay que preparar el corpus, y esa preparación — el pipeline de ingesta — es donde se gana o se pierde la calidad del sistema:

  1. Extracción: convertir PDFs, HTML, Markdown o tickets a texto limpio. Aburrido y decisivo: basura que entra, basura que se recupera.
  2. Chunking (troceado): los documentos se parten en fragmentos de unos 200–1.000 tokens. Un embedding comprime todo el fragmento en un solo vector: si el fragmento es muy largo, el significado se diluye; si es muy corto, pierde el contexto que lo hacía interpretable. Trocear respetando la estructura (secciones, párrafos) casi siempre gana a cortar por número de caracteres.
  3. Indexado: cada chunk se embebe y se guarda en una base de datos vectorial junto a su texto y metadatos (fuente, fecha, permisos).

Comparar la consulta contra millones de vectores por fuerza bruta es O(n)O(n) por búsqueda. Las bases vectoriales (pgvector sobre PostgreSQL, Qdrant, Pinecone, Chroma…) usan índices de vecinos más cercanos aproximados (ANN), típicamente grafos HNSW, que navegan el espacio en tiempo logarítmico a cambio de una pérdida de exactitud minúscula. Si ya tienes PostgreSQL en tu stack, pgvector es el punto de partida con menos fricción: tus vectores viven junto a tus datos relacionales, con los mismos backups y permisos.

RAG paso a paso: el pipeline completo con Claude

Con el corpus indexado, cada pregunta del usuario recorre cuatro pasos:

  1. Embeber la consulta con el mismo modelo del índice.
  2. Recuperar los kk chunks más cercanos (y filtrar por metadatos: permisos, fechas, producto).
  3. Construir el prompt: instrucciones + chunks recuperados + pregunta.
  4. Generar con el LLM, pidiéndole que cite sus fuentes y que se abstenga si el contexto no alcanza.

En código, la parte de generación con Claude:

from anthropic import Anthropic

client = Anthropic()  # lee ANTHROPIC_API_KEY del entorno

def responder(pregunta: str, chunks: list[dict]) -> str:
    contexto = "\n\n".join(
        f'<doc id="{c["id"]}" fuente="{c["fuente"]}">\n{c["texto"]}\n</doc>'
        for c in chunks
    )
    response = client.messages.create(
        model="claude-opus-4-8",
        max_tokens=2048,
        system=(
            "Responde SOLO con base en los documentos provistos. "
            "Cita el id de cada documento que uses, en el formato [doc-id]. "
            "Si los documentos no contienen la respuesta, dilo explícitamente: "
            "no inventes."
        ),
        messages=[{
            "role": "user",
            "content": f"<documentos>\n{contexto}\n</documentos>\n\nPregunta: {pregunta}",
        }],
    )
    return response.content[0].text

Tres decisiones de ese prompt que no son decorativas:

  • Delimitar los documentos con etiquetas y darles un id permite exigir citas verificables — tu interfaz puede enlazar cada cita al documento original, y el usuario puede auditar la respuesta.
  • Autorizar la abstención (“si no está, dilo”) reduce drásticamente las alucinaciones: sin esa salida explícita, el modelo tiende a rellenar los huecos con plausibilidad.
  • El system prompt fija las reglas una sola vez; si tu contexto recuperado comparte prefijo entre llamadas, el prompt caching que vimos en el artículo anterior abarata la factura.

Casos de uso donde este patrón brilla: soporte al cliente sobre tu documentación, búsqueda en bases de conocimiento internas, asistentes legales o médicos que citan normativa, y onboarding sobre bases de código grandes.

RAG vs. contexto largo vs. fine-tuning

RAG no es la respuesta universal; es una de tres herramientas para darle conocimiento a un LLM, y conviene saber cuándo toca cada una:

RAGContexto largoFine-tuning
Qué haceRecupera e inyecta fragmentos relevantesMete todo el material en el promptReentrena parámetros con tus datos
Ideal paraCorpus grandes o cambiantes, respuestas con citasCorpus pequeño (cabe en la ventana), análisis de documentos completosCambiar comportamiento: tono, formato, dominio de vocabulario
Actualizar conocimientoReindexar un documento (segundos)Cambiar el promptReentrenar (horas/días y dataset nuevo)
Costo por consultaBajo (solo lo relevante)Alto (todos los tokens, siempre)Bajo en inferencia, alto de entrada
TrazabilidadAlta: sabes qué se recuperóMediaNula: el conocimiento queda difuso en los pesos

La regla práctica: fine-tuning enseña habilidades, RAG aporta conocimiento. Si necesitas que el modelo sepa cosas actualizadas y citables, RAG. Si necesitas que se comporte distinto, fine-tuning. Y si tu corpus completo cabe cómodamente en el contexto y el volumen de consultas es bajo, la opción más simple — pegarlo todo — es perfectamente legítima.

Los errores que matan a un RAG en producción

La demo de RAG se hace en una tarde; el sistema confiable toma más, y casi siempre por estas causas:

  • Evaluar con vibras. Antes de optimizar nada, arma un conjunto de evaluación: 50–100 preguntas reales con su respuesta esperada y los chunks que deberían recuperarse. Mide la recuperación (¿el chunk correcto está en el top-kk?) por separado de la generación. Sin esto, cada “mejora” es una moneda al aire.
  • Chunks sin contexto. Un fragmento que dice “la versión anterior no soporta esta función” es inútil si el troceado le quitó de qué producto hablaba. La técnica de contextual retrieval de Anthropic ataca exactamente esto: usar un LLM para anteponer a cada chunk una o dos frases que lo sitúan en su documento, antes de embeberlo. En sus mediciones, reduce los fallos de recuperación hasta en un 49%.
  • Confiar solo en lo semántico. Los embeddings son malos con códigos de error exactos, SKUs o nombres propios raros — justo donde la búsqueda léxica clásica (BM25) es excelente. La búsqueda híbrida (vectores + palabras clave, fusionando resultados) supera a cualquiera de las dos por separado en casi todos los benchmarks.
  • Saltarse el reranking. El índice ANN optimiza velocidad sobre millones de candidatos; un reranker (un modelo más pesado que puntúa pares pregunta-fragmento) reordena los 50 mejores y deja los 5 realmente relevantes. Es el ajuste con mejor retorno después de la búsqueda híbrida.
  • Ignorar los permisos. Si indexas documentos con niveles de acceso distintos, el filtrado por usuario tiene que ocurrir en la recuperación, no después de generar. Un RAG que filtra tarde es una fuga de datos con interfaz amable.

Conclusión

La cadena completa, en cuatro ideas:

  1. Un embedding es la salida de una red entrenada — con el mismo backpropagation de siempre, pero con pérdida contrastiva — para que la geometría del espacio refleje el significado.
  2. La búsqueda semántica convierte “encontrar lo relevante” en “encontrar los vectores cercanos”, y los índices ANN lo hacen viable a escala de millones de documentos.
  3. RAG cierra el circuito: recupera lo relevante y se lo da al LLM justo antes de generar, con citas verificables y conocimiento actualizable en segundos.
  4. La calidad no vive en el LLM sino en la tubería: extracción limpia, chunks con contexto, búsqueda híbrida, reranking y un conjunto de evaluación que te diga la verdad.

Es la misma lección de toda la serie: no hay magia. Hay álgebra lineal, una función de pérdida bien elegida y bastante ingeniería de datos. Si quieres ensuciarte las manos, mi recomendación: indexa la documentación de tu propio proyecto con pgvector y un modelo de embeddings, monta el prompt de la sección 4 con Claude, y arma tus primeras 30 preguntas de evaluación antes de tocar ningún parámetro. Ese orden — medir antes de optimizar — es el que separa la demo del sistema.


Referencias

Papers fundacionales

Documentación y material técnico

En este blog


#Inteligencia Artificial, #RAG, #Embeddings, #LLM, #Claude, #Búsqueda Semántica,

Ver en GitHub | Realizar un PR