Guía técnica

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.

18 de febrero de 202618 min de lecturaEquipo de Ingeniería Oronts

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 FalloQué OcurreFrecuencia
Calidad de chunksLímites incorrectos dividen el contexto, la respuesta se basa en información parcialMuy común
Datos obsoletosÍndice no actualizado, la respuesta se basa en documentos desactualizadosComún
Fallo de recuperaciónEl documento relevante existe pero la similitud del embedding no lo encuentraComún
Alucinación pese a la recuperaciónEl modelo ignora el contexto recuperado y genera desde sus datos de entrenamientoComún
Desbordamiento de ventana de contextoDemasiados chunks recuperados, el modelo pierde el focoModerado
Confusión entre documentosChunks de diferentes documentos mezclados, el modelo combina datos contradictoriosModerado
Explosión de costosCostos de embedding + recuperación + generación escalan con el volumen de consultasGradual
Picos de latenciaBúsqueda vectorial + reranking + generación tarda demasiado para uso interactivoModerado

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 RecuperadosCalidad de RespuestaLatenciaCosto
1-2Riesgo de perder contextoRápidaBajo
3-5Mejor equilibrio (recomendado)ModeradaModerado
5-10Rendimiento decreciente, algo de ruidoMás lentaMayor
10+Dilución de contexto, modelo confundidoLentaAlto

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ÍndiceEstrategiaModelo
Pregunta sobre productoÍndice de productosHíbrida (texto + vector)Modelo rápido (GPT-4o-mini)
Pregunta legal/cumplimientoÍndice de políticasSolo vectorial (preciso)Modelo preciso (GPT-4o)
Soporte técnicoÍndice de base de conocimientoHíbrida + rerankModelo rápido
Consulta multiidiomaÍndice multilingüeVectorial con filtro de idiomaModelo 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:

ComponenteFactor de CostoRango Típico
Embedding de consultaPor consulta (inferencia del modelo)$0.0001 por consulta
Búsqueda vectorialPor consulta (cómputo + I/O)$0.0005 por consulta
RerankingPor consulta * candidatos (inferencia del modelo)$0.001 por consulta
Generación LLMTokens de entrada (contexto) + tokens de salida$0.01-0.10 por consulta
Embedding de documentosUna 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:

EtapaPresupuestoOptimización
Análisis de consulta50msBasado en reglas o modelo pequeño
Verificación de caché30msÍndice vectorial en memoria
Búsqueda vectorial100msClúster de búsqueda dedicado
Búsqueda de texto100msEn paralelo con búsqueda vectorial
Reranking200msCross-encoder pequeño, limitar candidatos
Ensamblaje de contexto10msEn memoria
Generación LLM1,500msStreaming, modelo rápido
Validación de salida100msBasado 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:

EscenarioMejor EnfoquePor Qué
FAQ estático (< 50 preguntas)Coincidencia por palabras clave + respuesta plantillaMás rápido, más barato, determinista
Consultas sobre datos estructuradosConsulta SQL/API + plantillaEl LLM añade latencia y riesgo de alucinación
Datos en tiempo real (precios de bolsa, inventario)Llamada directa a APILos embeddings están obsoletos por definición
Clasificación simpleClasificador fine-tunedMás barato, más rápido, más fiable
Resumen de documentosLlamada 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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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

RAG producciónlimitaciones RAGfiabilidad IAproblemas retrieval augmented generationarquitectura RAGbúsqueda híbridaorquestación RAGalucinación RAG

¿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