
Retrieval-Augmented Generation (RAG): guía técnica 2026
Las alucinaciones de los LLMs no son un bug que los próximos modelos vayan a corregir por completo: son una consecuencia estructural de cómo funcionan los transformers. Un modelo de lenguaje genera tokens prediciendo la continuación más probable dado un contexto; cuando ese contexto no contiene la información concreta que se le pide, el modelo «rellena» con lo estadísticamente plausible. El resultado es una respuesta fluida y convincente que puede ser factualmente incorrecta. Retrieval-Augmented Generation (RAG) es la arquitectura que resuelve este problema de raíz: en lugar de depender solo de los parámetros del modelo, recupera fragmentos de conocimiento actualizado y verificable antes de generar la respuesta.
Esta guía cubre la arquitectura RAG completa — desde la ingesta de documentos hasta la generación final, con código Python ejecutable, comparativas cuantitativas, técnicas avanzadas como reranking y hybrid search, y los patrones de agentic RAG que están redefiniendo el estado del arte en 2026. Si ya estás construyendo agentes de IA para empresas o evaluando si RAG o fine-tuning resuelve mejor tu caso de uso, aquí encontrarás la respuesta técnica completa. El post es parte del cluster AI Engineering de Baigency y asume conocimiento previo de Python y familiaridad básica con LLMs.
Lo que no cubre este post: configuración de infraestructura cloud, deploys en producción con Docker/Kubernetes, ni casos de uso de RAG multimodal sobre imágenes o audio — esos temas tienen su propio espacio. Lo que sí cubre: todo lo que un AI engineer necesita para diseñar, implementar, evaluar y llevar a producción un sistema RAG robusto en 2026.
Tabla de contenidos
- ¿Qué es RAG y por qué resuelve el problema de las alucinaciones?
- RAG vs fine-tuning vs prompt-only: cuándo usar cada uno
- Arquitectura RAG: 5 etapas (load → split → embed → store → retrieve)
- Fase 1 — Ingesta: loaders y chunking strategies
- Fase 2 — Embeddings: elegir el modelo correcto
- Fase 3 — Vector store: indexado y similarity search
- Fase 4 — Retrieval: top-K, MMR, hybrid search
- Fase 5 — Generación: prompt template y context window
- RAG Avanzado: reranking, query rewriting, multi-query
- Agentic RAG: agentes que deciden cuándo recuperar
- Evaluar tu RAG: métricas (precision@K, recall, faithfulness)
- RAG en producción: caching, latencia, costes
- Preguntas frecuentes sobre Retrieval-Augmented Generation
- Conclusión
¿Qué es RAG y por qué resuelve el problema de las alucinaciones?
Retrieval-Augmented Generation (RAG) es una arquitectura de IA que combina un sistema de recuperación de información (retrieval) con un modelo generativo de lenguaje. El concepto fue formalizado en el paper de Lewis et al. (2020), «Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks», que demostró que añadir un componente de recuperación densa de documentos mejoraba significativamente las métricas en tareas que requerían conocimiento factual externo al modelo.
El problema estructural que RAG resuelve
Los LLMs almacenan conocimiento en sus pesos durante el preentrenamiento. Ese conocimiento tiene dos limitaciones críticas: tiene una fecha de corte y no incluye información privada o específica de tu organización. Cuando un usuario pregunta algo que requiere datos más recientes o documentos internos, el modelo no puede «saber» la respuesta — pero tampoco puede admitir que no sabe, porque su entrenamiento lo optimizó para generar texto coherente. El resultado es una alucinación: una respuesta fluida construida desde correlaciones estadísticas, no desde hechos verificables.
RAG desacopla el conocimiento del comportamiento. El modelo sigue siendo responsable de razonar, sintetizar y generar lenguaje natural; pero el conocimiento factual se delega a un almacén de documentos externo, actualizable sin reentrenar el modelo. Cuando llega una query, el sistema primero recupera los pasajes más relevantes del corpus y los inyecta en el contexto del LLM. El modelo genera su respuesta anclada en esos fragmentos, no en suposiciones estadísticas.
RAG como solución de grounding
El término técnico para esto es grounding: el modelo está «anclado» a evidencia recuperada antes de responder. Una review exhaustiva de Gao et al. (2023) «Retrieval-Augmented Generation for Large Language Models: A Survey» categoriza más de 100 papers que demuestran mejoras en faithfulness (hasta 35-40% de reducción de alucinaciones en benchmarks como TruthfulQA) cuando se usa RAG frente a LLMs sin contexto externo. La clave está en que el modelo puede citar la fuente explícitamente, lo que permite validación programática de las respuestas.
Desde la perspectiva de agentes de IA, RAG no es solo una técnica de QA: es el mecanismo fundamental de memoria a largo plazo más utilizado en sistemas de producción. Un agente con RAG puede acceder a miles de documentos técnicos, conversaciones históricas o bases de conocimiento corporativo sin necesidad de llenar el context window con todo el corpus — que sería computacionalmente prohibitivo y económicamente inviable.
Casos de uso donde RAG es la arquitectura correcta
RAG sobresale en escenarios donde el conocimiento cambia frecuentemente (documentación de producto, normativa, precios), donde el corpus es privado y sensible (expedientes de clientes, contratos, historiales clínicos), y donde la trazabilidad de las fuentes es un requisito regulatorio o de negocio. Si necesitas que el sistema responda «según el contrato de servicio de la empresa X, firmado el 15/03/2025, la cláusula 4.2 establece…», RAG es la única arquitectura que hace eso de forma verificable.
RAG vs fine-tuning vs prompt-only: cuándo usar cada uno
Uno de los errores más frecuentes al diseñar sistemas LLM es usar fine-tuning cuando el problema requiere RAG, o viceversa. Son herramientas con trade-offs distintos y casos de uso complementarios, no alternativos absolutos.
Prompt-only (in-context learning)
El enfoque más simple: incluir la información relevante directamente en el prompt del sistema. Funciona cuando el corpus es pequeño (cabe en el context window), es estático (no cambia entre queries) y es el mismo para todos los usuarios. La ventaja es la ausencia de infraestructura; la desventaja es que escala fatal — los context windows tienen límite y coste, y mezclar recuperación manual con generación es error-prone. Regla de oro: si tu «base de conocimiento» tiene más de 50 páginas o se actualiza con frecuencia mayor que mensual, prompt-only no es viable a largo plazo.
Fine-tuning
El fine-tuning ajusta los pesos del modelo con ejemplos de entrenamiento específicos de tu dominio. Es la técnica correcta para modificar el estilo, el tono o el formato de salida del modelo — por ejemplo, hacer que un LLM responda siempre en el formato JSON de tu API, o que adopte el tono de atención al cliente de una empresa concreta. Lo que el fine-tuning no resuelve bien es la inyección de conocimiento factual actualizable: si entrenas el modelo con datos a fecha X y el corpus cambia, necesitas reentrenar. El coste en tiempo y compute es prohibitivo para updates frecuentes.
RAG: el punto medio que domina producción
RAG es la arquitectura dominante en producción en 2026 precisamente porque resuelve el problema de actualización de conocimiento sin tocar los pesos del modelo. El índice vectorial se puede reindexar en minutos cuando el corpus cambia; añadir un documento nuevo al sistema es una operación de ingesta, no de reentrenamiento. La tabla comparativa resume los trade-offs:
| Criterio | Prompt-only | Fine-tuning | RAG |
|---|---|---|---|
| Actualización de conocimiento | Manual (reescribir prompt) | Reentrenar el modelo | Reindexar documentos (minutos) |
| Corpus privado | Limitado por context window | Requiere anonimización cuidadosa | Ideal — el corpus nunca sale de tu infra |
| Trazabilidad de fuentes | No | No | Sí — citations explícitas |
| Coste inicial | Bajo | Alto (GPU time) | Medio (infra vector store) |
| Cambiar de LLM base | Trivial | Reentrenar todo | Solo re-embeder el corpus |
| Latencia | Baja | Baja | Media (retrieval + LLM) |
| Mejor para | Prototipos, corpus pequeño | Estilo/formato/tono fijo | Conocimiento dinámico y privado |
En la práctica, los sistemas de producción más robustos combinan RAG con fine-tuning ligero: el fine-tuning ajusta el estilo y la estructura de respuesta, y RAG provee el conocimiento factual. No son mutuamente excluyentes. El paper de Anthropic sobre Contextual Retrieval (2024) demuestra que añadir contexto al nivel de chunk durante la ingesta reduce la tasa de retrieval fallido en un 67%, lo que hace RAG competitivo incluso en dominios donde antes se usaba fine-tuning por defecto. Para profundizar en cómo los estrategias de chunking afectan la calidad del retrieval, ese post del cluster lo cubre en detalle.
Arquitectura RAG: 5 etapas (load → split → embed → store → retrieve)
Un pipeline RAG canónico se divide en dos fases temporalmente distintas: la fase offline (ingesta e indexado) que se ejecuta una vez o en batch cuando el corpus cambia, y la fase online (retrieval y generación) que se ejecuta en tiempo real por cada query del usuario.
Visión de conjunto del pipeline
Las cinco etapas son: (1) Load — cargar los documentos crudos desde su fuente (PDF, HTML, base de datos, API); (2) Split — fragmentar los documentos en chunks de tamaño manejable; (3) Embed — transformar cada chunk en un vector numérico de alta dimensión que captura su significado semántico; (4) Store — indexar esos vectores en una base de datos vectorial optimizada para similarity search; (5) Retrieve + Generate — en runtime, embeder la query del usuario, recuperar los chunks más similares, y pasarlos como contexto al LLM para generar la respuesta final.
Por qué cada etapa importa de forma independiente
Un error frecuente es tratar el pipeline RAG como una caja negra donde el único parámetro que importa es el modelo LLM. En realidad, cada etapa tiene su propio espacio de decisiones de diseño con impacto medible en la calidad final. Un chunking mal calibrado (chunks demasiado pequeños pierden contexto, demasiado grandes introducen ruido irrelevante) puede hundir el recall del retrieval independientemente de lo bueno que sea el modelo de embeddings. Una elección incorrecta de modelo de embedding (por ejemplo, un modelo genérico para texto legal muy específico) puede hacer que queries semánticamente correctas no encuentren los documentos relevantes. La calidad del sistema RAG es la multiplicación de la calidad de cada etapa — si una falla, el resultado es malo incluso si las demás son excelentes.
Fase offline vs online: implicaciones operativas
La separación offline/online tiene implicaciones críticas para la arquitectura del sistema. La fase offline puede ejecutarse en batch de forma asíncrona — ideal para procesado de grandes corpus (miles de documentos). La fase online debe ser lo suficientemente rápida para no degradar la experiencia del usuario: un retrieval que tarde 3 segundos más la llamada al LLM (2-4 segundos) supera el umbral de tolerancia en la mayoría de aplicaciones. Los sistemas de agentes de IA para empresas en producción suelen apuntar a latencias de retrieval inferiores a 200ms para el P95, lo que requiere tanto hardware adecuado como estrategias de caching que cubriremos en la sección de producción.
Fase 1 — Ingesta: loaders y chunking strategies
La ingesta es la etapa que más se subestima y la que más impacta la calidad final. Garbage in, garbage out aplica con especial severidad en RAG: un pipeline de ingesta que pierde estructura (títulos, tablas, listas), mezcla idiomas o incluye artefactos de OCR degradado contamina el corpus con ruido que el retrieval recuperará junto con la señal útil.
Document loaders: de la fuente al texto limpio
LangChain tiene más de 100 document loaders para fuentes distintas. Los más usados en contextos empresariales son PyPDFLoader (PDFs, con pérdida parcial de tablas), UnstructuredFileLoader (el más robusto para PDFs complejos, usa estrategia de partición semántica), WebBaseLoader (scraping HTML con limpieza), CSVLoader y NotionDBLoader para bases de conocimiento estructuradas. Para PDFs con tablas críticas se recomienda un paso previo de extracción estructurada con pdfplumber o camelot antes de pasar el texto al splitter.
Chunking: la decisión de diseño más crítica
El objetivo del chunking es producir unidades de texto que sean semánticamente coherentes y del tamaño adecuado para ser recuperadas y pasadas al LLM como contexto. Demasiado pequeño (< 100 tokens): el chunk pierde contexto local, el embedding captura detalles pero no el argumento completo. Demasiado grande (> 1.000 tokens): el chunk introduce ruido, el embedding es un promedio difuso, y al recuperarlo metes tokens innecesarios en el context window. El punto óptimo para la mayoría de corpus de texto corrido es entre 300 y 700 tokens con un overlap de 10-20%.
Para más profundidad sobre todas las estrategias — fixed-size, semantic, recursive, agentic y contextual chunking — el post dedicado a estrategias de fragmentación de documentos las cubre con benchmarks comparativos.
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 1. Cargar el corpus (aquí un PDF de ejemplo)
loader = PyPDFLoader("documentos/manual_producto_v3.pdf")
raw_docs = loader.load()
print(f"Páginas cargadas: {len(raw_docs)}")
# 2. RecursiveCharacterTextSplitter: divide por párrafos, luego frases,
# luego palabras hasta que el chunk cabe en el tamaño objetivo.
# Es el splitter de propósito general más robusto.
splitter = RecursiveCharacterTextSplitter(
chunk_size=600, # tokens aprox (1 token ≈ 4 chars en español)
chunk_overlap=80, # solapamiento para no cortar ideas en la frontera
separators=["\n\n", "\n", ". ", " ", ""], # jerarquía de separadores
length_function=len,
)
chunks = splitter.split_documents(raw_docs)
print(f"Chunks generados: {len(chunks)}")
print(f"Ejemplo chunk 0:\n{chunks[0].page_content[:300]}")
print(f"Metadata: {chunks[0].metadata}")
Contextual chunking: la mejora de Anthropic
El paper de Anthropic sobre Contextual Retrieval propone una técnica simple pero efectiva: antes de indexar cada chunk, usar el LLM para generar una frase de contexto que sitúa el chunk dentro del documento completo («Este fragmento explica la política de devoluciones de la empresa para productos de la categoría electrónica, tal como aparece en la sección 3.2 del manual de atención al cliente»). Ese contexto se antepone al chunk antes de embederlo. El resultado es un embedding que captura mejor la posición semántica del fragmento en el documento, mejorando el retrieval especialmente para queries que asumen contexto implícito. La contrapartida es un coste adicional de LLM por chunk durante la ingesta — razonable para corpus estáticos, costoso para corpus con millones de documentos.
Fase 2 — Embeddings: elegir el modelo correcto
Los embeddings son representaciones numéricas de texto en un espacio vectorial de alta dimensión donde la distancia geométrica entre vectores correlaciona con la similitud semántica. «El perro ladra» y «el can vocaliza» deberían estar cerca en ese espacio; «el perro ladra» y «la bolsa sube» deberían estar lejos. La calidad del modelo de embedding determina directamente la calidad del retrieval — un mal modelo de embeddings hace que queries correctas no encuentren los documentos relevantes.
Modelos de embedding: opciones y trade-offs en 2026
Para texto en castellano o multilingüe, los modelos más usados en producción son: text-embedding-3-large de OpenAI (3072 dimensiones, mejor performance en MTEB, $0.13/1M tokens), text-embedding-3-small (1536 dimensiones, 5x más barato, buena opción para corpus grandes), multilingual-e5-large de Microsoft (gratuito, open-source, 1024 dim, excelente para multilingüe) y bge-m3 de BAAI (open-source, top en benchmarks MTEB multilingüe, soporta queries de hasta 8192 tokens). Para aplicaciones que no pueden enviar datos a APIs externas, los modelos open-source hospedados localmente (via Hugging Face Inference Endpoints u Ollama) son la alternativa. El post sobre embeddings semánticos para LLMs tiene la comparativa MTEB completa.
Dimensionalidad y costes: la decisión práctica
Más dimensiones no siempre es mejor. Para la mayoría de corpus empresariales (< 1M documentos), la diferencia de calidad entre 1536 y 3072 dimensiones es marginal, pero el coste de almacenamiento y el tiempo de similarity search se duplican. El principio de diseño correcto: empezar con el modelo más pequeño que supere el umbral de calidad en tu benchmark de retrieval específico. Para el benchmark, lo más pragmático es construir un golden dataset de 100-200 pares (query, documento_relevante) representativos de los casos de uso reales y medir recall@5 y recall@10.
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma
import chromadb
# Modelo de embedding: text-embedding-3-small para balance coste/calidad
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
# dimensions=512 # opcional: reducir dimensiones con Matryoshka para ahorrar
)
# Crear el vector store Chroma persistente
# Chroma almacena vectores, documentos y metadatos en disco
vectorstore = Chroma.from_documents(
documents=chunks, # los chunks generados en la fase anterior
embedding=embeddings,
collection_name="manual_producto",
persist_directory="./chroma_db", # persistencia en disco
collection_metadata={"hnsw:space": "cosine"}, # distancia coseno
)
print(f"Vectores indexados: {vectorstore._collection.count()}")
Fase 3 — Vector store: indexado y similarity search
Una base de datos vectorial es un sistema de almacenamiento diseñado para indexar y buscar vectores de alta dimensión de forma eficiente. A diferencia de las bases de datos relacionales (que buscan por igualdad exacta o rango en atributos discretos), las vector stores buscan por similitud aproximada (ANN — Approximate Nearest Neighbors) en un espacio continuo de alta dimensión.
Opciones de vector store: de desarrollo a producción
El ecosistema en 2026 tiene opciones para todos los contextos. Chroma es el estándar de facto para desarrollo y prototipos: open-source, embebible en proceso Python, sin infraestructura adicional. Pinecone es la opción managed más popular para producción — gestión de índices, escalabilidad automática, pero coste proporcional al volumen. Weaviate y Qdrant son las alternativas open-source para producción autohospedada con todas las features necesarias (filtrado por metadatos, hybrid search, tenancy). pgvector permite añadir búsqueda vectorial a PostgreSQL existente — ideal si ya tienes infra Postgres y el volumen no supera los 10M vectores. La comparativa exhaustiva de trade-offs (latencia, throughput, coste, features) está en el post de bases de datos vectoriales para RAG.
HNSW: el índice que impulsa el retrieval rápido
El algoritmo de indexado dominante en vector stores es HNSW (Hierarchical Navigable Small World). Construye un grafo jerárquico de nodos donde los niveles superiores conectan nodos a «largo alcance» (para navegación rápida) y los niveles inferiores conectan nodos cercanos (para precisión fina). El resultado es una búsqueda ANN con complejidad O(log N) en lugar de O(N) del brute-force, con pérdida de recall controlable (típicamente 95-99% de recall vs exact search). Los parámetros clave son M (conexiones por nodo, trade-off velocidad/memoria) y ef_construction (calidad del índice, afecta al tiempo de build). Para la mayoría de casos de uso, los valores por defecto de Chroma o Qdrant son adecuados hasta ~1M vectores.
Fase 4 — Retrieval: top-K, MMR, hybrid search
El retrieval es la etapa online más crítica: dado el embedding de la query del usuario, encontrar los K chunks más relevantes del índice en el menor tiempo posible. La estrategia de retrieval tiene un impacto directo en la faithfulness de la respuesta final — si el retrieval falla (no devuelve los documentos realmente relevantes), el LLM genera una respuesta incorrecta aunque sea el mejor modelo disponible.
Top-K similarity search
La estrategia más simple: recuperar los K documentos cuyo embedding tiene mayor similitud coseno con el embedding de la query. K=5 es el valor por defecto habitual; aumentarlo a K=10 mejora el recall pero introduce más ruido en el contexto del LLM. La similitud coseno mide el ángulo entre vectores (no la distancia euclidiana), lo que la hace robusta a diferencias de magnitud entre embeddings generados por distintas fuentes.
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
# Cargar el vector store existente
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(
collection_name="manual_producto",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
# Retrieval básico: top-5 por similitud coseno
query = "¿Cuál es el plazo de garantía para los productos de la categoría herramientas?"
# similarity_search devuelve Document objects con page_content y metadata
docs = vectorstore.similarity_search(query, k=5)
for i, doc in enumerate(docs):
print(f"\n--- Chunk {i+1} (fuente: {doc.metadata.get('source', 'N/A')}) ---")
print(doc.page_content[:200])
# similarity_search_with_score devuelve también la distancia coseno
docs_with_scores = vectorstore.similarity_search_with_score(query, k=5)
for doc, score in docs_with_scores:
print(f"Score: {score:.4f} | {doc.page_content[:100]}")
MMR: diversidad en los resultados
Maximal Marginal Relevance (MMR) es un algoritmo que balancea relevancia y diversidad en los resultados del retrieval. En lugar de devolver los K chunks más similares a la query (que pueden ser K versiones casi idénticas del mismo párrafo si el documento tiene mucha redundancia), MMR selecciona iterativamente el próximo chunk que maximiza λ · sim(query, chunk) - (1-λ) · max_sim(chunk, selected_chunks). Con λ=0.5 se obtiene un balance equitativo. MMR es especialmente útil cuando el corpus tiene alta redundancia (FAQs, wikis con contenido solapado) y quieres que el contexto del LLM cubra aspectos distintos del tema.
Hybrid search: combinando sparse y dense retrieval
El hybrid search combina retrieval denso (embeddings semánticos, captura similitud conceptual) con retrieval disperso (BM25, captura coincidencia léxica exacta). Cada técnica cubre las debilidades de la otra: BM25 es superior cuando la query contiene términos técnicos muy específicos, acrónimos o números de modelo que los embeddings diluyen en el espacio semántico; el retrieval denso supera a BM25 cuando la query usa sinónimos o paráfrasis. La combinación con EnsembleRetriever de LangChain usando Reciprocal Rank Fusion (RRF) para normalizar y combinar las listas de resultados es el estado del arte en producción.
from langchain.retrievers import BM25Retriever, EnsembleRetriever
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
# Dense retriever (semántico)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(
collection_name="manual_producto",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
dense_retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 10}
)
# Sparse retriever BM25 (necesita los textos crudos de los chunks)
# chunks: lista de Document objects de la fase de ingesta
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 10
# EnsembleRetriever: combina ambos con Reciprocal Rank Fusion
# weights=[0.4, 0.6] → más peso al semántico; ajustar según benchmark
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, dense_retriever],
weights=[0.4, 0.6]
)
query = "temperatura máxima operación motor modelo XR-2200"
results = ensemble_retriever.invoke(query)
print(f"Chunks recuperados: {len(results)}")
for r in results[:3]:
print(f"\n{r.page_content[:200]}")
Fase 5 — Generación: prompt template y context window
La fase de generación recibe los chunks recuperados y la query del usuario, los combina en un prompt estructurado y llama al LLM para producir la respuesta final. Es la etapa más visible para el usuario final pero, paradójicamente, la menos impactante en la calidad del sistema — si el retrieval es bueno, cualquier LLM competente generará una respuesta aceptable; si el retrieval falla, ni el mejor LLM puede arreglarlo.
Prompt template: estructura y control
El prompt de generación en un RAG típico tiene cuatro componentes: instrucción del sistema (define el rol y las reglas de comportamiento), contexto recuperado (los chunks formateados), query del usuario, y opcionalmente historial de conversación para RAG conversacional. La instrucción más importante es definir el comportamiento cuando el retrieval no encuentra respuesta relevante: «Si el contexto proporcionado no contiene información suficiente para responder la pregunta, indica explícitamente que no dispones de esa información. No inventes datos.» Esta instrucción es el primer filtro anti-alucinaciones del sistema.
El siguiente bloque muestra un pipeline RAG end-to-end con LangChain LCEL (LangChain Expression Language), el patrón declarativo para componer cadenas:
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# 1. Retriever
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(
collection_name="manual_producto",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# 2. Prompt template
SYSTEM_PROMPT = """Eres un asistente técnico experto. Responde exclusivamente
basándote en el contexto proporcionado. Si la información no está en el contexto,
responde: "No dispongo de esa información en la base de conocimiento actual."
Contexto:
{context}
"""
prompt = ChatPromptTemplate.from_messages([
("system", SYSTEM_PROMPT),
("human", "{question}"),
])
# 3. LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 4. Helper para formatear los docs recuperados
def format_docs(docs):
return "\n\n---\n\n".join(
f"[Fuente: {d.metadata.get('source', 'N/A')} | Página: {d.metadata.get('page', '?')}]\n{d.page_content}"
for d in docs
)
# 5. Chain LCEL: retriever → format → prompt → LLM → parse
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 6. Invocar
query = "¿Cuáles son las condiciones de garantía para defectos de fabricación?"
response = rag_chain.invoke(query)
print(response)
Context window management
El context window disponible para el contexto recuperado es total_context_window - system_prompt_tokens - output_tokens_reserved. Con GPT-4o (128K tokens), esto da margen amplio; con modelos de 8K o 16K, la gestión es crítica. El patrón correcto para K grande es implementar un paso de compresión del contexto: antes de pasarlo al LLM, usar un modelo más barato para filtrar y comprimir los chunks recuperados a los fragmentos más relevantes para la query específica. LangChain tiene ContextualCompressionRetriever para este patrón.
RAG Avanzado: reranking, query rewriting, multi-query
El RAG básico (embed → top-K → generate) es suficiente para prototipos, pero en producción con corpus grandes y queries complejas, la calidad del retrieval con top-K simple tiene un techo. El RAG avanzado añade capas adicionales de procesamiento para mejorar la precisión del contexto que llega al LLM. El survey de Gao et al. clasifica estas técnicas en tres categorías: pre-retrieval (mejorar la query), retrieval (mejorar el algoritmo de búsqueda) y post-retrieval (filtrar y reordenar los resultados).
Reranking con cross-encoders
El retrieval inicial con bi-encoders (cada texto se embedde independientemente y se comparan los vectores) es rápido pero sacrifica precisión: el modelo de embedding no «ve» la query y el chunk al mismo tiempo, por lo que pierde señal de interacción fina. El cross-encoder recibe la query y el chunk concatenados y produce una puntuación de relevancia procesando la interacción completa entre ambos. Es órdenes de magnitud más preciso pero más lento — por eso se usa como segundo paso post-retrieval: primero recuperas K=20 candidatos con el bi-encoder, luego el cross-encoder los reordena y te quedas con los top-5 para el LLM.
from sentence_transformers import CrossEncoder
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
# Cross-encoder para reranking: bge-reranker-v2-m3 (multilingüe, open-source)
# Alternativas: ms-marco-MiniLM-L-6-v2 (EN), bge-reranker-large
reranker = CrossEncoder(
"BAAI/bge-reranker-v2-m3",
max_length=512,
device="cpu", # o "cuda" si tienes GPU
)
# 1. Retrieval inicial amplio (K=20 candidatos)
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(
collection_name="manual_producto",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 20})
query = "procedimiento de reclamación en garantía por defecto de fabricación"
candidates = retriever.invoke(query)
# 2. Reranking: el cross-encoder puntúa cada par (query, chunk)
pairs = [(query, doc.page_content) for doc in candidates]
scores = reranker.predict(pairs)
# 3. Ordenar por score y quedarse con top-5
reranked = sorted(
zip(scores, candidates),
key=lambda x: x[0],
reverse=True
)[:5]
print("Top-5 tras reranking:")
for score, doc in reranked:
print(f"\nScore: {score:.4f}")
print(doc.page_content[:200])
Query rewriting y multi-query
Las queries de los usuarios raramente están formuladas de la manera óptima para el retrieval. Query rewriting usa el LLM para reformular la query antes de buscar: expandirla con sinónimos, desambiguarla, o dividirla en sub-queries si es compuesta. El patrón multi-query retrieval genera N versiones alternativas de la query (normalmente 3-5), ejecuta el retrieval para cada una, y combina los resultados con deduplicación. Esto aumenta significativamente el recall — queries que no encuentran el documento relevante en su formulación original pueden encontrarlo en una de las variantes. LangChain tiene MultiQueryRetriever que automatiza este proceso con una sola llamada al LLM para generar las variantes.
HyDE: Hypothetical Document Embeddings
HyDE (Hypothetical Document Embeddings) invierte la lógica del retrieval: en lugar de embeder la query y buscar documentos similares, primero usa el LLM para generar un documento hipotético que respondería a la query (aunque sea incorrecto factualmente), luego embedea ese documento hipotético y busca documentos reales similares a él. La intuición es que un párrafo de texto tiene una distribución de embedding más parecida a otros párrafos de texto que a una pregunta corta. HyDE puede mejorar el recall en 5-15% en corpus técnicos densos donde la asimetría query/documento es grande. La contrapartida es una llamada LLM adicional antes del retrieval, aumentando la latencia y el coste.
Todas estas técnicas tienen sus propios trade-offs que se deben evaluar en el contexto específico. El impacto en métricas como faithfulness y answer relevance se mide con frameworks como RAGAS — tema que cubrimos en la sección de evaluación. El post de context engineering tiene las técnicas de gestión de contexto que complementan estas estrategias de retrieval.
Agentic RAG: agentes que deciden cuándo recuperar
El Agentic RAG es la evolución natural de RAG cuando el sistema necesita resolver queries complejas que requieren múltiples recuperaciones, razonamiento iterativo, o decisiones sobre si es necesario recuperar información en absoluto. En lugar de un pipeline lineal fijo (siempre recuperar K chunks → siempre generar), el agente decide dinámicamente la estrategia de retrieval para cada paso de razonamiento.
Por qué RAG lineal falla en queries complejas
Considera una query como «Compara las condiciones de garantía de nuestros modelos de la serie A con los de la serie B, e indica cuál ofrece mejor cobertura para defectos eléctricos». Un RAG lineal recupera K chunks en una sola búsqueda — puede mezclar información de ambas series o no encontrar ambas en los primeros K resultados. Un agente puede descomponer la query en sub-queries secuenciales, recuperar primero la información de la serie A, luego la de la serie B, y finalmente sintetizar la comparación con contexto completo de ambas fuentes.
Patrones de Agentic RAG
Los tres patrones principales son: Iterative RAG (el agente recupera, genera una respuesta parcial, evalúa si tiene suficiente información, y decide si recuperar más antes de dar la respuesta final); Adaptive RAG (el agente decide en cada query si es necesario el retrieval o si puede responder con el conocimiento interno del LLM, basándose en un clasificador de complejidad); y Corrective RAG (CRAG — el agente evalúa la calidad de los documentos recuperados y, si ninguno supera un umbral de relevancia, lanza una búsqueda web como fallback). Para una cobertura completa de estos patrones y su relación con la arquitectura de IA agéntica, ese post del cluster es la referencia.
Implementación con LangGraph
LangGraph es el framework idóneo para Agentic RAG porque modela el flujo como un grafo con estado: cada nodo es una función (retrieval, grade_documents, generate, rewrite_query) y las aristas son condiciones lógicas que determinan el siguiente paso según el estado actual. El siguiente ejemplo implementa un agente RAG con nodo de decisión: si los documentos recuperados son relevantes, generar; si no, reescribir la query y volver a intentar.
from typing import TypedDict, List, Literal
from langchain_core.documents import Document
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langgraph.graph import StateGraph, END
# --- Estado del agente ---
class RAGState(TypedDict):
question: str
documents: List[Document]
generation: str
attempts: int
# --- Nodos ---
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(
collection_name="manual_producto",
embedding_function=embeddings,
persist_directory="./chroma_db",
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
def retrieve(state: RAGState) -> RAGState:
"""Recuperar documentos relevantes."""
docs = retriever.invoke(state["question"])
return {**state, "documents": docs, "attempts": state.get("attempts", 0) + 1}
def grade_documents(state: RAGState) -> Literal["generate", "rewrite"]:
"""Decidir si los docs son relevantes o hay que reescribir la query."""
grade_prompt = ChatPromptTemplate.from_messages([
("system", "¿Son estos documentos relevantes para responder la pregunta? Responde solo 'yes' o 'no'."),
("human", "Pregunta: {question}\n\nDocumentos:\n{docs}"),
])
grader = grade_prompt | llm | StrOutputParser()
docs_text = "\n---\n".join(d.page_content[:300] for d in state["documents"])
result = grader.invoke({"question": state["question"], "docs": docs_text})
if "yes" in result.lower() or state.get("attempts", 0) >= 2:
return "generate"
return "rewrite"
def generate(state: RAGState) -> RAGState:
"""Generar respuesta con el contexto recuperado."""
context = "\n\n".join(d.page_content for d in state["documents"])
gen_prompt = ChatPromptTemplate.from_messages([
("system", "Responde basándote en el contexto:\n{context}"),
("human", "{question}"),
])
chain = gen_prompt | llm | StrOutputParser()
generation = chain.invoke({"context": context, "question": state["question"]})
return {**state, "generation": generation}
def rewrite_query(state: RAGState) -> RAGState:
"""Reescribir la query si el retrieval no fue suficiente."""
rewrite_prompt = ChatPromptTemplate.from_messages([
("system", "Reformula la siguiente pregunta para mejorar la búsqueda en una base documental técnica."),
("human", "{question}"),
])
chain = rewrite_prompt | llm | StrOutputParser()
new_question = chain.invoke({"question": state["question"]})
return {**state, "question": new_question}
# --- Grafo ---
graph = StateGraph(RAGState)
graph.add_node("retrieve", retrieve)
graph.add_node("generate", generate)
graph.add_node("rewrite", rewrite_query)
graph.set_entry_point("retrieve")
graph.add_conditional_edges("retrieve", grade_documents)
graph.add_edge("generate", END)
graph.add_edge("rewrite", "retrieve") # loop de corrección
app = graph.compile()
# Ejecutar
result = app.invoke({"question": "¿Qué piezas cubre la garantía estándar?"})
print(result["generation"])
Para un tratamiento completo de los patrones de grafos de estado, condiciones y ciclos de retroalimentación en agentes complejos, el post de LangGraph: arquitectura de agentes con estado es la referencia del cluster. Y para explorar la integración de RAG con el framework LangChain más allá de lo cubierto aquí, ese post tiene la cobertura exhaustiva de LCEL, tools, callbacks y memoria.
Evaluar tu RAG: métricas (precision@K, recall, faithfulness)
Un sistema RAG sin evaluación sistemática es un sistema que no sabes si funciona. Las métricas de evaluación de RAG se dividen en dos grupos: métricas de retrieval (¿está recuperando los documentos correctos?) y métricas de generación (¿está generando respuestas correctas y fieles al contexto?).
Métricas de retrieval
Precision@K mide la fracción de los K documentos recuperados que son realmente relevantes: Precision@K = |relevant ∩ retrieved_K| / K. Recall@K mide la fracción de todos los documentos relevantes que fueron recuperados entre los K primeros: Recall@K = |relevant ∩ retrieved_K| / |relevant|. En la práctica, para RAG el recall importa más que la precision: es preferible recuperar algún documento irrelevante extra (el LLM lo filtrará en la generación) que perder el documento realmente relevante. Un buen benchmark es medir Recall@5 y Recall@10 en tu golden dataset.
Métricas de generación: RAGAS
RAGAS es el framework open-source estándar para evaluar sistemas RAG end-to-end. Sus cuatro métricas core son: faithfulness (¿la respuesta es factualmente consistente con el contexto recuperado? — usa el LLM como juez), answer relevance (¿la respuesta responde la pregunta? — también LLM-as-judge), context precision (¿los chunks recuperados son relevantes para la pregunta?) y context recall (¿el contexto recuperado contiene la información necesaria para responder?). El concepto de LLM as judge para evaluar agentes y sistemas RAG tiene un post dedicado en el cluster con los prompts de evaluación detallados.
Golden dataset: el requisito no negociable
Para evaluar de forma significativa necesitas un golden dataset: un conjunto de pares (query, respuesta_correcta, documentos_relevantes) representativos de los casos de uso reales. La construcción del golden dataset es manual y laboriosa pero no tiene sustituto — un benchmark sobre distribuciones sintéticas no predice el rendimiento en producción. Un golden dataset mínimo viable tiene 100 pares para sistemas de producción; 200-500 para sistemas críticos. Si construyes el golden dataset con el propio LLM (auto-generación de preguntas sobre el corpus), asegúrate de validar manualmente al menos el 20% para detectar sesgos sistemáticos.
RAG en producción: caching, latencia, costes
Llevar un prototipo RAG a producción implica resolver tres problemas que no existen en el notebook de desarrollo: latencia, costes a escala y fiabilidad. Un sistema RAG tiene múltiples fuentes de latencia acumulada — embedding de la query, similarity search, llamada al LLM — y cada una tiene su estrategia de optimización específica.
Descomposición de latencia en un RAG típico
En un sistema sin optimizaciones, la latencia se distribuye aproximadamente así: embedding de la query (50-150ms con modelos API externos, 10-30ms con modelos locales), similarity search en vector store (10-50ms para < 1M vectores, hasta 200ms para volúmenes mayores sin tuning), llamada al LLM para generación (500ms-4s dependiendo del modelo y la longitud de respuesta). El TTFB (time to first byte) con streaming del LLM puede ser de 200-500ms aunque la respuesta completa tarde 3s, lo que mejora significativamente la experiencia percibida.
Semantic caching
El semantic caching es la optimización de mayor impacto para RAG conversacional: en lugar de cachear respuestas por hash exacto de la query (útil solo para queries idénticas), se cachea por similitud semántica. Si una query nueva tiene un embedding con similitud coseno > 0.95 respecto a una query previamente respondida, se devuelve la respuesta cacheada sin ejecutar el pipeline completo. GPTCache es la librería de referencia para este patrón. En sistemas de atención al cliente donde muchas queries son variaciones de las mismas preguntas frecuentes, el semantic cache puede reducir las llamadas al LLM en un 40-60%.
Costes: estimación práctica
Para un sistema RAG en producción con 10.000 queries/mes usando text-embedding-3-small + GPT-4o-mini (salida promedio 300 tokens, contexto recuperado 2.000 tokens): embeddings de queries: 10.000 × 500 tokens × $0.02/1M ≈ $0.10/mes; LLM input: 10.000 × 2.500 tokens × $0.15/1M ≈ $3.75/mes; LLM output: 10.000 × 300 tokens × $0.60/1M ≈ $1.80/mes. Total: ~$6/mes para 10.000 queries. Con reranking (cross-encoder local): añadir ~20ms de latencia, 0 coste adicional de API. El coste de la infra del vector store (Chroma autohospedado: $0; Pinecone managed: desde $70/mes por 1M vectores) es el componente que más varía según la elección de tecnología.
Para las empresas que implementan RAG como parte de sus sistemas de agentes de IA para ventas o atención al cliente, los costes unitarios son completamente manejables desde el primer mes — el ROI se obtiene en semanas comparado con los costes de atención manual. Si tu caso de uso incluye agentes integrados en WordPress o en un CMS existente, el post sobre agente IA para WordPress cubre los patrones de integración específicos.
Preguntas frecuentes sobre Retrieval-Augmented Generation
¿Cuánto mejora RAG la precisión de un LLM?
Los estudios empíricos muestran mejoras de entre un 20% y un 45% en faithfulness (reducción de alucinaciones) cuando se compara un LLM con RAG frente al mismo LLM sin contexto externo, dependiendo del dominio y la complejidad del corpus. En dominios técnicos muy específicos (documentación de producto, normativa sectorial), la mejora puede ser aún mayor porque el LLM sin RAG tiene cobertura de preentrenamiento mínima. La clave es que RAG no solo mejora la precisión — también hace las respuestas verificables al citar las fuentes recuperadas.
¿Qué tamaño de chunk es óptimo para RAG?
No existe un tamaño único óptimo: depende del tipo de contenido, el modelo de embedding y el tipo de queries. Como punto de partida, 300-600 tokens con un 15% de overlap es el rango más robusto para texto corrido en castellano. Los chunks cortos (100-200 tokens) funcionan mejor con modelos de embedding de alta dimensión y queries muy específicas. Los chunks largos (800-1.000 tokens) son preferibles cuando el corpus tiene estructura argumentativa donde el contexto local es crítico. La recomendación es siempre benchmarkar en tu golden dataset antes de fijar el parámetro.
¿Es mejor usar un vector store en la nube o autohospedado?
Para equipos pequeños y corpus de hasta 1-2M vectores, una solución autohospedada como Chroma (desarrollo) o Qdrant (producción) es técnicamente suficiente y significativamente más barata que las opciones managed. Las soluciones managed (Pinecone, Weaviate Cloud) añaden valor cuando el equipo no quiere gestionar la infra de vector store, necesita escalabilidad automática, o el corpus supera los 10M vectores. El factor determinante suele ser la capacidad operativa del equipo, no los requisitos técnicos del sistema.
¿RAG funciona con documentos en varios idiomas?
Sí, con las precauciones correctas. El modelo de embedding debe ser multilingüe — modelos como multilingual-e5-large o bge-m3 están específicamente entrenados para capturar similitud semántica cross-lingüe. El riesgo a evitar es mezclar un modelo de embedding entrenado principalmente en inglés con un corpus en castellano: el espacio semántico no estará bien calibrado y el recall sufrirá. Para corpus mixtos inglés/castellano (frecuente en empresas con documentación internacional), los modelos multilingüe son la elección segura.
¿Cuándo debería plantearme combinar RAG con fine-tuning?
Cuando tienes dos problemas distintos simultáneamente: el conocimiento factual del dominio cambia con frecuencia (RAG lo resuelve) y el modelo base no sigue el formato o estilo de respuesta que necesitas (fine-tuning lo resuelve). La arquitectura más robusta para casos de uso empresariales críticos combina un modelo base fine-tuneado para el formato/estilo de respuesta con RAG para el conocimiento factual. El fine-tuning solo necesita ejemplos de formato (cientos, no millones), por lo que el coste es manejable.
¿Cómo depuro un RAG que da respuestas incorrectas?
El proceso de debugging tiene tres pasos. Primero, aislar si el problema es de retrieval (¿los documentos recuperados son relevantes?) o de generación (¿los documentos son correctos pero la respuesta los malinterpreta?). Segundo, si el problema es de retrieval, revisar el chunking (¿la información relevante está fragmentada entre chunks?) y el modelo de embedding (¿captura la semántica del dominio?). Tercero, si el problema es de generación, revisar el prompt template y considerar añadir ejemplos few-shot. El framework RAGAS automatiza parte de este diagnóstico con sus métricas de context precision y faithfulness.
Conclusión
Retrieval-Augmented Generation ha pasado de ser una técnica académica documentada en el paper de Lewis et al. en 2020 a ser la arquitectura de facto para sistemas LLM con conocimiento factual en producción. Su ventaja fundamental — desacoplar el conocimiento del comportamiento del modelo — la hace especialmente adecuada para contextos empresariales donde el corpus es privado, actualizable y trazable.
El stack que cubre esta guía — RecursiveCharacterTextSplitter + OpenAI Embeddings + Chroma + LangChain LCEL + cross-encoder reranker + LangGraph para agentic RAG — es el punto de partida más sólido para un sistema en producción. Cada componente tiene alternativas según el contexto: para embedding open-source, bge-m3; para vector store en producción, Qdrant o Pinecone; para evaluación, RAGAS sobre un golden dataset propio.
El siguiente paso después de implementar el pipeline básico es medir — Recall@5, faithfulness, answer relevance sobre casos de uso reales. Sin métricas no hay mejora sistemática. El cluster de posts de Baigency cubre en profundidad los componentes individuales: embeddings, vector stores, chunking, LangGraph, y evaluación con LLM as judge.
Si estás valorando implementar RAG como parte de tu arquitectura de agentes de IA para empresas y necesitas una consultoría técnica sobre el diseño del sistema, en Baigency llevamos RAG a producción para empresas B2B en España. Cuéntanos tu caso de uso.



