RAG - Dotando de Memoria a tu Agente. Parte 1

Introducción: Rompiendo los límites del conocimiento paramétrico
Los Modelos de Lenguaje Grandes (LLMs) como Gemini son asombrosos en su capacidad para razonar y generar texto similar al humano. Sin embargo, su conocimiento, aunque vasto, es intrínsecamente limitado. Este conocimiento, conocido como conocimiento paramétrico, está "congelado" en los pesos de la red neuronal, fijado en el momento en que finalizó su entrenamiento. Esta limitación fundamental se manifiesta en tres problemas críticos para las aplicaciones empresariales:
- El vacío de conocimiento (Knowledge Gap): El modelo no conoce eventos ocurridos después de su fecha de corte de entrenamiento. No puede opinar sobre las últimas noticias, ni conocer los productos más recientes de una empresa.
- El muro de la privacidad (Privacy Wall): El modelo no tiene acceso a datos privados o propietarios. No conoce la documentación interna de tu empresa, el historial de un cliente específico o el contenido de tu base de datos de productos.
- La plaga de la alucinación (The Plague of Hallucination): Cuando se le pregunta por información que no posee, un LLM, en su afán por ser útil, a menudo "alucina" o inventa respuestas que parecen plausibles pero son factualmente incorrectas. Esto es inaceptable en la mayoría de los casos de uso profesionales.
La solución arquitectónica a estos problemas no es re-entrenar el modelo (un proceso prohibitivamente caro y lento), sino aumentar su capacidad de generación con información recuperada en tiempo real. Este es el paradigma de Retrieval-Augmented Generation (RAG).
RAG transforma al LLM de un "sabelotodo" con memoria finita a un "razonador experto" con acceso a una biblioteca externa y dinámica. El flujo es elegantemente simple y emula cómo un experto humano respondería a una pregunta difícil: primero busca información relevante en fuentes fiables y luego sintetiza una respuesta basada en esa evidencia.
En este capítulo, diseñaremos y construiremos las dos pipelines que componen un sistema RAG robusto:
- La pipeline de ingesta (Indexing): Un proceso, generalmente offline, que transforma nuestra base de conocimiento (documentos, artículos, etc.) en un formato consultable y optimizado para la búsqueda semántica.
- La pipeline de recuperación y generación (Retrieval & Generation): Un proceso en tiempo real que, ante una pregunta del usuario, busca la información más relevante en nuestra base de conocimiento y la proporciona al LLM como contexto para generar una respuesta precisa y fundamentada.
La anatomía de RAG: De documentos a respuestas fundamentadas
Un sistema RAG se compone de dos fases distintas. Comprender la separación y el propósito de cada una es clave para diseñar una solución eficiente y escalable.
Fase 1: La pipeline de ingesta (El proceso offline)
El objetivo de esta fase es preparar nuestro conocimiento para una búsqueda de similitud ultrarrápida. Este proceso se ejecuta cada vez que nuestra base de conocimiento se actualiza.
Diagrama de flujo de la ingesta:
- Carga y extracción de texto: El primer paso es cargar nuestros documentos fuente y extraer su contenido textual. Esto puede ser tan simple como leer un archivo de texto o tan complejo como parsear un PDF, extrayendo texto mientras se preserva cierta estructura.
2. Chunking (División): Este es, quizás, el paso más crítico y que más requiere juicio de ingeniería. No podemos generar un único embedding para un documento de 100 páginas; el vector resultante sería una representación semántica demasiado diluida e inútil. En su lugar, debemos dividir el documento en fragmentos más pequeños y manejables, o "chunks".
- ¿Por qué es crucial? Un buen chunk debe ser lo suficientemente grande para contener un concepto o idea coherente, pero lo suficientemente pequeño para que su embedding sea una representación semántica precisa y distintiva.
- Estrategias de chunking: Existen múltiples estrategias, desde la más simple (división por tamaño fijo de caracteres o tokens) hasta las más sofisticadas (división recursiva por separadores como párrafos y frases, o incluso división semántica basada en el contenido). La elección de la estrategia tiene un impacto directo en la calidad de la recuperación.
3. Generación de embeddings: Una vez que tenemos nuestros chunks de texto, los convertimos en representaciones numéricas. Un embedding es un vector de alta dimensionalidad (cientos o miles de números de punto flotante) que captura el "significado" semántico del texto.
- Modelo de embedding: Utilizamos un modelo de lenguaje especializado para esta tarea, como
text-embedding-004
de Google. Es fundamental usar el mismo modelo tanto en la ingesta como en la recuperación. - El espacio vectorial: El resultado es que cada chunk de texto se convierte en un punto en un espacio vectorial de alta dimensionalidad. En este espacio, chunks con significados similares (ej. "costo de la matrícula universitaria" y "precio de las tasas académicas") estarán geométricamente cerca, mientras que chunks con significados dispares estarán lejos.
4. Almacenamiento en base de datos vectorial: Necesitamos almacenar estos embeddings junto con el texto original de sus chunks de una manera que permita una búsqueda de similitud eficiente. Aquí es donde entran las bases de datos vectoriales.
* No es una BBDD tradicional: Mientras que una BBDD SQL está optimizada para búsquedas exactas con WHERE
, una BBDD vectorial está optimizada para encontrar los "K vecinos más cercanos" (K-Nearest Neighbors, KNN) a un vector de consulta dado.
* Nuestra elección: PostgreSQL con pgvector
: En lugar de introducir una nueva y exótica base de datos, aprovecharemos PostgreSQL, una herramienta robusta y familiar, mediante la extensión pgvector
. Esto nos permite combinar datos relacionales tradicionales con datos vectoriales en la misma base de datos, una gran ventaja arquitectónica.
* Índices vectoriales: Para que la búsqueda KNN sea rápida sobre millones de vectores, pgvector
utiliza índices especializados como HNSW (Hierarchical Navigable Small World). Estos índices organizan los vectores de una manera que permite encontrar los vecinos más cercanos sin tener que comparar el vector de consulta con todos y cada uno de los vectores en la base de datos, transformando una operación O(n) en una operación aproximadamente logarítmica.
Fase 2: La pipeline de recuperación y generación (El proceso en tiempo real)
Esta fase se activa cada vez que un usuario hace una pregunta a nuestro chatbot.
Diagrama de flujo de la recuperación:
- Embedding de la pregunta: Tomamos la pregunta del usuario (ej. "¿Cuánto cuesta estudiar en esa universidad?") y la convertimos en un vector usando exactamente el mismo modelo de embedding que usamos en la pipeline de ingesta.
2. Búsqueda de similitud: Con el vector de la pregunta, consultamos nuestra base de datos vectorial. La consulta no es un SELECT ... WHERE text = '...'
; es una operación que dice: "Devuélveme los 5 chunks cuyos embeddings tengan la mayor similitud de coseno con este vector de consulta".
* Similitud de coseno: Esta es la métrica más común. Mide el coseno del ángulo entre dos vectores. Un valor de 1 significa que los vectores apuntan en la misma dirección (máxima similitud semántica), 0 significa que son ortogonales (sin relación), y -1 significa que son opuestos.
3. Recuperación de chunks: La base de datos nos devuelve los chunks de texto originales correspondientes a los embeddings más similares. Por ejemplo, podría devolver:
* Chunk 1: "Las tasas de matrícula para estudiantes de grado son de 50,000€ por año académico..."
* Chunk 2: "Existen becas y ayudas financieras. El coste de vida en la ciudad se estima en 15,000€ anuales..."
4. Aumentación del prompt (Prompt Augmentation): Este es el corazón de RAG. Construimos un nuevo prompt para el LLM de generación (como Gemini 2.5 Pro) que combina la pregunta original del usuario con la evidencia que hemos recuperado.
Plantilla de prompt aumentado:
System: Eres un asistente experto. Responde a la pregunta del usuario basándote
únicamente en el siguiente contexto. Si la respuesta no se encuentra en el
contexto, di "No tengo suficiente información para responder a esa pregunta".
Contexto:
---
Fragmento 1: "Las tasas de matrícula para estudiantes de grado son de 50,000€ por año académico..."
Fragmento 2: "Existen becas y ayudas financieras. El coste de vida en la ciudad se estima en 15,000€ anuales..."
---
Pregunta del Usuario: ¿Cuánto cuesta estudiar en esa universidad?
Respuesta:
5. Generación de respuesta por LLM: Enviamos este prompt aumentado al LLM. Ahora, el modelo no necesita "recordar" la información; solo necesita sintetizar y razonar basándose en el contexto proporcionado. Su tarea se ha simplificado enormemente, y la probabilidad de que alucine se ha reducido drásticamente. La respuesta generada estará fundamentada en los datos que nosotros le hemos proporcionado.
Conclusión Teórica
Hemos desmitificado RAG, presentándolo no como una técnica de IA mágica, sino como un patrón de arquitectura de software sólido y bien definido. Al separar la preparación del conocimiento (ingesta) de su uso en tiempo real (recuperación), hemos diseñado un sistema que es a la vez potente y mantenible.
Hemos tomado decisiones de ingeniería clave: usar PostgreSQL con pgvector
para aprovechar la infraestructura existente, y entender que la calidad del chunking
y la consistencia de los modelos de embedding son cruciales para el éxito.
Con esta sólida comprensión de la arquitectura y el flujo de datos de un sistema RAG, estamos listos para pasar a la práctica. En la siguiente sección, implementaremos la pipeline de ingesta, preparando nuestra primera base de conocimiento para ser consultada por nuestro agente de IA.