RAG y su implementación práctica con LangChain, Bun, Ollama y Qdrant
Los Modelos de Lenguaje de Gran Escala (LLMs) modernos son impresionantes, pero tienen una limitación importante: su conocimiento está fijado en sus pesos, lo que dificulta la actualización y extensión de su conocimiento. Retrieval-Augmented Generation (RAG) es un enfoque diseñado para abordar este problema. Introducido por Meta en 2020, conecta un modelo de lenguaje a una base de conocimiento externa (por ejemplo, un conjunto de documentos) para que pueda incorporar información actualizada y específica en sus respuestas. En la práctica, para cada pregunta formulada, el sistema RAG primero extrae contenido relevante de su base documental, luego genera una respuesta combinando este contexto recuperado con las capacidades lingüísticas del LLM.
Nota: El código fuente completo del proyecto de ejemplo mencionado en este artículo está disponible en GitHub.
Esquema del artículo
-
¿Qué es RAG y por qué utilizarlo?
- Principio de funcionamiento
- Ventajas sobre enfoques clásicos
- Casos de uso concretos
-
Arquitectura de un sistema RAG
- Componentes esenciales
- Flujo de datos
- Elección tecnológica
-
Implementación práctica con TypeScript
- Configuración del proyecto con Bun
- Integración de LangChain
- Configuración de Ollama y Qdrant
-
Análisis de código y mejores prácticas
- Indexación de documentos
- Búsqueda semántica
- Generación de respuestas
-
Ventajas del stack tecnológico
- Rendimiento de Bun vs. Node.js
- Simplicidad de LangChain
- Flexibilidad de Ollama
- Escalabilidad de Qdrant
-
Temas avanzados
- Optimizaciones avanzadas
- Evaluación y métricas
- Alternativas tecnológicas
¿Qué es RAG y por qué utilizarlo?
Retrieval-Augmented Generation (RAG) significa literalmente "generación aumentada por recuperación". La idea es separar el conocimiento del modelo. En lugar de intentar incorporar toda la información en los parámetros de un LLM (mediante costosos fine-tuning) o diseñar un modelo clásico que prediga respuestas a partir de datos, permitimos que el modelo principal genere texto y lo aumentamos con una fase intermedia de recuperación de información. Una típica pipeline RAG funciona así:
- Consulta del usuario – El usuario plantea una pregunta o introduce una solicitud en lenguaje natural (por ejemplo, "¿Para qué se utiliza la clase X en este proyecto?").
- Búsqueda de documentos relevantes – El sistema transforma esta pregunta en una representación vectorial (embedding) y luego consulta una base de datos vectorial para recuperar documentos o pasajes semánticamente más similares a la consulta. Esto identifica el contexto relevante (por ejemplo, un extracto de la documentación, código o un artículo que corresponde a la pregunta).
- Combinación contexto + pregunta – Los documentos o extractos recuperados se proporcionan al modelo de lenguaje como contexto. En la práctica, se insertan en el prompt del LLM, típicamente a través de un mensaje de sistema o añadiendo el texto de los documentos encontrados a la pregunta del usuario.
- Generación de respuesta – El modelo de lenguaje (LLM) genera entonces una respuesta basada tanto en la pregunta como en el contexto proporcionado. La respuesta debería contener información de los documentos, formulada de manera coherente gracias a las capacidades del LLM.
Este proceso permite al modelo basarse en conocimientos externos específicos en el momento de la generación, sin tener que memorizarlos permanentemente. Puede compararse con un ser humano que, ante una pregunta, consultaría libros o documentos de referencia antes de responder: el LLM "consulta su biblioteca" antes de hablar.
Casos de uso concretos para RAG
El enfoque RAG es particularmente útil cuando un asistente conversacional debe gestionar una base de conocimiento en evolución o extensa. Aquí hay algunos ejemplos de casos de uso concretos en los que RAG sobresale en comparación con los métodos clásicos:
Chatbots documentales: Un asistente alimentado por la documentación técnica de una empresa, capaz de responder a preguntas de desarrolladores o clientes extrayendo directamente de manuales, bases de conocimiento internas o incluso código fuente. Por ejemplo, el modelo puede estar conectado a especificaciones de API o al código de un proyecto de código abierto para explicar cómo funciona una función o por qué se eligió un diseño determinado.
FAQ dinámicas: En un contexto de servicio al cliente, un chatbot RAG puede responder a preguntas frecuentes (FAQ) basándose en las últimas políticas o datos de producto. Si una política (por ejemplo, condiciones de devolución) cambia, solo es necesario actualizar el documento de referencia y el bot lo tendrá en cuenta inmediatamente, sin necesidad de reentrenamiento. Esto lleva a FAQ siempre actualizadas con la posibilidad de citar la fuente de información para respaldar la respuesta.
Asistentes legales: Un asistente puede ayudar a abogados o juristas encontrando pasajes relevantes en una base de datos de leyes, jurisprudencia o contratos para una determinada pregunta, y luego formulando la respuesta en lenguaje natural. El modelo no necesita conocer de memoria todo el código civil; solo debe buscar los artículos pertinentes. Lo mismo se aplica a un asistente médico que podría consultar bases de datos de publicaciones científicas o protocolos médicos para proporcionar respuestas basadas en los conocimientos clínicos más recientes.
Asistente de programación: Este es el caso de nuestro proyecto de ejemplo – un asistente que conoce el contenido de un repositorio de código y puede responder a preguntas sobre este código (arquitectura, rol de un módulo, posibles errores, etc.). En lugar de entrenar un modelo especializado en programación, utilizamos un LLM generalista aumentado por la búsqueda de archivos de código relevantes en el repositorio.
Arquitectura de un sistema RAG
Componentes esenciales
Un sistema RAG completo típicamente incluye los siguientes componentes:
-
Indexación y almacenamiento
- Procesador de documentos (extracción, limpieza, chunking)
- Generador de embeddings (transformación en vectores)
- Base de datos vectorial (almacenamiento y búsqueda)
-
Pipeline de consulta
- Preprocesador de consultas
- Motor de búsqueda semántica
- Generador de prompts
-
Generación y postprocesamiento
- Interfaz LLM
- Evaluador de respuestas
- Formateador de salida
Flujo de datos
typescript
Elección tecnológica
Para nuestra implementación, hemos elegido un stack moderno y potente:
- Bun: Entorno de ejecución JavaScript ultrarrápido, ideal para aplicaciones de servidor
- TypeScript: Tipado estático para una mejor mantenibilidad
- LangChain: Framework para crear aplicaciones basadas en LLM
- Ollama: Herramienta para ejecutar modelos de lenguaje localmente
- Qdrant: Base de datos vectorial potente y fácil de implementar
Esta combinación ofrece un excelente equilibrio entre rendimiento, comodidad de desarrollo y flexibilidad.
Implementación práctica con TypeScript
Configuración del proyecto con Bun
Comencemos con la inicialización de nuestro proyecto:
bash
Configuración básica
typescript
Indexación de documentos
La indexación es una fase crucial en un sistema RAG. Implica la conversión de documentos en bruto en chunks de tamaño apropiado y luego la generación de embeddings para cada chunk.
typescript
Búsqueda y generación de respuestas
typescript
Interfaz de usuario simple
typescript
Análisis de código y mejores prácticas
Chunking eficiente
La división de documentos en chunks es una fase crítica que influye directamente en la calidad de los resultados. Algunas mejores prácticas:
- Tamaño apropiado: Los chunks deberían ser lo suficientemente grandes para contener contexto, pero no demasiado grandes para seguir siendo relevantes (típicamente entre 500 y 1500 caracteres).
- Solapamiento: El solapamiento entre chunks previene la pérdida de contexto en los límites.
- División semántica: Idealmente, la división debería respetar la estructura semántica de los documentos (párrafos, funciones, etc.).
Optimización de búsqueda
La calidad de la búsqueda semántica es esencial:
- Filtros de metadatos: Utilizar metadatos (tipo de archivo, fecha, autor) para refinar búsquedas.
- Re-ranking: Aplicar un segundo nivel de filtro para mejorar la relevancia.
- Diversidad: Asegurar la diversidad de resultados para cubrir diferentes aspectos de la pregunta.
Prompting avanzado
La construcción de prompts es un arte que influye fuertemente en la calidad de las respuestas:
typescript
Ventajas del stack tecnológico
Rendimiento de Bun vs. Node.js
Bun ofrece ventajas significativas para este tipo de aplicación:
- Inicio rápido: Tiempo de inicio hasta 4 veces más rápido que Node.js
- Ejecución optimizada: Rendimiento de ejecución superior, especialmente para operaciones I/O
- Bundler integrado: Simplificación del flujo de trabajo de desarrollo
Simplicidad de LangChain
LangChain simplifica notablemente el desarrollo de aplicaciones basadas en LLM:
- Abstracción: Interfaz unificada para diferentes modelos y proveedores
- Componentes reutilizables: Chains, Agents y Tools listos para usar
- Patrones establecidos: Implementaciones de referencia para casos de uso comunes
Flexibilidad de Ollama
Ollama permite ejecutar modelos de lenguaje localmente con gran flexibilidad:
- Modelos locales: Sin dependencia de APIs externas
- Privacidad: Los datos permanecen en tu infraestructura
- Personalización: Posibilidad de adaptar los modelos a tus necesidades
Escalabilidad de Qdrant
Qdrant es una base de datos vectorial moderna diseñada para búsqueda semántica:
- Rendimiento: Optimizado para búsquedas de similitud rápidas
- Filtrado: Capacidades de filtrado avanzadas para metadatos
- Despliegue flexible: Utilizable en modo embebido o como servicio
Temas avanzados
Optimizaciones avanzadas
- Búsqueda híbrida: Combinación de búsqueda vectorial y búsqueda por palabras clave
- Chunking jerárquico: Uso de diferentes niveles de granularidad para los chunks
- Caching: Almacenamiento en caché de resultados de búsqueda y respuestas frecuentes
Evaluación y métricas
Para medir la calidad de un sistema RAG:
- Relevancia: ¿Los documentos recuperados son relevantes para la pregunta?
- Fidelidad: ¿La respuesta es fiel a los documentos de origen?
- Utilidad: ¿La respuesta responde eficazmente a la pregunta del usuario?
Alternativas tecnológicas
- Frameworks: Haystack, LlamaIndex como alternativas a LangChain
- Bases de datos vectoriales: Pinecone, Weaviate, Milvus como alternativas a Qdrant
- Modelos: Varios modelos locales (Llama, Mistral) o APIs (OpenAI, Anthropic)
Conclusión
El Retrieval-Augmented Generation representa un avance significativo en la forma en que podemos utilizar los modelos de lenguaje para casos de uso específicos. Al separar el conocimiento del modelo de generación, RAG permite crear asistentes de IA más precisos, actualizados y transparentes.
Nuestra implementación con TypeScript, Bun, LangChain, Ollama y Qdrant demuestra que ahora es posible construir potentes sistemas RAG con tecnologías modernas y accesibles. Este enfoque allana el camino para una nueva generación de asistentes de IA capaces de razonar sobre bases de conocimiento específicas mientras mantienen la fluidez y coherencia de los grandes modelos de lenguaje.
No dudes en explorar el código fuente completo en GitHub y adaptarlo a tus propios casos de uso. RAG es una tecnología en evolución, y hay numerosas oportunidades de innovación en este emocionante campo.