Construir un pipeline RAG con LangChain y bases de datos
Cómo construir un pipeline de Retrieval-Augmented Generation (RAG) usando LangChain y bases de datos vectoriales para búsqueda potenciada por IA
Visión General
Retrieval-Augmented Generation (RAG) combina un modelo de lenguaje grande con un paso de recuperación de documentos. En lugar de depender únicamente de la memoria paramétrica del LLM, RAG obtiene pasajes relevantes de una base de conocimiento y los inyecta en el prompt. Esto reduce dramáticamente las alucinaciones y permite que el modelo responda preguntas sobre datos privados o recientes.
Esta receta construye un pipeline RAG completo: fragmenta documentos, genera embeddings, los almacena en una base de datos vectorial, recupera fragmentos relevantes y genera respuestas con LangChain.
Cuándo Usar
Usa este recurso cuando:
- Quieres que un LLM responda preguntas basadas en tus propios documentos, no solo en sus datos de entrenamiento
- Necesitas respuestas actualizadas que el modelo base no ha visto
- Quieres reducir alucinaciones en aplicaciones de dominio específico
- Estás construyendo una base de conocimiento, bot de soporte o sistema de Q&A de documentos
Solución
Python
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI
# 1. Cargar y fragmentar documentos
loader = TextLoader("docs/knowledge_base.txt")
docs = loader.load()
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
# 2. Generar embeddings y almacenar en BD vectorial
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(chunks, embeddings, persist_directory="./chroma_db")
# 3. Configurar recuperador + cadena LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectorstore.as_retriever(search_kwargs={"k": 4}),
return_source_documents=True
)
# 4. Consultar
result = qa({"query": "What is the refund policy?"})
print(result["result"])
for doc in result["source_documents"]:
print(f"Fuente: {doc.metadata.get('source')}")
JavaScript
const { OpenAIEmbeddings } = require('langchain/embeddings/openai');
const { RecursiveCharacterTextSplitter } = require('langchain/text_splitter');
const { Chroma } = require('@langchain/community/vectorstores/chroma');
const { ChatOpenAI } = require('@langchain/openai');
const { RetrievalQAChain } = require('langchain/chains');
const fs = require('fs');
async function buildRag() {
// 1. Cargar y fragmentar
const text = fs.readFileSync('docs/knowledge_base.txt', 'utf-8');
const splitter = new RecursiveCharacterTextSplitter({ chunkSize: 500, chunkOverlap: 50 });
const chunks = await splitter.createDocuments([text]);
// 2. Embed y almacenar
const embeddings = new OpenAIEmbeddings({ modelName: 'text-embedding-3-small' });
const vectorstore = await Chroma.fromDocuments(chunks, embeddings, {
collectionName: 'knowledge_base',
});
// 3. Consultar
const llm = new ChatOpenAI({ modelName: 'gpt-4o-mini', temperature: 0 });
const qa = RetrievalQAChain.fromLLM(llm, vectorstore.asRetriever({ k: 4 }));
const result = await qa.call({ query: 'What is the refund policy?' });
console.log(result.text);
}
buildRag();
Java
// Java RAG con Spring AI (ejemplo conceptual simplificado)
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
// 1. Cargar y dividir documentos
var reader = new TextReader("docs/knowledge_base.txt");
List<Document> documents = reader.get();
var splitter = new TokenTextSplitter(500, 50);
List<Document> chunks = splitter.split(documents);
// 2. Almacenar embeddings
vectorStore.add(chunks);
// 3. Recuperar fragmentos relevantes
List<Document> relevant = vectorStore.similaritySearch(
"What is the refund policy?", 4
);
// 4. Construir prompt aumentado
String context = relevant.stream().map(Document::getContent).collect(Collectors.joining("\n---\n"));
Prompt prompt = new Prompt(
new SystemMessage("Answer using only the provided context.\nContext:\n" + context),
new UserMessage("What is the refund policy?")
);
String answer = chatClient.call(prompt).getResult().getOutput().getContent();
Explicación
Un pipeline RAG tiene cuatro etapas:
- Ingesta: Los documentos se cargan, limpian y dividen en fragmentos. Para manejo de documentos, consulta validación de archivos. El tamaño de fragmento (típicamente 200–1000 tokens) equilibra granularidad con preservación de contexto.
- Embedding: Cada fragmento se convierte en un vector de alta dimensión usando un modelo de embeddings. Los vectores capturan significado semántico, no solo coincidencia de palabras clave.
- Recuperación: En tiempo de consulta, la pregunta del usuario también se embebe. La base de datos vectorial devuelve los
kfragmentos más similares mediante similitud coseno o distancia euclidiana. - Generación: Los fragmentos recuperados se concatenan en un bloque de contexto y se inyectan en el prompt del LLM. El modelo genera una respuesta fundamentada en el texto proporcionado.
Compromisos:
- Tamaño de fragmento: fragmentos más pequeños = recuperación precisa pero menos contexto; fragmentos más grandes = más contexto pero pueden diluir relevancia
- Valor
k: demasiado bajo pierde contexto importante; demasiado alto desperdicia tokens y puede confundir al modelo - La calidad del modelo de embeddings impacta directamente en la precisión de recuperación
Variantes
| Tecnología | Enfoque | Notas |
|---|---|---|
| LangChain | Orquestación + chaining | Ecosistema maduro, maneja plantillas de prompt y parsing de salida |
| LlamaIndex | Framework centrado en datos | Conectores integrados para PDFs, SQL, APIs; mejor para datos complejos |
| Haystack | Pipeline modular | Fuerte para evaluación y monitoreo en producción |
| Bases vectoriales | Chroma / Pinecone / Weaviate / pgvector | Chroma para local, Pinecone para gestionado, pgvector si ya usas Postgres |
| Embeddings | OpenAI / Cohere / locales (BGE, E5) | OpenAI por calidad; modelos locales para privacidad y costo |
Lo que funciona
- Agrega metadatos (nombre de archivo, número de página) a los fragmentos para trazabilidad y citación
- Usa
chunk_overlap(10–20% del tamaño de fragmento) para evitar dividir en medio de oraciones - Filtra fragmentos recuperados con umbral de relevancia para evitar inyectar texto no relacionado
- Indica al LLM que cite fuentes y se niegue a responder si la respuesta no está en el contexto
- Re-indexa documentos periódicamente o usa detección de cambios para mantener la tienda vectorial actualizada
Errores Comunes
- Fragmentos demasiado grandes — un fragmento de 2000 tokens diluye relevancia y desperdicia contexto del prompt
- Sin overlap — dividir en medio de una oración pierde contexto entre límites de fragmentos
- Ignorar metadatos — los usuarios necesitan saber de dónde proviene una respuesta
- Almacenar HTML/PDF crudo — siempre extrae texto limpio antes de embeber
- Sin evaluación — mide precisión de recuperación y corrección de respuestas con un conjunto de prueba
Lectura Adicional
- Documentación oficial: consulta la referencia actualizada del framework o herramienta utilizada.
- Guías relacionadas: explora las guías de ai y embeddings para profundizar.
- Patrones complementarios: revisa los patrones de diseño aplicables a tu stack tecnológico.
- Postmortems públicos: estudia incidentes reales de equipos que enfrentaron problemas similares en producción.
Notas de Producción
- Despliega gradualmente usando canary o blue-green para detectar regresiones temprano.
- Configura alertas para errores, latencia p99 y tasa de fallos antes de habilitar en producción.
- Documenta el rollback en el runbook; prueba el procedimiento en staging al menos una vez por trimestre.
- Revisa logs estructurados con correlation IDs para trazar requests end-to-end en incidentes.
Puntos Clave
- Aplica construir un pipeline rag con langchain y bases de datos cuando necesites una solución práctica para tu caso de uso.
- Monitorea el rendimiento después de implementar; mide latencia, errores y uso de recursos antes y después.
- Revisa la sección de Troubleshooting ante errores comunes; la mayoría tienen causa raíz documentada con solución.
- Mantén dependencias actualizadas y ejecuta tests en CI para prevenir regresiones en producción.
Errores comunes adicionales
- No deduplicar fragmentos — si el mismo documento se ingiere dos veces, los fragmentos duplicados sesgan los resultados de recuperación y desperdician almacenamiento. Usa un content hash o document ID para detectar duplicados antes de insertar.
- Usar la métrica de distancia equivocada — cosine similarity y Euclidean distance producen rankings diferentes. Matchea la métrica al entrenamiento del modelo de embedding. Los embeddings de OpenAI funcionan mejor con cosine similarity.
- No manejar fragmentos vacíos o muy cortos — fragmentos con menos de 10 tokens producen embeddings ruidosos. Fíltralos durante la ingestión o mergea con fragmentos adyacentes.
- Ignorar versionamiento del modelo de embedding — cuando cambias de modelo de embedding, todos los embeddings existentes se vuelven inválidos. Re-indexa todo el corpus y actualiza la versión del modelo en tu configuración.
- No testear recuperación con queries reales de usuarios — las queries sintéticas de test pueden no reflejar cómo los usuarios realmente formulan preguntas. Recopila queries reales de logs y úsalas como tu conjunto de evaluación.
- Almacenar embeddings sin metadatos — sin metadatos de fuente, página o sección, no puedes filtrar o citar resultados. Siempre adjunta metadatos al momento de ingesta.
- Usar un solo tamaño de fragmento para todos los tipos de documento — documentación de código, contratos legales y artículos de noticias tienen estructuras diferentes. Ajusta el tamaño de fragmento y overlap por tipo de documento para recuperación óptima.
- No implementar comportamiento de fallback — cuando el vector store no retorna fragmentos relevantes (todos below threshold), el LLM debería decir “No sé” en lugar de alucinar una respuesta desde sus datos de entrenamiento.
Buenas Prácticas
- Usa modelos de re-ranking: después de recuperar top-20 fragmentos vía búsqueda vectorial, re-rankea con un modelo cross-encoder como
bge-rerankero Cohere Rerank. Re-ranking mejora precisión scoreando pares query-fragmento directamente en lugar de depender solo en similitud de embedding. - Implementa expansión de queries: transforma la query del usuario en múltiples variaciones antes de la recuperación. Esto captura documentos que usan terminología diferente.
- Añade una capa de guardrails: antes de enviar el contexto recuperado al LLM, filtra fragmentos por PII, contenido tóxico o material off-topic.
- Trackea métricas de recuperación en producción: loguea los top-k fragmentos recuperados para cada query, sus scores de similitud, y si el usuario encontró útil la respuesta.
- Usa parent-child chunking: Esto le da al LLM suficiente contexto circundante sin diluir la precisión de recuperación.
- Implementa indexación incremental: en lugar de re-indexar todo el corpus en cada update, trackea versiones de documentos y solo re-embebe documentos cambiados.
- Setea monitoreo de calidad de recuperación: trackea métricas como recall@k, mean reciprocal rank y relevancia de respuestas a lo largo del tiempo. Alerta cuando la calidad caiga, lo que puede indicar embeddings stale, cambios de modelo o problemas de datos.
Checklist de Producción
- Fragmentos deduplicados por content hash antes de inserción
- Métrica de distancia matchea el modelo de embedding (cosine para OpenAI)
- Fragmentos vacíos o muy cortos filtrados durante ingestión
- Versión del modelo de embedding trackeada en configuración
- Metadatos adjuntos a cada fragmento (fuente, página, sección)
- Tamaño de fragmento y overlap tuneados por tipo de documento
- Comportamiento de fallback cuando ningún fragmento cumple el threshold de relevancia
- Modelo de re-ranking aplicado después de recuperación vectorial inicial
- Pipeline de indexación incremental para actualizaciones de documentos
- Métricas de recuperación (recall@k, MRR) trackeadas a lo largo del tiempo
Consideraciones de Escalado
Al desplegar pipelines RAG a escala, considera estos factores:
- Elección de vector store: Para 1M+ documentos, self-hostea Milvus o Qdrant con búsqueda GPU-accelerated.
- Costo de embedding: indexar 100K documentos con
text-embedding-3-smallcuesta ~$1. Re-indexar en cada cambio de documento es costoso. - Latencia de recuperación: búsqueda vectorial en 1M documentos toma 10-50 ms con un índice optimizado (HNSW). Re-ranking con un cross-encoder agrega 50-100 ms.
- Gestión del context window: el context window del LLM limita cuántos fragmentos puedes alimentarle. GPT-4o soporta 128K tokens, pero alimentar más de 10K tokens de contexto aumenta el costo y puede reducir la calidad de la respuesta. Selecciona top 3-5 fragmentos después del re-ranking.
- Frecuencia de actualización de documentos: para contenido frecuentemente actualizado (noticias, catálogos de productos), setea un pipeline de re-indexación triggered por webhooks. Para contenido estático (documentación, políticas), un batch re-index nocturno es suficiente.
Estimación de Costos
| Componente | Costo | Notas |
|---|---|---|
| Embedding (text-embedding-3-small) | $0.02/1M tokens | OpenAI API |
| Embedding (self-hosted e5-large) | $0 | Solo costo de GPU |
| Vector store (Pinecone starter) | $0 | 100K vectores, 1 namespace |
| Vector store (Pinecone standard) | $70/mes | 5M vectores, namespaces ilimitados |
| LLM generation (GPT-4o) | $5/1M input, $15/1M output | Por query: ~2K input + 500 output |
| LLM generation (GPT-4o-mini) | $0.15/1M input, $0.60/1M output | 30x más barato, bueno para Q&A simple |
Para 10K queries/día con GPT-4o: ~$150/día. Switchea a GPT-4o-mini para queries simples y reduce costos en 90%.
Cuándo No Usar RAG
- Tu base de conocimiento es pequeña (<50 documentos): con pocos documentos, fittalos todos en el context window del LLM directamente. RAG agrega complejidad de infraestructura sin beneficio cuando el corpus entero fittea en un solo prompt.
- Las respuestas requieren quotes exactos, no resúmenes: RAG recupera fragmentos, no documentos completos. Si los usuarios necesitan texto verbatim (cláusulas legales, samples de código), retorna documentos completos con keyword search en su lugar.
- Tus documentos son altamente estructurados (tablas, formularios): los embeddings strugglean con datos tabulares.
- Se requiere data real-time: los índices RAG son eventually consistent. Si necesitas respuestas que reflejen data que cambió hace segundos, queryea la fuente directamente en lugar de depender del índice.
- Necesitas fuentes citadas para cada claim: aunque RAG puede proporcionar source chunks, no garantiza que cada oración en la respuesta sea traceable.
Benchmarks de Rendimiento
| Componente | Latencia | Throughput | Notas |
|---|---|---|---|
| Embedding (text-embedding-3-small) | 50-200ms | 10K tokens/req | OpenAI API |
| Vector search (pgvector, 100K docs) | 5-20ms | 100 QPS | HNSW index |
| Vector search (Pinecone, 1M docs) | 10-50ms | 200 QPS | Servicio managed |
| Re-ranking (cross-encoder) | 50-100ms | 20 docs/batch | bge-reranker-large |
| LLM generation (GPT-4o) | 1-5s | 50 concurrentes | Streaming habilitado |
Pipeline RAG end-to-end: 200ms-6s por query. El step de LLM generation domina la latencia total. Pre-computa embeddings y cachea queries comunes para reducir latencia p99.
Troubleshooting
- Model outputs are inconsistent: set temperature to 0 for deterministic tasks, use seed where supported, and version the prompt.
- Prompt injection leaks context: separate user input from system instructions.
- High token costs: cache embeddings, summarize long context, and choose smaller models for simple tasks.
- Retrieval returns irrelevant chunks: tune chunk size, overlap, and metadata filters. Evaluate retrieval metrics separately from generation.
- Evaluation scores do not match human judgment: Human review is still the ground truth.
Errores Comunes en Producción
- Copiar el ejemplo sin adaptarlo a volúmenes y modos de fallo reales.
- Saltar tests de carga e inyección de errores antes del primer despliegue productivo.
- Codificar valores fijos que deberían ser configurables por entorno.
- Olvidar agregar logging y monitoreo en cada paso.
- Desplegar sin plan de rollback ni estrategia de backup probada.
- Asumir que el ejemplo mínimo escalará sin agregar caché o procesamiento por lotes.
- No documentar la versión y configuración usadas en producción.
- Dejar la receta sin cambios cuando evolucionan las dependencias o la escala.
Preguntas frecuentes
¿Qué tamaño de fragmento debo usar?
Comienza con 500 tokens y 50 tokens de overlap. Evalúa con consultas reales y ajusta según la densidad de tu contenido. La documentación de código a menudo necesita fragmentos más pequeños; los contratos legales pueden necesitar fragmentos más grandes.
¿Puedo usar RAG sin LangChain?
Sí. LangChain simplifica la orquestación, pero puedes construir el pipeline manualmente: dividir texto → embeber con API → almacenar en cualquier base de datos vectorial → recuperar con búsqueda de similitud → formatear prompt → llamar LLM.
¿Cómo manejo actualizaciones de documentos?
Rastrea fragmentos por ID de documento. En actualización, elimina fragmentos antiguos por ID e inserta los nuevos. Para corpus grandes, usa indexación incremental con timestamps o checksums para detectar cambios.
¿Cómo evalúo la calidad del RAG?
Construye un conjunto de prueba de 50-100 preguntas con respuestas correctas conocidas. Mide dos métricas:
- Precisión de recuperación: ¿la búsqueda vectorial retornó los fragmentos correctos? Verifica si el documento fuente aparece en los top-k resultados.
- Corrección de respuestas: ¿el LLM generó una respuesta precisa a partir del contexto recuperado? Usa evaluación humana o LLM-as-judge con una rúbrica.
Trackea estas métricas a lo largo del tiempo a medida que cambias el tamaño de fragmento, modelo de embedding o plantilla de prompt. Usa frameworks como Ragas o TruLens para evaluación automatizada.
¿Qué modelo de embedding debería usar?
Para la mayoría de aplicaciones, text-embedding-3-small (OpenAI) ofrece un buen balance de calidad, costo y velocidad. Para aplicaciones sensibles a privacidad, usa all-MiniLM-L6-v2 (sentence-transformers) localmente. Para contenido multilingüe, considera multilingual-e5-large o embeddings multilingües de Cohere.
¿Cómo manejo documentos multi-formato (PDF, HTML, Markdown)?
Usa los document loaders de LangChain: PyPDFLoader para PDFs, UnstructuredHTMLLoader para HTML, TextLoader para Markdown. Cada loader extrae texto limpio y metadatos. Pasa todos los documentos por el mismo pipeline de chunking y embedding. Almacena el formato fuente en metadatos para filtering.
¿Puedo usar RAG con una base de datos SQL en lugar de un vector store?
Sí. Usa pgvector (extensión de PostgreSQL) para almacenar embeddings junto a tus datos relacionales. Esto te permite combinar búsqueda de similitud vectorial con filtros SQL (ej. "encontrar documentos sobre Python publicados después de 2024-01-01"). Para MySQL, usa MyVector o hospeda una base de datos vectorial separada.
Recursos Relacionados
Crea búsqueda semántica con embeddings en Python, JS y Java
Crea un motor de búsqueda semántica con embeddings de texto y similitud vectorial. Incluye ejemplos en Python, JavaScript y Java con FAISS, pgvector y OpenAI.
RecipeChatbot con OpenAI Assistants API: Build, Coste y Deploy
Cómo crear un chatbot de IA usando la OpenAI Assistants API con function calling y file search.
RecipeFine-Tuning de un LLM para Generación de Código
Cómo hacer fine-tuning de un LLM para código usando LoRA y QLoRA.
GuideGuía de Entrevistas de System Design: Conceptos Clave
Una guía práctica para entrevistas de system design: escalabilidad, bases de datos, caching, load balancing, microservicios y cómo estructurar tu respuesta.
GuideGuía de Arquitectura de Software
Una guía para diseñar arquitectura de software: monolitos vs microservicios, arquitectura en capas, flujo de datos y criterios de selección de tecnología.
RecipeConstruir Agentes de IA Autónomos con Uso de
Cómo diseñar agentes de IA que autónomamente planifiquen, ejecuten herramientas e iteren hacia objetivos usando ReAct, function calling y arquitecturas de memoria.