Pregúntale a un LLM genérico por la política de devoluciones de tu empresa o por los tickets de soporte de la semana pasada, y dirá que no lo sabe o inventará algo plausible. Su conocimiento termina en lo que había en sus datos de entrenamiento. Nunca ha visto tus documentos y no puede ir a consultarlos a mitad de la conversación. La retrieval-augmented generation (RAG) soluciona justo esto: antes de que el modelo responda, un paso separado busca el texto relevante en tus propios datos y se lo entrega como parte del prompt.
Cómo funciona realmente la retrieval-augmented generation
Una llamada normal a un LLM es un solo paso: entra el prompt, sale la respuesta, usando solo lo que el modelo aprendió durante el entrenamiento. RAG divide esto en dos: primero recupera, luego genera.
- Tus documentos se dividen en chunks, se convierten en vectores (embeddings) y se guardan de antemano en una base de datos vectorial.
- En el momento de la consulta, la pregunta del usuario se convierte en embedding de la misma forma y se compara con los chunks guardados para encontrar las coincidencias más cercanas.
- Las mejores coincidencias se insertan en el prompt como contexto, y solo entonces el LLM genera una respuesta.
Piénsalo como un examen a libro abierto frente a uno a libro cerrado. Un LLM a libro cerrado responde solo de memoria, y por eso mismo inventa cosas cuando la pregunta se sale de lo que memorizó. RAG le pone delante la página correcta, así que responde a partir de algo que tiene enfrente en vez de algo que está reconstruyendo.
El modelo en sí nunca cambia. Sus pesos siguen siendo exactamente los mismos que antes de añadir RAG; el retrieval es la única pieza nueva. Eso es también lo que hace que RAG sea barato de mantener actualizado: editas un documento y la siguiente consulta ve el cambio.
Construir una pipeline RAG: chunking y embedding de tus datos
La ingesta es la mitad de la pipeline que se ejecuta antes de que nadie haga una pregunta. Convierte tus documentos en algo que una búsqueda por similitud pueda consultar.
Los documentos son demasiado largos para convertirlos en embedding como una sola unidad. Un PDF de 20 páginas convertido en un único vector termina pareciéndose un poco a todas las consultas, pero bien a ninguna en concreto, así que primero lo divides en chunks. Un divisor de tamaño fijo con solapamiento es suficiente para empezar:
| |
500 caracteres con 50 de solapamiento es un punto de partida razonable. Las pipelines en producción suelen dividir por número de tokens en lugar de caracteres, usando un divisor que conoce el tokenizador para que el corte de un chunk no caiga a mitad de una palabra. El solapamiento existe para que una frase dividida entre dos chunks aparezca completa al menos en uno de ellos.
Cada chunk se convierte luego en embedding y se guarda. Chroma es una buena primera base de datos vectorial porque corre en el mismo proceso sin necesitar un servidor, y su función de embedding por defecto (all-MiniLM-L6-v2) no necesita una API key:
| |
Eso es toda la fase de ingesta: chunk, embedding, almacenamiento. Se ejecuta una vez por documento, y otra vez cada vez que un documento cambia.
Si prefieres no instalar una base de datos vectorial en tu máquina, la mayoría (Chroma, Weaviate, Milvus) distribuye una imagen oficial y funciona bien en un contenedor; consulta qué es Docker y para qué sirve si no has usado uno antes.
Cómo funciona el retrieval: embedding de la consulta y ranking de resultados
En el momento de la consulta, el mismo modelo de embedding convierte la pregunta del usuario en un vector, y la base de datos devuelve los chunks guardados que están más cerca, usando la métrica de distancia que tenga configurada — cosine similarity y L2 al cuadrado son las dos que más verás:
| |
La consulta nunca usa la palabra “devolución” (refund) y aun así encuentra la coincidencia. Esa es toda la razón para hacer embedding en lugar de grep: dos textos terminan cerca en el espacio vectorial cuando significan cosas parecidas, no cuando comparten palabras.
El último paso une los chunks recuperados en el prompt:
| |
prompt va a cualquier API de chat completion que ya estés usando. RAG no requiere un modelo o proveedor concreto; es un patrón que decide qué pones en el prompt antes de enviarlo. Si esa llamada a la API necesita una clave, mantenla fuera de tu imagen igual que harías con cualquier otra credencial: como variable de entorno o secret montado, nunca como build arg. Variables de Entorno y Secrets en Docker explica la diferencia.
RAG contra fine-tuning: cuál resuelve tu problema
Ambos buscan que un LLM funcione mejor en tu caso de uso específico, pero cambian cosas distintas.
| RAG | Fine-tuning | |
|---|---|---|
| Qué cambia | Nada en el modelo; añades un paso de retrieval | Los propios pesos del modelo, mediante más entrenamiento |
| Actualizar el conocimiento | Editas o añades un documento, la siguiente consulta lo ve | Reentrenar (u otra pasada de fine-tuning) |
| Bueno para | Hechos, datos actuales, cualquier cosa que cambie a menudo | Tono, formato de salida, patrones de razonamiento específicos de la tarea |
| Coste de actualizar | Bajo — sin entrenamiento | Alto — necesita un entrenamiento y un dataset |
| Falla cuando | El retrieval falla o clasifica mal el chunk | El comportamiento necesario no se reduce a ejemplos de entrada-salida |
Un bot de soporte cuya política de devoluciones cambia cada trimestre es un problema de RAG: haz fine-tuning con la política del trimestre pasado y estará seguro de sí mismo pero equivocado sobre la nueva. Un modelo que tiene que emitir un esquema JSON concreto cada vez, o mantener un estilo de razonamiento particular, se acerca más a un problema de fine-tuning: ninguna cantidad de contexto recuperado cambia cómo un modelo formatea su salida. Muchos sistemas en producción usan ambos: un modelo con fine-tuning para el tono y la estructura, alimentado con hechos por RAG.
Dónde falla la retrieval-augmented generation
Las demos de RAG parecen fáciles porque la demo tiene tres documentos y una respuesta obvia. Los sistemas en producción tienen miles de documentos, y cualquiera de los siguientes puntos puede arruinar una respuesta sin que nadie lo note:
- Chunking que ignora la estructura. Un divisor de tamaño fijo que corta una tabla por la mitad, o separa un título del párrafo que introduce, produce chunks que fuera de contexto son incomprensibles — y lo que es incomprensible se convierte en embedding y se recupera mal.
- Un índice desactualizado. La búsqueda vectorial no tiene ninguna noción del tiempo. Si un documento se actualiza pero nunca se vuelve a convertir en embedding, sigue siendo la versión antigua la que se recupera, y nada en la pipeline avisa de que está mal.
- El chunk correcto existe pero no queda bien posicionado. El retrieval top-k solo devuelve las k coincidencias más cercanas; si la respuesta real está en el chunk número 15 más cercano y recuperas 5, el modelo nunca la ve.
- Demasiado contexto, mal colocado. Meter diez chunks recuperados en el prompt no significa que el modelo los pese a todos por igual. Los modelos rinden notablemente peor usando información que está en medio de un contexto largo que al principio o al final.
- Ninguna evaluación más allá de “la respuesta de la demo parecía correcta”. Sin un conjunto de preguntas y respuestas esperadas, una regresión en el retrieval causada por un cambio de chunking o un error de reindexación pasa desapercibida.
La mayoría de estos son fallos de retrieval, no del modelo, y todos son visibles si inspeccionas los chunks recuperados en lugar de solo la respuesta final.
Cómo construir tu primera pipeline RAG
Construye primero la versión de tres pasos: haz chunking de un puñado de documentos reales, conviértelos en embedding en Chroma o en otro almacén local, y conecta los chunks recuperados a un prompt exactamente como se muestra arriba. Después hazle las 10 preguntas que esperas que hagan los usuarios reales. Comprueba si los chunks recuperados contienen la respuesta antes de juzgar la respuesta generada — un retrieval equivocado hace inevitable una respuesta equivocada, sin importar cómo esté escrito el prompt. Solo una vez que ese ciclo funcione con preguntas reales merece la pena ajustar el tamaño de los chunks, cambiar el modelo de embedding o añadir un reranker.