RAG No Es Suficiente: Lo Que Necesitan los Sistemas de IA Fiables
Dónde falla RAG en producción y qué construir encima. Calidad de chunks, capas de orquestación, búsqueda híbrida, límites de alucinación, gestión de costos y cuándo evitar RAG.
La Trampa de la Demo de RAG
Toda demo de RAG funciona. Subes unos PDFs, los fragmentas, los embedes, consultas y obtienes una respuesta. La demo siempre impresiona. Los stakeholders se emocionan. El equipo estima dos semanas para producción.
Seis meses después, el sistema sigue siendo poco fiable. Los usuarios reciben respuestas incorrectas basadas en chunks desactualizados. El modelo cita documentos con total confianza que dicen lo contrario de lo que afirma la respuesta. Los costos son 10 veces superiores a la estimación original. Y nadie logra entender por qué la misma pregunta produce respuestas diferentes según la hora del día.
RAG no es una solución. RAG es un patrón de recuperación. Un sistema de IA fiable necesita una capa de orquestación, controles de calidad, búsqueda híbrida, límites de alucinación, gestión de costos y monitorización encima de RAG. Este artículo cubre lo que hemos aprendido construyendo sistemas RAG en producción.
Para contexto más amplio sobre arquitectura RAG empresarial y búsqueda vectorial, esas guías cubren los patrones fundamentales. Este artículo se centra en dónde esos patrones fallan y qué necesitas más allá de ellos.
Dónde Falla RAG
| Modo de Fallo | Qué Ocurre | Frecuencia |
|---|---|---|
| Calidad de chunks | Límites incorrectos dividen el contexto, la respuesta se basa en información parcial | Muy común |
| Datos obsoletos | Índice no actualizado, la respuesta se basa en documentos desactualizados | Común |
| Fallo de recuperación | El documento relevante existe pero la similitud del embedding no lo encuentra | Común |
| Alucinación pese a la recuperación | El modelo ignora el contexto recuperado y genera desde sus datos de entrenamiento | Común |
| Desbordamiento de ventana de contexto | Demasiados chunks recuperados, el modelo pierde el foco | Moderado |
| Confusión entre documentos | Chunks de diferentes documentos mezclados, el modelo combina datos contradictorios | Moderado |
| Explosión de costos | Costos de embedding + recuperación + generación escalan con el volumen de consultas | Gradual |
| Picos de latencia | Búsqueda vectorial + reranking + generación tarda demasiado para uso interactivo | Moderado |
La Calidad de los Chunks Lo Es Todo
El problema más subestimado. Si tus chunks dividen un párrafo en mitad de una idea, el contexto recuperado está incompleto. Si tus chunks son demasiado grandes, el contenido irrelevante diluye la información útil. Si tus chunks no preservan la estructura del documento (encabezados, tablas, listas), el modelo pierde el contexto organizativo.
// Malo: chunks de tamaño fijo rompen el contexto
function naiveChunk(text: string, size: number): string[] {
const chunks = [];
for (let i = 0; i < text.length; i += size) {
chunks.push(text.slice(i, i + size));
}
return chunks;
// Problema: divide oraciones, párrafos y tablas a mitad de contenido
}
// Mejor: chunking semántico con solapamiento
function semanticChunk(text: string, options: ChunkOptions): Chunk[] {
const sections = splitByHeadings(text); // Respetar la estructura del documento
const paragraphs = sections.flatMap(s =>
splitByParagraphs(s, { maxSize: options.maxChunkSize })
);
return paragraphs.map((p, i) => ({
content: p.text,
metadata: {
section: p.sectionTitle,
pageNumber: p.pageNumber,
documentId: p.documentId,
position: i,
},
// Solapamiento: incluir las últimas 2 oraciones del chunk anterior
prefix: i > 0 ? getLastSentences(paragraphs[i - 1].text, 2) : '',
}));
}
El solapamiento importa. Sin él, una pregunta que abarca dos chunks obtiene contexto parcial de cada uno y una respuesta completa de ninguno. Con 2-3 oraciones de solapamiento, el modelo tiene suficiente contexto para conectar los límites entre chunks.
Los metadatos del chunk son igualmente críticos. Cada chunk debe llevar el ID del documento fuente, el título de la sección, el número de página y la posición. Sin metadatos, no puedes indicar al usuario de dónde viene la respuesta. Sin atribución de fuente, la respuesta es inverificable.
Calidad de Recuperación vs Cantidad de Recuperación
Recuperar más chunks no significa mejores respuestas. En la práctica, hemos comprobado que 3-5 chunks de alta calidad superan consistentemente a 10-15 chunks mediocres.
| Chunks Recuperados | Calidad de Respuesta | Latencia | Costo |
|---|---|---|---|
| 1-2 | Riesgo de perder contexto | Rápida | Bajo |
| 3-5 | Mejor equilibrio (recomendado) | Moderada | Moderado |
| 5-10 | Rendimiento decreciente, algo de ruido | Más lenta | Mayor |
| 10+ | Dilución de contexto, modelo confundido | Lenta | Alto |
La solución: recuperar ampliamente, luego reordenar agresivamente.
async function retrieveAndRerank(query: string, options: RetrievalOptions) {
// Paso 1: Recuperación amplia (obtener 20 candidatos)
const candidates = await vectorStore.search(query, { limit: 20 });
// Paso 2: Reranking con cross-encoder (puntuar cada candidato contra la consulta)
const reranked = await reranker.rank(query, candidates, {
model: 'cross-encoder/ms-marco-MiniLM-L-12-v2',
});
// Paso 3: Tomar los 5 mejores después del reranking
const topChunks = reranked.slice(0, 5);
// Paso 4: Filtrar por puntuación mínima de relevancia
return topChunks.filter(c => c.score > options.minRelevanceScore);
}
El reranker es un modelo cross-encoder que puntúa cada candidato contra la consulta con mucha mayor precisión que la similitud coseno sobre embeddings. Es más lento (ejecuta inferencia por candidato), pero la mejora en calidad es sustancial. Ejecutarlo sobre 20 candidatos para seleccionar 5 añade 100-200ms de latencia, lo cual es aceptable para la mayoría de casos de uso.
La Capa de Orquestación Que RAG Necesita
RAG básico es: embeber la consulta, buscar vectores, meter el contexto en el prompt, generar. Un sistema en producción necesita una capa de orquestación entre la recuperación y la generación.
Consulta del Usuario
│
▼
┌──────────────────┐
│ Análisis de │ Clasificar intención, extraer entidades, detectar idioma
│ Consulta │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Enrutamiento │ ¿Qué índice? ¿Qué estrategia de recuperación? ¿Cache hit?
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Recuperación │ Búsqueda vectorial + búsqueda por palabras clave (híbrida)
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Reranking │ Puntuación con cross-encoder, filtrar chunks de baja relevancia
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Ensamblaje de │ Ordenar chunks, añadir metadatos, respetar presupuesto de tokens
│ Contexto │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Generación │ Llamada al LLM con contexto ensamblado + system prompt
└────────┬─────────┘
│
▼
┌──────────────────┐
│ Validación de │ Verificar alucinaciones, validar citas, escaneo de PII
│ Salida │
└────────┬─────────┘
│
▼
Respuesta
Análisis de Consulta
No toda consulta necesita RAG. Algunas son conversacionales ("hola", "gracias"). Algunas son sobre el propio sistema ("¿cómo uso esta herramienta?"). Algunas son ambiguas y necesitan aclaración. El analizador de consultas clasifica la intención antes de lanzar la recuperación.
async function analyzeQuery(query: string): Promise<QueryAnalysis> {
// Clasificación rápida (puede ser un modelo pequeño o basado en reglas)
const intent = await classifyIntent(query);
if (intent === 'greeting' || intent === 'meta') {
return { needsRetrieval: false, intent, response: getStaticResponse(intent) };
}
if (intent === 'ambiguous') {
return { needsRetrieval: false, intent, clarificationNeeded: true };
}
return {
needsRetrieval: true,
intent,
extractedEntities: await extractEntities(query),
detectedLanguage: await detectLanguage(query),
};
}
Enrutamiento
Diferentes consultas pueden necesitar diferentes índices, diferentes estrategias de recuperación o diferentes modelos.
| Tipo de Consulta | Índice | Estrategia | Modelo |
|---|---|---|---|
| Pregunta sobre producto | Índice de productos | Híbrida (texto + vector) | Modelo rápido (GPT-4o-mini) |
| Pregunta legal/cumplimiento | Índice de políticas | Solo vectorial (preciso) | Modelo preciso (GPT-4o) |
| Soporte técnico | Índice de base de conocimiento | Híbrida + rerank | Modelo rápido |
| Consulta multiidioma | Índice multilingüe | Vectorial con filtro de idioma | Modelo multilingüe |
Ensamblaje de Contexto
Después de la recuperación y el reranking, los chunks deben ensamblarse en un prompt que respete el presupuesto de tokens del modelo.
function assembleContext(chunks: RankedChunk[], tokenBudget: number): string {
let context = '';
let tokensUsed = 0;
for (const chunk of chunks) {
const chunkTokens = estimateTokens(chunk.content);
if (tokensUsed + chunkTokens > tokenBudget) break;
context += `\n\n---\nSource: ${chunk.metadata.documentTitle} (${chunk.metadata.section})\n`;
context += chunk.content;
tokensUsed += chunkTokens;
}
return context;
}
El presupuesto de tokens debe tener en cuenta el system prompt, la consulta del usuario, el contexto ensamblado Y la longitud esperada de la respuesta. Un error común es llenar toda la ventana de contexto con chunks recuperados, sin dejar espacio para una respuesta de calidad.
Búsqueda Híbrida: Texto + Vector
La búsqueda vectorial pura falla con consultas basadas en palabras clave. Un usuario buscando "código de error E-4021" obtendrá resultados pobres con similitud de embeddings porque los códigos de error no tienen significado semántico. La búsqueda de texto pura falla con consultas semánticas. Un usuario buscando "cómo solucionar problemas de inicio de sesión" no encontrará un documento titulado "Guía de Resolución de Problemas de Autenticación."
La búsqueda híbrida combina ambas:
async function hybridSearch(query: string, options: SearchOptions) {
// Ejecución en paralelo
const [vectorResults, textResults] = await Promise.all([
vectorStore.search(query, { limit: options.vectorLimit }),
textIndex.search(query, { limit: options.textLimit }),
]);
// Reciprocal Rank Fusion (RRF) para fusionar resultados
const merged = reciprocalRankFusion(vectorResults, textResults, {
vectorWeight: 0.6,
textWeight: 0.4,
});
return merged.slice(0, options.totalLimit);
}
function reciprocalRankFusion(
vectorResults: SearchResult[],
textResults: SearchResult[],
weights: { vectorWeight: number; textWeight: number },
): SearchResult[] {
const scores = new Map<string, number>();
const k = 60; // Constante RRF
vectorResults.forEach((result, rank) => {
const score = (scores.get(result.id) || 0) + weights.vectorWeight / (k + rank + 1);
scores.set(result.id, score);
});
textResults.forEach((result, rank) => {
const score = (scores.get(result.id) || 0) + weights.textWeight / (k + rank + 1);
scores.set(result.id, score);
});
return Array.from(scores.entries())
.sort(([, a], [, b]) => b - a)
.map(([id, score]) => ({ id, score }));
}
La proporción de pesos (vector 0.6, texto 0.4) es un punto de partida. Ajústala según la distribución de tus consultas. Si la mayoría de las consultas son pesadas en palabras clave (SKUs de productos, códigos de error), aumenta el peso del texto. Si la mayoría son lenguaje natural, aumenta el peso vectorial.
Para más información sobre arquitectura de búsqueda en contextos de comercio, consulta nuestra guía de plataformas ecommerce.
Límites de Alucinación
RAG reduce la alucinación comparado con la generación pura de LLM. No la elimina. El modelo puede seguir:
- Ignorando el contexto recuperado y generando desde datos de entrenamiento
- Mezclando información de múltiples chunks incorrectamente
- Inventando citas que no existen en el contexto recuperado
- Extrapolando más allá de lo que el contexto respalda
Estrategias de Mitigación
1. System prompts restringidos:
Eres un asistente de soporte. Responde SOLO basándote en el contexto proporcionado.
Si el contexto no contiene suficiente información para responder, di
"No tengo suficiente información para responder esa pregunta."
NO uses información de tus datos de entrenamiento.
Cada afirmación debe referenciar una fuente específica del contexto.
2. Verificación de citas:
async function verifyCitations(response: string, chunks: RankedChunk[]): VerificationResult {
const citations = extractCitations(response);
const verified = [];
const unverified = [];
for (const citation of citations) {
const found = chunks.some(chunk =>
chunk.content.includes(citation.claimedText) ||
fuzzyMatch(chunk.content, citation.claimedText, 0.85)
);
(found ? verified : unverified).push(citation);
}
return {
allVerified: unverified.length === 0,
verified,
unverified,
confidenceScore: verified.length / (verified.length + unverified.length),
};
}
3. Puntuación de confianza:
Si la respuesta del modelo no se alinea bien con el contexto recuperado (bajo solapamiento, sin citas directas), márcala como baja confianza. Muestra una advertencia al usuario o escala a un humano.
Para más información sobre modos de fallo de IA y cómo manejarlos, consulta nuestra guía de modos de fallo de IA.
Costo y Latencia
Los costos de RAG escalan con el volumen de consultas en tres dimensiones:
| Componente | Factor de Costo | Rango Típico |
|---|---|---|
| Embedding de consulta | Por consulta (inferencia del modelo) | $0.0001 por consulta |
| Búsqueda vectorial | Por consulta (cómputo + I/O) | $0.0005 por consulta |
| Reranking | Por consulta * candidatos (inferencia del modelo) | $0.001 por consulta |
| Generación LLM | Tokens de entrada (contexto) + tokens de salida | $0.01-0.10 por consulta |
| Embedding de documentos | Una vez por documento (al ingestar) | $0.0001 por página |
La generación LLM domina el costo. Reducir el tamaño del contexto (menos chunks, chunks más cortos) reduce directamente el componente más caro.
Estrategias de Caché
// Caché semántica: cachear respuestas para consultas similares
async function cachedQuery(query: string): Promise<string | null> {
// Embeber la consulta
const queryEmbedding = await embedder.embed(query);
// Buscar en el índice de caché consultas similares
const cached = await cacheIndex.search(queryEmbedding, {
minSimilarity: 0.95, // Umbral alto para cache hits
limit: 1,
});
if (cached.length > 0) {
return cached[0].response; // Cache hit
}
return null; // Cache miss, proceder con el pipeline RAG completo
}
La caché semántica funciona porque muchos usuarios hacen preguntas similares de formas ligeramente diferentes. "¿Cómo restablezco mi contraseña?" y "Instrucciones para restablecer contraseña" son cadenas diferentes pero semánticamente idénticas. Un umbral de similitud de 0.95 asegura que solo las consultas casi idénticas obtengan respuestas cacheadas.
Presupuesto de Latencia
Para uso interactivo (chatbot, asistente de soporte), el pipeline completo debe completarse en menos de 3 segundos:
| Etapa | Presupuesto | Optimización |
|---|---|---|
| Análisis de consulta | 50ms | Basado en reglas o modelo pequeño |
| Verificación de caché | 30ms | Índice vectorial en memoria |
| Búsqueda vectorial | 100ms | Clúster de búsqueda dedicado |
| Búsqueda de texto | 100ms | En paralelo con búsqueda vectorial |
| Reranking | 200ms | Cross-encoder pequeño, limitar candidatos |
| Ensamblaje de contexto | 10ms | En memoria |
| Generación LLM | 1,500ms | Streaming, modelo rápido |
| Validación de salida | 100ms | Basado en reglas + modelo pequeño |
| Total | ~2,100ms |
Hacer streaming de la respuesta del LLM al usuario mientras la generación está en curso hace que la latencia percibida sea mucho menor. El usuario ve los primeros tokens en 300-500ms aunque la respuesta completa tarde 1,500ms.
Cuándo Evitar RAG por Completo
RAG no siempre es el patrón correcto. A veces los enfoques más simples funcionan mejor:
| Escenario | Mejor Enfoque | Por Qué |
|---|---|---|
| FAQ estático (< 50 preguntas) | Coincidencia por palabras clave + respuesta plantilla | Más rápido, más barato, determinista |
| Consultas sobre datos estructurados | Consulta SQL/API + plantilla | El LLM añade latencia y riesgo de alucinación |
| Datos en tiempo real (precios de bolsa, inventario) | Llamada directa a API | Los embeddings están obsoletos por definición |
| Clasificación simple | Clasificador fine-tuned | Más barato, más rápido, más fiable |
| Resumen de documentos | Llamada directa al LLM (sin recuperación) | El documento completo ES el contexto |
RAG tiene sentido cuando tienes una base de conocimiento grande (cientos a miles de documentos), consultas en lenguaje natural que no pueden resolverse por palabras clave, y necesidad de respuestas sintetizadas de múltiples fuentes. Si tu caso de uso no encaja en este perfil, un enfoque más simple será más fiable y más barato.
Para cómo abordamos estas decisiones de arquitectura en nuestra práctica de servicios de IA, y para patrones más amplios en diseño de flujos de trabajo de IA, esas páginas ofrecen más contexto.
Errores Comunes
-
Chunking de tamaño fijo. Los chunks que dividen párrafos, tablas o bloques de código a mitad de contenido producen recuperación basura. Usa chunking semántico que respete la estructura del documento.
-
Sin solapamiento de chunks. Sin solapamiento, las consultas que abarcan límites de chunks obtienen contexto parcial de cada uno y una respuesta completa de ninguno.
-
Sin reranking. La similitud de embeddings es un filtro aproximado. Un reranker con cross-encoder mejora drásticamente la calidad de los 5 mejores resultados.
-
Llenar toda la ventana de contexto. Deja espacio para el system prompt, la consulta del usuario Y la respuesta esperada. Un prompt relleno con 15 chunks no deja espacio para una respuesta de calidad.
-
Sin búsqueda híbrida. La búsqueda vectorial pura falla con consultas de palabras clave (códigos de error, SKUs de productos). La búsqueda de texto pura falla con consultas semánticas. Usa ambas.
-
Sin caché semántica. Preguntas similares hechas por diferentes usuarios activan el pipeline RAG completo cada vez. Una caché semántica con umbral de similitud de 0.95 reduce los costos significativamente.
-
Confiar en la salida de RAG sin verificación. RAG reduce la alucinación. No la elimina. Verifica las citas contra el contexto recuperado. Marca las afirmaciones no verificadas.
-
Sin monitorización. Necesitas rastrear la calidad de recuperación (¿se recuperaron los chunks correctos?), la calidad de la respuesta (¿el usuario encontró útil la respuesta?), la latencia, el costo por consulta y la tasa de cache hits.
Conclusiones Clave
-
RAG es un patrón de recuperación, no una solución. Un sistema fiable necesita análisis de consultas, enrutamiento, búsqueda híbrida, reranking, ensamblaje de contexto, generación y validación de salida encima de la recuperación.
-
La calidad de los chunks determina la calidad de las respuestas. El chunking semántico con solapamiento, metadatos y preservación de la estructura del documento es la base. Todo lo demás se construye sobre buenos chunks.
-
Recupera ampliamente, reordena agresivamente. Obtén 20 candidatos con similitud de embeddings. Puntúalos con un cross-encoder. Toma los 5 mejores. Filtra por relevancia mínima.
-
La búsqueda híbrida cubre lo que los vectores no alcanzan. Palabras clave, códigos de error, IDs de producto y coincidencias exactas necesitan búsqueda de texto. Las consultas semánticas necesitan búsqueda vectorial. Usa ambas con reciprocal rank fusion.
-
La generación LLM domina el costo. Reducir el tamaño del contexto (menos chunks, mejores chunks) es la optimización de costos más efectiva.
-
A veces RAG es el patrón equivocado. FAQs estáticos, consultas sobre datos estructurados, datos en tiempo real y clasificación simple tienen soluciones más simples y fiables.
Construimos sistemas RAG en producción como parte de nuestra práctica de servicios de IA e ingeniería de datos. Si estás construyendo un sistema RAG o depurando uno que no es fiable, habla con nuestro equipo o solicita un presupuesto. También puedes explorar nuestra página de metodología para conocer cómo abordamos los proyectos de IA.
Temas cubiertos
Guías relacionadas
Modos de Fallo de la IA: Guia de Ingenieria para Produccion
Guia tecnica sobre fallos de sistemas IA en produccion. Aprende sobre alucinaciones, limites de contexto, inyeccion de prompts y deriva del modelo.
Leer guíaGuía Empresarial de Sistemas de IA Agéntica
Guia tecnica de sistemas de IA agentica en entornos empresariales. Descubre la arquitectura, capacidades y aplicaciones de agentes IA autonomos.
Leer guíaComercio Agéntico: Cómo Dejar que los Agentes IA Compren de Forma Segura
Cómo diseñar comercio iniciado por agentes IA con gobernanza. Motores de políticas, puertas de aprobación HITL, recibos HMAC, idempotencia, aislamiento de tenants y el Agentic Checkout Protocol completo.
Leer guía¿Listo para construir sistemas de IA listos para producción?
Nuestro equipo se especializa en sistemas de IA listos para producción. Hablemos de cómo podemos ayudar.
Iniciar una conversación