[!NOTE] Traducción automática Este artículo se tradujo automáticamente a partir de la versión original en inglés.

Evaluación de RAG: Métricas para cada etapa de un sistema RAG en producción

Parte 1 de la serie de producción RAG

Un sistema RAG con filtros dañados puede seguir funcionando durante meses sin generar una alerta operativa. Aún así, proporciona respuestas y cumple con su objetivo de latencia, pero dichas respuestas se basan en evidencias incompletas. El análisis de recall@k frente al conjunto de referencia original revela esta deficiencia, algo que los paneles de control de latencia y disponibilidad no logran detectar.

La evaluación solo puede detectar una falla cuando cada etapa pipeline dispone de su propia métrica específica. En este artículo se establece una correspondencia entre los modos de fallo más habituales y dichas métricas, abarcando todo el proceso desde el análisis de documentos hasta la monitorización en producción.

[!NOTE] ¿Quieres saltarte los pasos y ejecutar el código directamente?

El código ejecutable slavadubrov/rag-evals-demo El repositorio aplica las métricas a SciFact. make eval ejecuta el conjunto de pruebas, y make benchmark Compara los métodos de chunking, embedding, y las configuraciones de LLM. Los cuadernos de trabajo del 00 al 09 presentan de forma independiente cada una de estas métricas. La demostración utiliza Qdrant integrado, por lo que no requiere Docker.

TL;DR

Las secciones siguen el orden establecido por pipeline. Se debe comenzar con la tabla de decisiones y, a continuación, utilizar las secciones posteriores como referencia para cada etapa.


Tabla de decisión para la evaluación de RAG

Utilice esta tabla como punto de partida antes de seleccionar un framework. La métrica adecuada depende del modo de fallo que se pretende detectar, y no del nombre de la herramienta.

PreguntaFamilia de métricasUtilízalo cuandoTenga cuidado con
¿Se ha conservado el contenido original mediante el análisis sintáctico?Completitud de la extracción, cobertura de tablas y figurasLos PDF, diapositivas, escaneos y páginas HTML se incorporan al corpus.Un texto de aspecto limpio puede seguir presentando errores en los subtítulos, notas al pie o en la estructura de las tablas.
¿La fase de recuperación encontró las pruebas adecuadas?Recuerdo@k, nDCG@k, MRR, precisión/recuerdo en contextoPuede etiquetar los fragmentos o documentos relevantes.Un filtro estricto de metadatos puede eliminar el documento adecuado antes de que comience el proceso de clasificación.
¿Mejoró reranking la lista de candidatos?Reranker mejora, precisión@1, delta nDCGLos cross-encoders o los LLM rankers se encuentran después de la fase de recuperación.Mida la latencia y el costo mediante el aumento de calidad.
¿Utilizó la respuesta las pruebas presentadas?Fidelidad, fundamentación y soporte para citacionesLa respuesta hace referencia a documentos o establece hechos a partir del contexto.La fidelidad no puede diagnosticar una parseo defectuoso ni una recuperación incorrecta.
¿Es el sistema estable en producción?Deriva, regeneración, modo de respaldo, latencia p95, costo por respuestaCambios en el tráfico tras el lanzamientoLa telemetría de producción requiere una revisión humana por muestreo para mantenerse calibrada.

Para una comparación más breve de las herramientas, consulte Mejores herramientas y métricas de evaluación para RAG en 2026.

Parte 1: Definir el éxito antes de la arquitectura

Elabore el conjunto de evaluación antes del diagrama de arquitectura. Esto permite establecer un objetivo medible para cada elección de componente que se realice posteriormente.

No se puede elegir entre BM25 y recuperación densa, fragmentación recursiva y semántica, ni entre Cohere Rerank y BGE hasta saber qué es lo que se está optimizando. “Mejores respuestas” no es una métrica válida. Un contrato ilustrativo sería “fidelidad ≥ 0.85 en un conjunto de referencia de 200 consultas que abarque nuestras tres intenciones principales, con una latencia p95 < 1.5 s y una tasa de exclusión falsa por filtrado < 2 %”. Estos valores son solo marcadores; lo importante es que la calidad, la cobertura, la latencia y el filtrado cuenten con umbrales explícitos.

Defina el harness antes de escribir el código de recuperación. El primer harness resultará incorrecto, por lo que tendrá que modificarlo. Revisar una métrica es mucho más económico que corregir un sistema que ya se ha desplegado.

Tres capas pipeline y dos modos de ejecución

El RAG moderno es un pipeline, por lo que la evaluación debe realizarse de forma pipeline. Ningún valor numérico único puede capturar todos los modos de fallo.

La evaluación en producción cuenta con tres capas pipeline. La evaluación de ingestión verifica si el corpus y el índice conservan la información original. La evaluación en tiempo de consulta comprueba si la reescritura, el filtrado, la recuperación, reranking y la compilación del contexto permitieron encontrar las pruebas adecuadas. Finalmente, la evaluación de respuesta y producción analiza si la respuesta hace uso de dichas pruebas y si mantiene una calidad aceptable bajo tráfico real. Al combinar estas capas en una única puntuación, los errores de normalización pueden quedar ocultos dentro de un valor de respuesta considerado aceptable.

Los tres escenarios en los que un sistema RAG puede perder evidencias

Esas capas describen el punto exacto en el que se produce una falla. Los modos offline y online indican cuándo y con qué datos se ejecuta la verificación. La evaluación offline utiliza un conjunto de datos fijo dataset acompañado de valores de referencia conocidos; es reproducible y resulta útil para la selección de componentes, las comparaciones A/B y los controles de integración continua. Por su parte, la evaluación online analiza tráfico en tiempo real y permite registrar procesos de regeneración, tiempos de permanencia, retroalimentación explícita y cambios reales en las consultas. Este método genera más ruido y resulta más difícil de instrumentar.

Cada capa pipeline puede aportar comprobaciones tanto en modo offline como en línea. Un corpus de ingestión fijo permite detectar regresiones en el analizador antes del lanzamiento, mientras que los monitores de frescura y de fallos de análisis se encargan de supervisar las actualizaciones en tiempo real. Un conjunto de consultas fijo sirve para evaluar el rendimiento de la recuperación de información antes del lanzamiento, mientras que los registros en tiempo real extraídos de forma aleatoria revelan posibles desviaciones en el entorno de producción. Las comprobaciones exclusivamente en modo offline no detectan cambios en tiempo real; por su parte, las comprobaciones exclusivamente en línea dificultan la reproducción de las regresiones.

A nivel de componente vs. de extremo a extremo

Existen dos errores comunes. Una evaluación exclusiva de tipo end-to-end solo indica que el sistema está defectuoso, pero no dónde exactamente. Por su parte, una evaluación centrada únicamente en los componentes puede mostrar que cada parte funciona correctamente, aun cuando el sistema completo siga fallando. La solución consiste en utilizar unas pocas métricas clave de tipo end-to-end para tomar decisiones de aceptación o rechazo, además de métricas específicas de cada componente con el fin de realizar un diagnóstico preciso. Las métricas de recuperación permiten detectar regresiones en el módulo encargado de buscar información. Las métricas de generación, a su vez, sirven para identificar regresiones en el módulo generador. Finalmente, la corrección de las respuestas en el nivel end-to-end permite detectar fallos en la integración del sistema.

La referencia frameworks (recorrido basado en opiniones)

FrameworkMejor enDónde falla
RAGASMétricas sin referencia a RAG (fidelidad, relevancia de la respuesta, precisión/recuerdo del contexto); el vocabulario de factoLLM: evaluar el costo; componentes de puntuación opacos durante la depuración; valores predeterminados centrados en el enfoque inglés.
ARESEl clasificador entrenado evalúa según pipeline; se requieren menos anotaciones que en los enfoques de tipo RAGAS; presenta una alta precisión para sistemas con similitudes cercanas.Configuración más compleja; es necesario entrenar los modelos realmente.
TruLensFunciones de retroalimentación componibles que ofrecen una alta explicabilidad; trazas OpenTelemetry; adecuadas para entornos de producciónEn las métricas específicas de RAG, se incluyen menos baterías que en RAGAS.
DeepEvalPruebas unitarias al estilo Pytest para los resultados de LLM; G-Eval, métricas personalizadas, integración nativa en flujos CI/CDUn uso intensivo de LLM-jueces provoca picos en los costes.
Arize PhoenixRastreo detallado y visualización con embedding; permite detectar visualmente la deriva de embedding; nativo de OTELTraes tus propias definiciones de métricas.
Track de TREC 2024 RAGPublicación de benchmark para la evaluación de nuggets (AutoNuggetizer), la evaluación de soporte y la fluidez en MS MARCO Segment v2.1No es una herramienta runtime, sino un benchmark para realizar calibraciones de referencia.

Mi stack por defecto incluye RAGAS para el vocabulario de métricas, DeepEval para las puertas de control en CI, Phoenix para el rastreo en producción, además de código personalizado para métricas específicas de la ontología. Con el tiempo, cualquier solución con la que comiences resultará insuficiente. Elige el framework que facilite la implementación de métricas personalizadas.

Para benchmarks, utilice BEIR (Thakur et al., NeurIPS 2021) para la generalización en recuperación de información sin entrenamiento previo. MTEB para una calidad general de embedding. MIRACL para la recuperación multilingüe, y el TREC 2024 RAG Carrera para la evaluación de extremo a extremo de RAG.


Parte 2: Asignar los puntos de evaluación a pipeline

Un sistema RAG en entorno de producción es mucho más complejo que una simple operación de “incorporar documentos, recuperar fragmentos y llamar a un LLM”. Cualquiera de las etapas que se encuentran entre la adquisición del documento y la entrega de la respuesta puede fallar.

Todo el RAG pipeline completo, con indicadores métricos en cada etapa del proceso.

Cada etapa del diagrama cuenta con al menos una métrica. Una etapa que carezca de métricas puede fallar sin que nadie se dé cuenta.

La vía de las tres etapas se corresponde con los puntos en los que puede producirse la pérdida de evidencias. La vía de ingestión abarca el análisis sintáctico, la limpieza de datos, el particionamiento en bloques, embedding, e indexación. La vía de tiempo de consulta incluye la reescritura de consultas, el filtrado, la recuperación de información, reranking, y la compilación del contexto relevante. Finalmente, la vía de respuesta y producción se ocupa de garantizar la fidelidad de los resultados, verificar las fuentes citadas, analizar las señales provenientes de los usuarios, detectar desviaciones, controlar la latencia y gestionar los costes asociados.

Los errores se acumulan a lo largo de la cadena de procesamiento. Una解析 incorrecta limita la posibilidad de dividir los datos en chunks. Una mala división en chunks limita la capacidad de recuperación de información. Una recuperación deficiente limita a reranking. Un reranking inadecuado, a su vez, limita la generación del resultado. La fidelidad solo permite evaluar la respuesta final, y nunca las causas que se producen en etapas anteriores del proceso.


Parte 3: Evaluación de la ingesta

Muchas fallas en entornos de producción relacionadas con RAG tienen su origen en la fase de ingestión de datos. El sistema funciona correctamente con documentos de prueba limpios, pero falla al tratar con PDF reales, escaneos, tablas y páginas de corpus con formato desordenado.

Adquisición y análisis del documento

Qué medir:

Compare diferentes familias de analizadores de texto, como por ejemplo la versión de referencia de Tesseract, un modelo basado en VLM y OCR, así como la opción propuesta por su proveedor. Utilice una muestra estratificada de clases reales de documentos a una resolución DPI fija, que incluya escaneos nítidos, fotografías, tablas, texto multilingüe, fórmulas matemáticas y texto manuscrito. Indique el CER o el WER para cada clase, además del TEDS para las páginas que contienen tablas.

Limpieza y normalización

El chunking controla la calidad de la recuperación

El particionamiento en chunks puede generar una brecha en la recuperación de múltiples puntos, incluso cuando el modelo embedding permanece estable. En El proveedor de NVIDIA para 2024 benchmark,

El agrupamiento semántico organiza las oraciones adyacentes en función de la similitud embedding y realiza cortes en los límites donde dicha similitud es baja. LangChain’s SemanticChunker y los de LlamaIndex SemanticSplitterNodeParser

La división recursiva de caracteres prueba primero los saltos de párrafo, luego los saltos de oración y, finalmente, los saltos de palabra, hasta que cada fragmento cumpla con el tamaño objetivo. LangChain’s RecursiveCharacterTextSplitter Implementa la secuencia seleccionando los valores de ventana y solapamiento adecuados para la estructura de tu documento, y deja que el conjunto dorado determine los valores finales.

Métricas a seguir:

Mi opinión: el particionamiento estructural (división basada en encabezados, tablas y secciones, implementado por analizadores como unstructured.io

Extracción y enriquecimiento de metadatos

Embedding generación

Construcción del índice


Parte 4: Evaluación en tiempo de consulta

La vía de tiempo de consulta contiene las métricas que permiten diagnosticar la ruta de recuperación. El Recall@k por sí solo no puede indicar si el fallo fue causado por la reescritura, el filtrado, reranking, o por la compilación del contexto.

Comprensión y reescritura de consultas

Métricas de recuperación

Estas son las métricas de referencia. Si no se registran, no es posible determinar si la recuperación de información está mejorando.

MétricaQué mide¿Cuándo utilizarlo?
Recall@kfracción de los documentos relevantes de una consulta que se devuelven entre los k primerosSe utiliza cuando la ausencia de cualquier elemento del conjunto relevante tiene importancia.
Precisión@kporcentaje de los top-k que son relevantesResulta útil cuando el ventanillo de contexto constituye el cuello de botella.
MRRpromedio de 1/posición del primer documento relevantecuando los usuarios solo examinan los resultados de los primeros 1 o 3
nDCG@kganancia ponderada por descuento de posición y grados de relevanciala métrica estándar de recuperación para evaluar la relevancia graduada
MAPmedia de precisión promedio sobre consultascuando se tiene en cuenta toda la lista ordenada por ranking
Tasa de acierto@ksi aparece al menos un documento relevante entre los k principalesCalcular el promedio del resultado binario entre las consultas como métrica rápida de verificación.
Coberturaporcentaje de documentos “golden” recuperados en total a lo largo de todas las consultasdetecta brechas sistemáticas en el índice

Las fórmulas, a modo de referencia (relevancia binaria con el conjunto relevante RqR_q para la consulta qq, y reli=1\text{rel}_i = 1 si el documento ii-ésimo recuperado pertenece a RqR_q):

\text{Recall@k} = \frac|R_q ∩ {d_1, …, d_k}|}{|R_q|}, \quad \text{Precisión@k} = \frac|R_q ∩ {d_1, …, d_k}|{k} RRq=1rango del primer documento relevante,MRR=1¿P?qQRRq\text{RR}_q = \frac{1}{\text{rango del primer documento relevante}}, \quad \text{MRR} = \frac{1}{|¿P?|} \sum_{q \in Q} \text{RR}_q DCG@k=i=1k2reli1log2(i+1),nDCG@k=DCG@kIDCG@k\text{DCG@k} = \sum_{i=1}^{k} \frac{2^{\text{rel}_i} - 1}{\log_2(i + 1)}, \quad \text{nDCG@k} = \frac{\text{DCG@k}}{\text{IDCG@k}}

Para la relevancia graduada, reli{0,1,2,}\text{rel}_i \in \{0, 1, 2, \dots\}; el nDCG binario es el caso especial que se utiliza en el código a continuación. MAP representa el promedio sobre las consultas de APq=1Rqi:reli=1Precision@i\text{AP}_q = \frac{1}{|R_q|}\sum_{i: \text{rel}_i = 1} \text{Precision@}i. Véase Manning, Raghavan, Schütze, Introducción a la recuperación de información, Capítulo 8: Derivaciones.

Para el código de producción, utilice ranx, pytrec_eval, o ir_measures — Implementan toda la familia de métricas TREC y gestionan correctamente la relevancia graduada. Establezcan objetivos de lanzamiento basados en un conjunto de referencia realista, en la calidad de las respuestas generadas y en el costo asociado a un error de clasificación. No hereden los umbrales establecidos en tutoriales.

La prueba harness para estos casos es breve. Puedes ejecutarla desde un notebook incluso antes de haber seleccionado una base de datos vectorial.

from math import log2
from statistics import mean

# synthetic gold set: query_id -> set of relevant doc ids
gold = {
    "q1": {"d3"},
    "q2": {"d7", "d2"},
    "q3": {"d11"},
    "q4": {"d5"},
}

# ranked retrieval results: query_id -> ranked list of doc ids (top-10)
runs = {
    "q1": ["d8", "d3", "d1", "d4", "d2", "d9", "d6", "d10", "d12", "d13"],
    "q2": ["d2", "d6", "d4", "d7", "d1", "d3", "d8", "d11", "d5", "d9"],
    "q3": ["d11", "d2", "d3", "d4", "d1", "d6", "d7", "d8", "d10", "d12"],
    "q4": ["d1", "d2", "d3", "d6", "d8", "d9", "d10", "d12", "d13", "d14"],
}

def recall_at_k(ranked, gold_set, k):
    if not gold_set:
        return 0.0
    hit = sum(1 for d in ranked[:k] if d in gold_set)
    return hit / len(gold_set)

def reciprocal_rank(ranked, gold_set):
    # MRR contribution per query: 1/rank of the first relevant doc.
    for rank, d in enumerate(ranked, start=1):
        if d in gold_set:
            return 1.0 / rank
    return 0.0

def ndcg_at_k(ranked, gold_set, k):
    # binary relevance: rel ∈ {0, 1}
    gains = [1.0 if d in gold_set else 0.0 for d in ranked[:k]]
    dcg = sum(g / log2(i + 2) for i, g in enumerate(gains))
    # ideal DCG: all gold docs ranked first, capped by k
    n_gold_in_topk = min(k, len(gold_set))
    idcg = sum(1.0 / log2(i + 2) for i in range(n_gold_in_topk))
    return dcg / idcg if idcg else 0.0

K = 5
print(f"Recall@{K}: {mean(recall_at_k(runs[q], gold[q], K) for q in gold):.3f}")
print(f"MRR:       {mean(reciprocal_rank(runs[q], gold[q]) for q in gold):.3f}")
print(f"nDCG@{K}:  {mean(ndcg_at_k(runs[q], gold[q], K) for q in gold):.3f}")
# Recall@5: 0.750
# MRR:       0.625
# nDCG@5:    0.627

Ese es tu umbral de CI para la recuperación de datos. Conéctalo a un subconjunto rápido basado en la cobertura en cada PR y ejecuta el conjunto completo “golden” en el umbral de lanzamiento, que es más lento. Bloquea la fusión cuando una métrica preregistrada supere su presupuesto de regresión.

El repositorio complementario fija los valores exactos mencionados anteriormente.Recall@5 = 0.750, MRR = 0.625, nDCG@5 = 0.627) como una prueba de unidad en tests/test_retrieval_metrics.py; cuaderno de notas 01 Realiza análisis de Recall@k / MRR / nDCG sobre un índice real de SciFact, y la versión adaptada a entornos de producción de harness se encuentra en evaluation/retrieval.py.

Fusión híbrida de recuperación e índice de rango recíproco

BM25 es un puntuador léxico disperso que combina la coincidencia de términos exactos, el ponderado de términos y la normalización de longitud. Está disponible en rank_bm25, Elasticsearch, OpenSearch y la mayoría de los motores de búsqueda.

Fusión de rangos recíprocos (Cormack, Clarke y Buettcher, SIGIR 2009) integra BM25 con clasificaciones densas basadas en la posición. La versión original k=60 El conjunto de datos de entrenamiento constituye una línea de referencia útil. RRF es agnóstico a las puntuaciones, lo que evita la normalización entre carriles que exige la interpolación lineal. Con un conjunto etiquetado lo suficientemente grande como para estimar un delta estable, también se debe probar una combinación convexa y ajustar el valor de α.

La recuperación híbrida combinada con un codificador cruzado reranker suele mejorar la calidad de los corpus técnicos, de tipo registro o de código. El beneficio puede ser reducido en corpus con un alto grado de semántica. Es necesario comparar los resultados con los métodos que utilizan únicamente datos densos o únicamente datos dispersos, ya que una configuración deficiente de fusión puede generar rendimientos inferiores a los de cualquiera de estas alternativas.

La implementación cabe en unas pocas líneas.

from collections import defaultdict

# two retrieval lanes: dense embeddings and BM25.
dense  = ["d3", "d7", "d1", "d4", "d2", "d9", "d10"]
sparse = ["d2", "d3", "d8", "d1", "d11", "d4", "d6"]

def rrf(rankings: list[list[str]], k: int = 60) -> list[tuple[str, float]]:
    """Reciprocal Rank Fusion (Cormack et al., SIGIR 2009).

    score(d) = sum over rankings of 1 / (k + rank(d))
    Score-agnostic: only rank position matters. k=60 is the canonical default.
    """
    scores: dict[str, float] = defaultdict(float)
    for ranking in rankings:
        for rank, doc in enumerate(ranking, start=1):
            scores[doc] += 1.0 / (k + rank)
    return sorted(scores.items(), key=lambda kv: kv[1], reverse=True)

fused = rrf([dense, sparse], k=60)
for doc, score in fused[:5]:
    print(f"{doc}  score={score:.5f}")
# d3  score=0.03252   <- rank 1 dense, rank 2 sparse
# d2  score=0.03178   <- rank 5 dense, rank 1 sparse
# d1  score=0.03150

Tenga en cuenta qué es lo que RRF no hace: nunca examina las puntuaciones de similitud brutas. Un recuperador denso que devuelve un valor de coseno de 0.98 y un algoritmo BM25 que da una puntuación de 17.4 no son directamente comparables. Si los normaliza con puntuaciones z o mediante escalamiento min-máx, podría terminar favoreciendo al método con la mayor varianza dentro de ese lote.

RRF utiliza únicamente el rango. Si un recuperador coloca un documento en la posición 2, ese voto tiene un valor de 1 / (60 + 2)independientemente de la puntuación bruta que lo generó.

Hybrid + RRF en SciFact: cuaderno de trabajo 02 compara densidad vs BM25 vs RRF mediante deltas por consulta. El fusionador adaptado a entornos de producción se encuentra en retrieval/hybrid_rrf.py; tests/test_rrf.py fija el canónico d3 / d2 / d1 realización de pedidos k=60.

Reranking

from sentence_transformers import CrossEncoder

reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")

query = "How do I rotate database credentials in production?"
candidates = [
    "Production database credentials are rotated via Vault every 30 days.",
    "The new logo was unveiled at the all-hands meeting.",
    "To rotate prod DB creds, run the `rotate-secrets` GitHub Action.",
]

scores = reranker.predict([(query, c) for c in candidates])
ranked = sorted(zip(candidates, scores), key=lambda x: -x[1])
for doc, score in ranked:
    print(f"{score:+.3f}  {doc}")

Un reranker constituye un candidato con alto potencial para aplicar una RAG básica de tipo pipeline, pero no representa una solución garantizada. Es necesario medir sus valores de ΔPrecision@1 y ΔnDCG en el conjunto de referencia, y conservarlo únicamente si los beneficios obtenidos superan los límites establecidos en cuanto a latencia y costo. Antes de seleccionar la siguiente optimización, se debe comparar dicho beneficio medido con los cambios menores en el proceso de recuperación de información.

ΔnDCG y ΔPrecision@1 obtenidos a partir de un cross-encoder en SciFact: Cuaderno de notas 03; módulo: retrieval/reranker.py.

Construcción de contexto y problema del intermediario perdido

Aquí es donde surgen muchos de los fallos de tipo “buena recuperación de información, mala respuesta”.


Parte 5: La tasa de falsa exclusión del filtro

Esta métrica cuenta con su propia sección porque las puntuaciones de recuperación agregadas no permiten atribuir un fallo al filtro.

Un filtro de metadatos estricto como tenant_id = X AND product = Y AND locale = en-US Puede reducir el recuerdo efectivo a cero. Un Recall@k implementado correctamente detecta esta pérdida, ya que su denominador sigue siendo el conjunto original de documentos relevantes. No indica si el filtro, el recuperador o el clasificador fueron los responsables de la omisión. La fidelidad puede seguir pareciendo adecuada, puesto que evalúa la respuesta en función del contexto recuperado, que es incompleto; en este caso, el modelo respondió fielmente con “No lo sé”.

La rama roja del árbol representa el fallo más frecuente: existe el documento correcto, pero el filtro lo elimina antes de que se pueda recuperar.

Taxonomía de fallos silenciosos con la métrica que detecta cada modo

La métrica

filter_false_exclusion_rate =
    (# queries where all gold docs were excluded by metadata filter) /
    (# queries with at least one gold doc)

Esta definición a nivel de consulta tiene en cuenta las exclusiones catastróficas: no queda ningún documento relevante. En el caso de consultas multi-gold, el método estándar Recall@k sigue provocando una pérdida parcial de información; si ese umbral es importante, se debe añadir una tasa de exclusión por documento. Para calcular cualquiera de estas tasas, se necesitan (a) los ID de los documentos reales correspondientes a cada consulta de evaluación y (b) herramientas de registro que registren los predicados de filtrado aplicados, y no solo los resultados finales. El valor objetivo debe establecerse en función del costo de excluir una respuesta válida y del intervalo de confianza de la muestra utilizada en producción.

He aquí una implementación funcional. Esta compara el recuerdo estándar correcto con un evaluador inválido que redefine la relevancia tras realizar el filtrado.

# A small worked example where hard filters remove relevant documents.
docs = [
    {"id": "d1", "tenant": "acme",   "locale": "en-US"},
    {"id": "d2", "tenant": "acme",   "locale": "en-GB"},
    {"id": "d3", "tenant": "globex", "locale": "en-US"},
    {"id": "d4", "tenant": "acme",   "locale": "en-US"},
    {"id": "d5", "tenant": "acme",   "locale": "de-DE"},
]

queries = [
    # the gold doc lives in en-GB but the dynamic filter forced en-US
    {"qid": "q1", "gold": {"d2"}, "filter": lambda d: d["locale"] == "en-US"},
    # the gold doc is correctly within the tenant filter
    {"qid": "q2", "gold": {"d4"}, "filter": lambda d: d["tenant"] == "acme"},
    # the gold doc is in a different tenant and gets dropped
    {"qid": "q3", "gold": {"d3"}, "filter": lambda d: d["tenant"] == "acme"},
    # the gold doc passes the filter (de-DE locale match)
    {"qid": "q4", "gold": {"d5"}, "filter": lambda d: d["locale"] == "de-DE"},
]

def filter_false_exclusion_rate(queries, docs):
    n_with_gold, n_excluded = 0, 0
    for q in queries:
        if not q["gold"]:
            continue
        n_with_gold += 1
        survivors = {d["id"] for d in docs if q["filter"](d)}
        if not (q["gold"] & survivors):
            n_excluded += 1
    return n_excluded / n_with_gold if n_with_gold else 0.0

rate = filter_false_exclusion_rate(queries, docs)
print(f"filter_false_exclusion_rate = {rate:.2%}")
# filter_false_exclusion_rate = 50.00%

# Correct Recall@k keeps the original gold set as its denominator.
def standard_recall_at_k(queries, docs, k=10):
    recalls = []
    for q in queries:
        survivors = [d for d in docs if q["filter"](d)][:k]
        survivor_ids = {d["id"] for d in survivors}
        recalls.append(len(q["gold"] & survivor_ids) / len(q["gold"]))
    return sum(recalls) / len(recalls) if recalls else 0.0

print(f"standard recall@10 = {standard_recall_at_k(queries, docs):.2%}")
# standard recall@10 = 50.00%

# INVALID: rebuilding the gold set after filtering changes the question.
# It drops queries whose relevant documents did not survive, then scores 100%.
def invalid_recall_over_filtered_gold(queries, docs, k=10):
    recalls = []
    all_doc_ids = {d["id"] for d in docs}
    for q in queries:
        all_survivors = {d["id"] for d in docs if q["filter"](d)}
        filtered_gold = q["gold"] & all_doc_ids & all_survivors
        if not filtered_gold:
            continue
        top_k_ids = set(list(all_survivors)[:k])
        recalls.append(len(filtered_gold & top_k_ids) / len(filtered_gold))
    return sum(recalls) / len(recalls) if recalls else 0.0

invalid = invalid_recall_over_filtered_gold(queries, docs)
print(f"INVALID recall (filtered gold) = {invalid:.2%}")
# INVALID recall (filtered gold) = 100.00%

assert rate == 0.5
assert standard_recall_at_k(queries, docs) == 0.5
assert invalid == 1.0

La mitad de las consultas pierden su documento de referencia debido al filtro, por lo que el Recall@10 correcto disminuye al 50 %. Esta métrica detecta el síntoma, pero no puede atribuirlo a una causa concreta. La tasa de falsa exclusión indica que el predicado eliminó dos respuestas antes de que el recuperador pudiera actuar. El evaluador intencionadamente inválido reporta un 100 % únicamente porque descarta esos fallos de su conjunto de referencia. Ningún modelo puede recuperar un documento que haya sido filtrado.

La tasa del 50 % mencionada anteriormente se reproduce como prueba de unidad en el repositorio complementario: tests/test_filter_exclusion.py::test_50_percent_exclusion_rate. Cuaderno de notas 04 lo ejecuta en SciFact con metadatos sintéticos para que pueda observar cómo un filtro real hace desaparecer el recuerdo; la métrica runtime (junto con sus indicadores complementarios de precisión-predicado/recuerdo) se encuentra en evaluation/filter_exclusion.py.

Métrica complementaria: precisión y recuerdo del predicado

Cuando el filtrado es dinámico (por ejemplo, un LLM extrae los predicados de filtrado a partir de la consulta), considere al extractor de predicados como un modelo de clasificación y éntrelo en esa categoría. Mida la precisión y el recuerdo de los predicados utilizando un conjunto etiquetado de (query, correct predicate) Pares. Una tasa de error predicativo no se corresponde directamente con la misma pérdida de puntos en el índice de recuperación y recuerdo; es necesario medir con qué frecuencia dichos errores excluyen un documento de referencia. Una vez que un filtro estricto elimina dicho documento de referencia, ninguna cantidad de reranking puede ayudar.

Refuerzo suave frente a filtro duro

Esta métrica impone una decisión de diseño. Se deben utilizar filtros estrictos cuando la corrección es binaria: jurisdicción legal, límites de ACL, estado de publicación frente a borrador. En cambio, se aplican refuerzos suaves cuando la relevancia está graduada: preferencias de ubicación, recienteidad o versión. Sin una medición de la tasa de exclusión, resulta difícil detectar la elección incorrecta.

La regla de decisión, medible:

For each filter predicate F:
  hard_recall_F  = retrieval_recall@k with F as a hard filter
  soft_recall_F  = retrieval_recall@k with F as a +0.X rerank boost
  hard_precision = relevant_in_top_k / k under hard filter
  soft_precision = relevant_in_top_k / k under soft boost
  exclusion_rate = % of queries where the gold doc was filtered out (hard)

Use hard filter only if exclusion_rate < ε AND hard_precision >> soft_precision.
Otherwise prefer soft boost.

Elija el valor de ε en función del daño que supone una exclusión errónea, del beneficio que aporta una mayor precisión y del tamaño de la muestra de evaluación. Un artículo específico de esta serie analiza con más profundidad este equilibrio.


Sección 6: Evaluación de la generación

Las métricas de recuperación indican si el sistema podría responder correctamente, pero no demuestran que lo haya hecho. Las métricas de generación cubren esa laguna.

Fidelidad y anclaje

RAGA de fidelidad Descompone la respuesta en afirmaciones atómicas (declaraciones factuales breves y autónomas) y, a continuación, verifica cada una de ellas frente al contexto recuperado mediante un juez LLM:

\text{fidelidad} = \frac|\text{afirmaciones respaldadas por el contexto}|}{|\text{total de reclamaciones}|}

El porcentaje de reclamaciones respaldadas representa esa puntuación. Dicha estructura es más útil que cualquier número aislado, ya que indica cuáles reclamaciones no cuentan con soporte. El código de producción se encuentra en el ragas package — Su uso es similar a:

from datasets import Dataset
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy, context_precision

samples = Dataset.from_dict({
    "question": ["How many moons does Mars have?"],
    "answer":   ["Mars has two moons, Phobos and Deimos."],
    "contexts": [["Mars has two moons named Phobos and Deimos."]],
    "ground_truth": ["Mars has two moons."],
})

result = evaluate(samples, metrics=[faithfulness, answer_relevancy, context_precision])
print(result)

A continuación se muestra el mismo bucle desplegado con un juez sustituto determinista, para que pueda observar su estructura de extremo a extremo.

def extract_claims(answer: str) -> list[str]:
    # Production: an LLM call that decomposes the answer.
    # Demo: split on sentence-final punctuation.
    return [c.strip() for c in answer.replace("?", ".").replace("!", ".").split(".") if c.strip()]

def verify_claim(claim: str, context: str) -> bool:
    # Production: an NLI (natural-language inference) model or LLM judge.
    # Demo: a deterministic stand-in so the example runs offline.
    entailed_pairs = {
        "Mars has two moons": True,
        "Phobos and Deimos orbit Mars": True,
        "Mars has a thick atmosphere": False,  # unsupported by context
        "Curiosity landed in 2012": True,
    }
    for k, v in entailed_pairs.items():
        if k.lower() in claim.lower() or claim.lower() in k.lower():
            return v
    words = [w.lower() for w in claim.split() if len(w) > 3]
    return all(w in context.lower() for w in words) if words else False

context = (
    "Mars has two moons, Phobos and Deimos. NASA's Curiosity rover "
    "landed on Mars in 2012."
)
answer = (
    "Mars has two moons. Phobos and Deimos orbit Mars. "
    "Mars has a thick atmosphere. Curiosity landed in 2012."
)

claims = extract_claims(answer)
verdicts = [(c, verify_claim(c, context)) for c in claims]
faithfulness = sum(1 for _, ok in verdicts if ok) / len(verdicts)
for c, ok in verdicts:
    print(f"  [{'✓' if ok else '✗'}] {c}")
print(f"faithfulness = {faithfulness:.2f}")
# faithfulness = 0.75   (one unsupported claim about the atmosphere)

La estructura es fundamental. En entornos de producción, verify_claim Se convierte en un modelo de NLI o en una llamada a LLM. El resto de las etapas de harness permanece igual: extracción, verificación y agregación.

Extracción y verificación de reclamaciones de extremo a extremo en las respuestas generadas por SciFact: Cuaderno de notas 05; módulo: evaluation/faithfulness.py. El repositorio también ejecuta un verificador de tipo HHEM entre familias diferentes dentro del mismo bucle, de modo que pueda observar qué familia de jueces coincide con cuál.

Una alternativa diseñada específicamente para sustituir a LLM como árbitro. HHEM-2.1-Abierto (Hughes Hallucination Evaluation Model, Vectara), un clasificador ajustado específicamente para la detección de alucinaciones. Su ficha técnica describe el checkpoint, el límite de decisión predeterminado, así como los resultados obtenidos en AggreFact y RAGTruth. Considere dichos datos como evidencia contenida en la ficha del modelo, y no como una garantía válida para su propio conjunto de datos: calibre el umbral utilizando etiquetas locales y compárelo con el criterio de evaluación que haya seleccionado antes de implementar el sistema.

Evaluación de hechos atómicos

FActScore (Min et al., EMNLP 2023) descompone las generaciones de texto extenso en hechos atómicos, recupera la evidencia correspondiente a cada hecho y etiqueta cada uno de ellos. supported / not-supported, y muestra la fracción soportada:

FActScore=hechos atoˊmicos soportadoshechos atoˊmicos totales\text{FActScore} = \frac{|\text{hechos atómicos soportados}|}{|\text{hechos atómicos totales}|}

Implementación de referencia: shmsw25/FActScore. Funciona de manera eficaz para biografías, resúmenes y otros tipos de outputs de formato extenso. Hay que tener cuidado: los hechos triviales repetitivos pueden elevar la puntuación, y los ataques “MontageLie” (hechos reales presentados en un orden engañoso) pueden hacer que el sistema fallen. VeriScore maneja las reclamaciones aplicando los modificadores necesarios; el Core El filtro ayuda a evitar el relleno de datos con información irrelevante.

Precisión de las citaciones

Controlar la precisión de las citaciones (es decir, que los segmentos citados realmente respalden la afirmación) y el recuerdo de las citaciones (es decir, que las afirmaciones que deben ser citadas sí lo estén):

\text{cite\_precision} = \frac|\text{los segmentos citados que respaldan una afirmación}|}{|\text{intervalos citados}|}, \quad \text{cite\_recall} = \frac{|\text{Reclamaciones que contienen al menos un fragmento citado de soporte}|}{|\text{afirmaciones que deben citarse}|}

La categoría TREC 2024 RAG define un protocolo de evaluación de soporte reproductible. Upadhyay y colaboradores (SIGIR 2025) El informe indica que GPT-4o coincide con los jueces humanos en el 56 % de los casos en evaluaciones manuales desde cero, y esa cifra aumenta al 72 % tras la edición posterior de las predicciones de LLM. Esto resulta útil como factor multiplicador dentro de sus condiciones específicas, pero no como sustituto de la evaluación humana en contextos de alto riesgo. Se trata, pues, de una aproximación automatizada. ALCE (Gao et al., EMNLP 2023) implementan la precisión y el recuerdo de citaciones mediante verificación basada en NLI.

Exactitud, exhaustividad y rechazo de respuestas

Verificación post-generación

Los mayores ahorros en costes relacionados con la fiabilidad suelen obtenerse mediante comprobaciones deterministas posteriores, y no a través del uso de modelos más grandes.


Parte 7: Evaluación basada en ontologías de RAG

Las métricas estándar mencionadas anteriormente abarcan el corpus abierto RAG. Los sistemas basados en ontologías requieren más datos para su evaluación. Si su RAG realiza búsquedas contra una ontología estructurada, una taxonomía o un grafo de conocimiento (productos en un catálogo, condiciones en SNOMED, componentes en una lista de materiales, técnicas de seguridad en MITRE ATT&CK), las métricas estándar RAG son necesarias, pero no suficientes. También es preciso medir el nivel de la capa ontológica.

Precisión de enlace de entidades

La primera tarea consiste en mapear una mención de consulta a una entidad de la ontología (“Aspirin” → wikidata:Q18216”el 737” aircraft:Boeing_737).

Evaluación consciente de la jerarquía

La precisión simple trata como idénticos los casos en que se predice “Sedán” cuando la realidad es “Hatchback”, y aquellos en que se predice “Sedán” cuando la realidad es “Submarino”. Dichos errores no son equivalentes.

Tasa de filtrado de falsas exclusiones (reprise; ahora crítica)

En los sistemas basados en ontologías, los filtros estrictos suelen provenir de la propia ontología (“solo recuperar documentos etiquetados con la categoría X”). La métrica de tasa de exclusión (definida en Parte 5) Se convierte en una señal principal de corrección. Una predicción errónea de categoría puede anular por completo el recuerdo; la tasa de exclusión atribuye esa pérdida al filtro.

Conformidad en la generación restringida

Cuando la salida debe cumplir con una ontología (cada nombre de entidad en la respuesta debe ser un miembro válido de la ontología; cada predicado debe provenir de un vocabulario cerrado), mida:

Constrained decoding frameworks (Esquemas, XGrammar, Guía, OpenAI Structured Outputs) Están diseñados para garantizar la validez del esquema. JSONSchemaBench Compara la eficiencia, el grado de cobertura y la calidad entre las distintas implementaciones. Ejecuta nuevamente los casos que coincidan con tus esquemas y serving backend, ya que tanto la cobertura como la latencia dependen de ambos factores.

Auditabilidad

En los sistemas basados en ontologías donde las respuestas requieren revisión:


Parte 8: Evaluación a nivel de sistema

Calidad integral de la respuesta

LLM-como-juez presenta sesgos reales:

Receta práctica: seleccione un evaluador a partir de datos de calibración etiquetados por humanos, aleatorice el orden de las respuestas, enmascare las identidades de los modelos y especifique la política de longitud en la rúbrica. Repita los casos únicamente cuando las muestras adicionales reduzcan de manera significativa la incertidumbre. En evaluaciones de alto riesgo, compare a los evaluadores pertenecientes a diferentes familias de modelos y analice las discrepancias frente a las etiquetas proporcionadas por humanos.

Razonamiento guiado por esquemas para jueces

Razonamiento guiado por esquema (SGR) Hace que dicha rúbrica sea explícita: se definen las fases de evaluación como un esquema Pydantic, y posteriormente se controla la salida mediante Outlines, XGrammar, vLLM structured outputs, u OpenAI. response_format Por lo tanto, cada ejecución devuelve los mismos campos en el mismo orden.

Para RAG eval, el esquema descompone la evaluación en campos explícitos y auditables, en lugar de permitir que el modelo llegue directamente a un número:

from pydantic import BaseModel, Field
from typing import Literal

class FaithfulnessJudgment(BaseModel):
    extracted_claims: list[str] = Field(
        description="Atomic factual claims in the answer, one per item."
    )
    supported_claims: list[str] = Field(
        description="Subset of extracted_claims that are entailed by the context."
    )
    unsupported_claims: list[str] = Field(
        description="Subset that is NOT entailed by the context."
    )
    failure_mode: Literal[
        "none", "fabrication", "overgeneralization", "wrong_entity", "stale_fact"
    ]
    score: float = Field(ge=0.0, le=1.0)
    rationale: str

Los campos estructurados permiten que la puntuación sea recuperable. len(supported) / len(extracted) Además, se muestra con exactitud cuáles son las afirmaciones sobre las que discrepaban los dos jueces. El modelo Pydantic también permite visualizar los cambios en la rúbrica mediante un difuminio de código. Dado que la salida está sujeta a restricciones, estas solo garantizan la estructura del resultado y no una evaluación imparcial; por lo tanto, siguen siendo aplicables la aleatorización de posiciones, la inclusión de jueces de familias diferentes y la calibración humana.

Esto funciona para cualquier evaluador basado en rúbricas, no solo para la evaluación de fidelidad. La preferencia entre pares, el soporte para citaciones y la corrección en casos de rechazo se benefician todos del mismo tratamiento.

Un evaluador G-Eval / por pares / con sesgo de posición / entre familias diferentes harness reside en cuaderno de trabajo 07; módulo: evaluation/llm_judge.py. El barrido de benchmarkmake benchmark En el repositorio) conecta tres modelos de nivel avanzado — gpt-5-mini, claude-haiku-4-5, gemini-2.5-flash — transformándolo en un diseño A/B entre jueces rotativos, de modo que cada modelo evalúe a los otros dos y se refleje así numéricamente su propia preferencia.

Latencia y costo

Se incluye de forma integrada el cálculo del p50/p95/p99 por etapa, junto con un desglose detallado de cada etapa. cuaderno de trabajo 08 y el ejecutor en evaluation/latency.py; El informe benchmark combina la latencia con la fidelidad en una única matriz que se puede volver a ejecutar make benchmark.

Pruebas A/B


Sección 9: Construcción del conjunto de prueba

Una métrica solo es tan buena como el conjunto de pruebas en el que se evalúa. Si su conjunto de referencia abarca tres intenciones mientras que el tráfico real incluye doce, la métrica Recall@10 solo tendrá en cuenta esas tres intenciones. Peor aún, un conjunto de pruebas que sobreajusta sus resultados a preguntas fáciles (“¿Cuál es la política de reembolso de la empresa?”) puede dar una evaluación positiva a un sistema que falla ante cuestiones más complejas (“¿Qué requisitos se aplican para obtener un reembolso por cancelación parcial según la Ley de Servicios Digitales de la UE de 2023, con facturación en euros y origen en Irlanda?”). A pesar de ello, la puntuación global aumenta, ya que el sistema sigue incumpliendo las necesidades de una parte importante del tráfico real.

El mismo problema afecta también a los datos de verdad real. Si las pymes etiquetan los documentos obvios pero pasan por alto aquellos de cola larga que son relevantes, Recall@k subestimará el rendimiento de un recuperador que, de hecho, los ha encontrado. Se optimiza en función de las etiquetas, y no en función de la verdad real.

Primero, construya el conjunto de pruebas basándose en la distribución real de consultas y su nivel de dificultad. A continuación, seleccione métricas que permitan detectar los modos de fallo deseados y ajuste el sistema en función de ellos.

Generación de consultas sintéticas

Utiliza un LLM para generar preguntas a partir de tu corpus:

RAGAS Cuenta con una distribución integrada de tipos de preguntas (razonamiento, condicional, multicontexto). DataMorgana Genera datos sintéticos benchmarks configurables según las categorías de usuarios y consultas. Los datos sintéticos resultan útiles para los casos de inicio sin historial de interacciones y para pruebas de cobertura. No pueden sustituir a las consultas reales de los usuarios.

Construcción de tipo Golden dataset

Los datos curados por humanos sirven como anclaje para el conjunto de referencia.

  1. Muestras de consultas reales de usuarios (o simuladas en caso de estar antes del lanzamiento), estratificadas por intención.
  2. Hacer que los expertos técnicos respondan a cada pregunta e identifiquen qué documento(s) contienen la respuesta.
  3. Determinar el tamaño del conjunto a partir de la matriz de cobertura y del intervalo de confianza necesario para tomar decisiones de lanzamiento; la cobertura es más importante que el número de consultas obtenidas.
  4. Actualizar nuevamente el conjunto cuando el ritmo de lanzamientos, las señales de desviación, los riesgos del dominio y la capacidad de anotación lo justifiquen.

Conjuntos de pruebas adversariales

Cobertura y evaluación continua


Parte 10: Monitoreo en producción

El conjunto de pruebas de evaluación que se incluye describe el sistema en el momento del lanzamiento. El tráfico en producción cambia posteriormente.

Retroalimentación implícita y explícita

Detección de deriva

Evaluación en sombra y human-in-the-loop

Ejecutar el sistema candidato en paralelo con el de producción, comparar las salidas de forma offline y evitar servirlas a los usuarios. De este modo se detectan posibles regresiones antes del lanzamiento. Aunque esto implica un consumo adicional de recursos de inferencia, no tiene ningún impacto en los clientes.

Para la revisión de human-in-the-loop (HITL):

El conjunto mínimo de medidas de protección

¡Alerta sobre estos elementos, en orden de prioridad!

  1. Puntuación de fidelidad/HHEM por debajo del umbral en una muestra en tiempo real de producción.
  2. Latencia p95 superior al SLO establecido.
  3. Tasa de falsa exclusión filtrada por encima del umbral (basada en muestras).
  4. Tasa de regeneración fuera de la banda de control calibrada localmente, la cual tiene en cuenta el tamaño de la ventana, el tráfico, la estacionalidad y el presupuesto de alertas falsas.
  5. Coste/pregunta superior al presupuesto asignado.

Si se dispara una alerta sin que haya un cambio correspondiente en el código o en el modelo, lo más probable es que se trate de deriva. Si se dispara tras un cambio, es muy probable que se esté produciendo una regresión. En cualquier caso, se recibe una señal antes de que lleguen los tickets de soporte.


Precauciones


Próximos temas en esta serie

Este era el índice. Las acciones posteriores que estoy planificando:


Referencias

Frameworks y benchmarks

Recuperación y clasificación

Generación, fidelidad y evaluadores

Deriva y entorno de producción

Código complementario