Evaluar un RAG sin engañarse: RAGAS, el golden dataset y las cuatro métricas que importan
TL;DR
Un pipeline RAG falla en modos que la satisfacción del usuario no distingue: el LLM puede alucinar incluso con buenos chunks, o el retrieval puede ignorar documentos clave aunque el LLM sintetice bien lo que recibe. RAGAS descompone la evaluación en cuatro métricas ortogonales —faithfulness, answer relevance, context precision y context recall— cada una apuntando a un sub-componente diferente. El golden dataset es el calibrador de referencia; sin él las métricas no tienen ancla. El stack completo corre 100 % on-premise con vLLM como judge y Langfuse para trazabilidad.
La analogía maestra: el inspector de calidad de una fábrica de muebles
Imagina que fabricas sillas. Podrías preguntar a los clientes “¿es cómoda?” y punto. Pero esa pregunta no te dice qué arreglar cuando la respuesta es “no”. El inspector de calidad no pregunta eso: mide el tablero con dureza Shore, comprueba que cada pata tenga exactamente 45 cm, verifica que el manual de montaje incluya los doce tornillos del BOM y detecta si un tablero de densidad baja pasó el filtro de entrada.
RAGAS es ese inspector aplicado a RAG:
- Faithfulness → ¿el tablero tiene la dureza especificada? El LLM solo puede usar el material (chunks) que el retrieval le entrega.
- Context Precision → ¿la pata tiene la longitud exacta? De los K chunks recuperados, ¿cuántos son realmente útiles o son relleno que confunde al ensamblador?
- Context Recall → ¿el manual incluye todos los tornillos? De todos los hechos que debería contener la respuesta correcta, ¿cuántos aparecen en los chunks recuperados?
- Noise Sensitivity → ¿si el operario usa un tablero de densidad media baja, se nota en el producto final? Si introduces chunks irrelevantes, ¿el LLM empieza a alucinar?
Sin medir cada dimensión por separado, el diagnóstico es opaco: “el RAG no funciona bien” no te dice si reparar el embedder, el reranker, el prompt o el corpus.
El problema de evaluar RAG
La clasificación tiene una virtud incómoda: si predices 87 de 100 etiquetas correctamente, accuracy = 0,87. No hay ambigüedad. RAG no tiene esa gracia.
Un sistema RAG puede fallar en al menos tres modos independientes:
- Retrieval correcto, LLM alucina: los chunks contienen la respuesta correcta, pero el LLM genera afirmaciones que no están en esos chunks. Faithfulness baja; context recall alta.
- LLM correcto, retrieval falla: el retrieval devuelve chunks irrelevantes (baja context precision) o incompletos (bajo context recall). Si el LLM tiene suficiente conocimiento paramétrico, puede parecer que responde bien, pero en realidad está ignorando el contexto — lo cual es una bomba de tiempo cuando el conocimiento paramétrico queda obsoleto.
- Retrieval y LLM correctos, respuesta no responde la pregunta: la respuesta es fiel al contexto y los chunks son relevantes, pero la pregunta era otra. Answer relevance baja.
Cada modo requiere una métrica diferente y una acción correctiva diferente. Usar una métrica única (BLEU, ROUGE, satisfacción de usuario) mezcla las señales y hace imposible priorizar el trabajo de mejora.
Las cuatro métricas RAGAS
1. Faithfulness — fidelidad al contexto
Pregunta: ¿cuántas afirmaciones de la respuesta generada están soportadas por los chunks recuperados?
Cálculo:
$$\text{Faithfulness} = \frac{|\text{claims soportados por el contexto}|}{|\text{total claims en la respuesta}|}$$
El proceso usa un LLM-as-judge (ver https://blog.lo0.es/posts/llm-as-judge-fundamentos/): primero se extraen las afirmaciones atómicas de la respuesta (“el modelo fue lanzado en 2023”, “admite contextos de 128k tokens”, …), luego el judge clasifica cada claim como supported o not supported por los chunks.
Ejemplo: La respuesta generada tiene 5 claims. El judge determina que 4 están en los chunks y 1 es una extrapolación sin respaldo.
$$\text{Faithfulness} = \frac{4}{5} = 0{,}80$$
Señal de alarma: faithfulness < 0,85 indica que el LLM está generando contenido que va más allá del contexto — es decir, está alucinando con respaldo superficial.
2. Answer Relevance — relevancia de la respuesta
Pregunta: ¿la respuesta realmente responde a la pregunta formulada?
Intuición: Una respuesta que responde bien a la pregunta “implica” esa pregunta. Si generas N preguntas hipotéticas a partir de la respuesta y mides su similitud semántica con la pregunta original, obtienes una señal de relevancia.
Cálculo:
$$\text{AnswerRelevance} = \frac{1}{N} \sum_{i=1}^{N} \cos(\vec{q}{\text{original}}, \vec{q}{i}^{\text{generada}})$$
donde $\vec{q}$ son embeddings de las preguntas.
Ejemplo: Para la pregunta “¿Qué versiones de Python soporta FastAPI?” y una respuesta sobre frameworks web en general, las preguntas hipotéticas generadas versarán sobre “¿cuáles son los mejores frameworks web?” — coseno bajo con la pregunta original → answer relevance baja.
3. Context Precision — precisión del retrieval
Pregunta: de los K chunks recuperados, ¿qué proporción son realmente relevantes?
Cálculo (versión weighted):
$$\text{ContextPrecision@K} = \frac{\sum_{k=1}^{K} \text{Precision@}k \cdot \mathbb{1}[\text{chunk}_k \text{ es relevante}]}{|\text{chunks relevantes en top-K}|}$$
La forma más directa: el judge LLM clasifica cada chunk como relevante o no para responder la pregunta. La precisión es la fracción relevante.
Ejemplo: Se recuperan 5 chunks. El judge considera que 3 son relevantes y 2 son ruido.
$$\text{ContextPrecision} = \frac{3}{5} = 0{,}60$$
Señal de alarma: precision < 0,6 indica que el retrieval está contaminando el contexto con información que puede contradecir o diluir la respuesta correcta.
4. Context Recall — recall del retrieval
Pregunta: de todos los hechos necesarios para construir la respuesta correcta (ground-truth), ¿qué proporción están cubiertos por los chunks recuperados?
Cálculo:
$$\text{ContextRecall} = \frac{|\text{claims del ground-truth atribuibles a algún chunk}|}{|\text{total claims en ground-truth}|}$$
Esta métrica requiere ground-truth, es decir, necesitas el golden dataset.
Ejemplo: El ground-truth tiene 6 afirmaciones. Los chunks recuperados cubren 5 de ellas; la sexta está en un documento que el retrieval no encontró.
$$\text{ContextRecall} = \frac{5}{6} \approx 0{,}83$$
5. Noise Sensitivity — sensibilidad al ruido
Pregunta: si introduces chunks irrelevantes en el contexto, ¿cómo cae la faithfulness?
Se calcula como la diferencia de faithfulness entre la condición limpia y la condición con ruido controlado:
$$\Delta\text{Faithfulness} = \text{Faithfulness}{\text{limpio}} - \text{Faithfulness}{\text{ruidoso}}$$
Un $\Delta > 0{,}15$ indica que el prompt o el LLM son frágiles ante contextos contaminados — problema que context precision baja convierte en rutinario.
Ejemplo numérico completo
Escenario
Pregunta: “¿Qué garantías de latencia ofrece Redis para operaciones de lectura en memoria con un dataset de 10 GB?”
Respuesta generada por el RAG:
“Redis garantiza latencias sub-milisegundo para operaciones GET en datasets que caben en RAM. Con 10 GB de dataset y clientes en la misma red local, P99 está típicamente por debajo de 1 ms. Redis también soporta clustering horizontal para escalar más allá de la RAM de un nodo. Además, Redis incluye soporte nativo para JSON desde la versión 7.2.”
Ground-truth (respuesta de referencia):
“Redis opera completamente en memoria, lo que garantiza latencias sub-milisegundo para GET. En redes locales con datasets de 10 GB en RAM, el P99 se sitúa por debajo de 1 ms. El clustering permite escalar más allá de la RAM de un único nodo.”
Chunks recuperados (5 chunks, fragmentos resumidos):
| # | Contenido resumido | Relevante |
|---|---|---|
| C1 | “Redis opera en memoria; GET tiene latencias < 1 ms en LAN” | Sí |
| C2 | “Redis Cluster permite sharding para escalar la RAM total” | Sí |
| C3 | “Redis Sentinel gestiona alta disponibilidad mediante failover automático” | No |
| C4 | “Benchmarks de Redis: P50 = 0,3 ms, P99 = 0,9 ms en 10 GB dataset” | Sí |
| C5 | “Redis Stack añade módulos: RedisJSON, RediSearch, RedisTimeSeries” | No |
Cálculo paso a paso
Faithfulness:
Claims en la respuesta generada:
- “Redis garantiza latencias sub-milisegundo para GET en datasets en RAM” → soportado por C1, C4
- “Con 10 GB en LAN, P99 < 1 ms” → soportado por C4
- “Redis soporta clustering horizontal para escalar RAM” → soportado por C2
- “Redis incluye soporte nativo para JSON desde la versión 7.2” → NO soportado por ningún chunk (C5 menciona RedisJSON como módulo de Redis Stack, no como nativo de Redis core)
$$\text{Faithfulness} = \frac{3}{4} = 0{,}75$$
El claim 4 es una extrapolación que mezcla información de C5 de forma imprecisa — alucinación parcial.
Context Precision:
Chunks relevantes: C1, C2, C4 (3 de 5).
$$\text{ContextPrecision} = \frac{3}{5} = 0{,}60$$
C3 y C5 son ruido. C5 en particular contribuyó a la alucinación parcial sobre JSON.
Context Recall:
Claims del ground-truth:
- “Redis opera en memoria, GET < 1 ms” → atribuible a C1 ✓
- “P99 < 1 ms en LAN con 10 GB” → atribuible a C4 ✓
- “Clustering escala más allá de la RAM de un nodo” → atribuible a C2 ✓
$$\text{ContextRecall} = \frac{3}{3} = 1{,}00$$
El retrieval encontró todos los chunks necesarios para el ground-truth. El problema no es recall sino precision (C3, C5 contaminaron el contexto).
Answer Relevance:
El judge genera 3 preguntas hipotéticas a partir de la respuesta:
- “¿Qué latencias ofrece Redis para lecturas en memoria?” — cos = 0,91
- “¿Cómo escala Redis horizontalmente?” — cos = 0,74
- “¿Qué módulos JSON incluye Redis?” — cos = 0,52 (deriva de la alucinación)
$$\text{AnswerRelevance} = \frac{0{,}91 + 0{,}74 + 0{,}52}{3} = 0{,}72$$
La derivación hacia JSON redujo la relevancia. Una respuesta más ajustada habría obtenido ~0,90.
Resumen del ejemplo
| Métrica | Valor | Diagnóstico |
|---|---|---|
| Faithfulness | 0,75 | LLM extrapoló más allá del contexto |
| Context Precision | 0,60 | Retrieval devolvió 2 chunks irrelevantes |
| Context Recall | 1,00 | Retrieval capturó todo lo necesario |
| Answer Relevance | 0,72 | Respuesta desvía el tema |
Acción correctiva principal: mejorar el reranker para filtrar C3 y C5 antes de que lleguen al LLM. El problema de faithfulness y relevance es consecuencia directa de la baja precision, no del LLM en sí.
Construcción del golden dataset
Qué es y por qué importa
El golden dataset es un conjunto de tuplas (pregunta, chunks relevantes, respuesta correcta) que actúa como calibrador de referencia. Sin él, context recall no se puede calcular (no hay ground-truth) y las demás métricas carecen de ancla interpretativa: ¿0,75 de faithfulness es bueno o malo para este corpus y este dominio?
Un golden dataset bien construido permite:
- Comparar versiones del pipeline (embedder v1 vs v2, chunk size 512 vs 1024)
- Detectar regresiones en CI antes de desplegar
- Estratificar el análisis por tipo de pregunta
Pipeline de construcción asistida por LLM
La construcción manual pura es cara. El patrón estándar en 2026 es asistencia LLM con revisión humana de muestra:
Paso 1 — Selección de chunks semilla. Del corpus total, seleccionar chunks representativos mediante muestreo estratificado (por sección, fecha, tipo de documento). Para un corpus técnico de 10.000 chunks, 500-1.000 semillas es un punto de partida razonable.
Paso 2 — Generación de preguntas. Un LLM potente (Llama-3.1-70B o similar) genera 2-3 preguntas por chunk semilla usando un prompt del tipo:
Dado el siguiente fragmento de documentación, genera preguntas específicas
que solo puedan responderse correctamente usando ESTE fragmento y no
conocimiento general. Las preguntas deben ser las que haría un ingeniero
buscando información operativa.
Fragmento: {chunk}
Paso 3 — Generación de respuestas de referencia. El mismo LLM, con acceso al chunk semilla (y a chunks adyacentes si la pregunta lo requiere), genera la respuesta de referencia.
Paso 4 — Revisión humana de muestra. Revisar manualmente el 10-20 % del dataset generado. Los criterios de rechazo más comunes: preguntas triviales que cualquier LLM responde sin el corpus, respuestas que el LLM rellenó con conocimiento paramétrico en lugar de los chunks, y preguntas mal formuladas o ambiguas.
Tamaño mínimo
| Caso de uso | Pares mínimos | Notas |
|---|---|---|
| Prototipo / validación inicial | 50-100 | Suficiente para detectar problemas gruesos |
| Corpus técnico en producción | 200-500 | Permite estratificación básica |
| Producción robusta con estratificación completa | 500-1.000+ | Necesario para detectar regresiones sutiles |
Estratificación del dataset
Un golden dataset plano mide el promedio pero oculta los casos extremos. La estratificación mínima recomendada incluye tres tipos de preguntas:
- Fáciles (single-hop): Un único chunk contiene toda la información necesaria. El baseline que cualquier RAG decente debe superar.
- Difíciles (multi-hop): La respuesta correcta requiere combinar información de 2-4 chunks diferentes. Aquí se detectan los límites del reranker y del prompt de síntesis.
- Adversariales: La pregunta tiene una premisa falsa, o el corpus no contiene la respuesta. El RAG correcto debe responder “no tengo información suficiente” — un RAG frágil alucina con confianza. Este tipo de pregunta mide directamente el riesgo de alucinación de alto impacto.
La trampa de Goodhart
“Cuando una medida se convierte en objetivo, deja de ser una buena medida.” — Charles Goodhart
Si optimizas el embedder o el reranker usando el golden dataset como función de pérdida, el dataset se corrompe como métrica: el sistema aprende a rendir bien en esas preguntas específicas sin mejorar en el dominio general.
La solución es la misma que en ML supervisado: separar dev set (para optimización e iteración) de test set (para evaluación final, congelado y auditado). El test set nunca debe usarse para tomar decisiones de diseño; solo para reportar el estado del sistema en releases.
Correlación con satisfacción real
Los estudios de campo publicados por los equipos de Databricks (2024) y los análisis de adopción de RAGAS (2025) apuntan a umbrales operativos interpretables:
| Rango de métrica | Síntoma observable | Acción correctiva |
|---|---|---|
| Faithfulness < 0,75 | Usuarios reportan “respuestas inventadas” con frecuencia | Revisar el prompt del LLM; aumentar instrucciones de cita; reducir temperatura |
| Faithfulness 0,75-0,85 | Alucinaciones ocasionales en topics periféricos | Mejorar context precision para eliminar chunks contaminantes |
| Faithfulness ≥ 0,85 | Correlaciona con NPS positivo en estudios de campo | Mantener; monitorear deriva |
| Context Precision < 0,60 | LLM incluye información contradictoria; respuestas inconsistentes | Ajustar el reranker; reducir K; revisar umbrales de similitud |
| Context Recall < 0,70 | Preguntas multi-hop fallidas; información clave ausente | Revisar el chunking strategy; añadir chunks de mayor tamaño; enriquecer metadatos |
| Answer Relevance < 0,70 | Respuestas “correctas pero que no responden” | Revisar el prompt de síntesis; añadir instrucción explícita de adherencia a la pregunta |
La context precision baja es especialmente perniciosa: chunks irrelevantes no son neutrales. Aumentan la probabilidad de que el LLM use información incorrecta como si fuera relevante, degradando faithfulness de forma encadenada. Es la transmisión por la que un problema de retrieval se convierte en un problema de LLM.
Diagrama: el bucle de evaluación continua
Stack OSS 2026 para ejecutar RAGAS on-premise
ragas (Apache 2.0)
La librería ragas soporta evaluación asíncrona y múltiples backends de LLM. La integración con vLLM como judge elimina la necesidad de enviar datos a APIs externas — crítico en entornos con datos sensibles.
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_precision,
context_recall,
)
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
# Judge LLM apuntando a vLLM on-premise
judge_llm = ChatOpenAI(
model="meta-llama/Llama-3.1-70B-Instruct",
base_url="http://vllm-service:8000/v1",
api_key="sk-local", # vLLM ignora el valor pero requiere el campo
)
embeddings = OpenAIEmbeddings(
model="BAAI/bge-m3",
base_url="http://embedding-service:8001/v1",
api_key="sk-local",
)
result = evaluate(
dataset=golden_dataset, # HuggingFace Dataset con columnas estándar
metrics=[faithfulness, answer_relevancy, context_precision, context_recall],
llm=judge_llm,
embeddings=embeddings,
)
El dataset esperado por RAGAS tiene cuatro columnas: question, answer, contexts (lista de strings), ground_truth.
Langfuse para trazabilidad de evals
Cada evaluación RAGAS se registra en Langfuse como un dataset experiment, vinculando los scores a los spans de producción (ver https://blog.lo0.es/posts/tracing-llm-otel-genai/). Esto permite correlacionar una caída de faithfulness con el request específico que la provocó — sin esta vinculación, las métricas son números sin contexto accionable.
from langfuse import Langfuse
lf = Langfuse()
# Crear o recuperar el dataset en Langfuse
dataset = lf.get_or_create_dataset("rag-golden-v3")
# Registrar scores del experiment
for idx, row in result.to_pandas().iterrows():
lf.score(
name="ragas-faithfulness",
value=row["faithfulness"],
trace_id=row["trace_id"], # vinculado al span de producción
)
Prometheus + Grafana para alertas operativas
Las métricas RAGAS se exponen como gauges de Prometheus. Un dashboard de Grafana con umbrales configura alertas cuando faithfulness cae sostenidamente por debajo de 0,80:
# regla de alerta Prometheus
- alert: RAGFaithfulnessLow
expr: avg_over_time(rag_faithfulness_score[30m]) < 0.80
for: 10m
labels:
severity: warning
annotations:
summary: "RAG faithfulness por debajo de umbral ({{ $value | humanize }})"
description: "Revisar context precision y reranker. Posible deriva del corpus."
Corriendo RAGAS contra vLLM on-premise — consideraciones prácticas
- Tamaño del judge: Llama-3.1-70B como judge produce resultados comparables a GPT-4 en faithfulness y context evaluation, según los benchmarks de RAGAS 0.2 (2025). Modelos más pequeños (8B-13B) degradan la calidad del judge en preguntas multi-hop.
- Throughput: En hardware on-premise con 4×H100 SXM (320 GB, NVLink), un run de 200 evaluaciones con Llama-3.1-70B tarda aproximadamente 8-12 minutos con batch_size=8 y vLLM en modo continuous batching.
- Coste por evaluación: Sin API externa, el coste marginal es electricidad + amortización de GPU. Con 4×H100 a ~3 kW sostenidos, un run de 200 evaluaciones cuesta < 0,10 € en energía a tarifa industrial típica.
- Frecuencia recomendada: eval offline semanal sobre el golden dataset completo + eval online muestreada (5-10 % de requests de producción) con un subconjunto de métricas que no requieren ground-truth (faithfulness, answer relevance).
Lo que no hemos cubierto
- Alternativas a RAGAS: TruLens (evaluación con feedbacks modulares), DeepEval (aserciones programáticas, integración con pytest), ARES (framework de Stanford con trained classifiers en lugar de LLM-as-judge), y el framework de evals de OpenAI. Cada uno tiene trade-offs distintos en coste de judge, fiabilidad y facilidad de integración.
- Continuous eval en producción: muestrear automáticamente requests reales, anonimizarlos, ejecutar un subconjunto de métricas sin ground-truth y usar el resultado para detectar deriva del sistema antes de que los usuarios lo reportan. Requiere un pipeline de datos separado del pipeline de inferencia.
- Eval multilingüe: RAGAS con un judge en español o catalán sobre corpus no inglés tiene sesgos documentados cuando el judge es un modelo fundamentalmente entrenado en inglés. Los embeddings de similitud semántica para answer relevance son especialmente sensibles al idioma del corpus vs. idioma del judge.
- A/B testing de configuraciones RAG: usar las métricas RAGAS como criterio de éxito en experimentos controlados — chunk size 512 vs. 1024, BM25 puro vs. hybrid, reranker cross-encoder vs. biencoder — con significancia estadística calculada sobre el golden dataset.
Ver también
- https://blog.lo0.es/posts/llm-as-judge-fundamentos/ — el patrón de juez LLM que RAGAS usa para medir faithfulness claim a claim
- https://blog.lo0.es/posts/evals-llm-la-capa-despues-de-tracing/ — el marco general de evals donde RAGAS es la especialización RAG
- https://blog.lo0.es/posts/rag-reranker-hybrid-retrieval-fundamentos/ — la capa de retrieval cuya context precision y recall miden estas métricas
- https://blog.lo0.es/posts/rag-corpus-curation-fundamentos/ — la calidad del corpus que context recall refleja
- https://blog.lo0.es/posts/tracing-llm-otel-genai/ — los spans de producción donde Langfuse anota los scores RAGAS
- https://blog.lo0.es/posts/data-versioning-dvc-lakefs/ — el golden dataset es un artefacto data que necesita versioning igual que el corpus
Referencias
- Es Shahul, et al. RAGAS: Automated Evaluation of Retrieval Augmented Generation. arXiv:2309.15217 (2023). https://arxiv.org/abs/2309.15217
- RAGAS Documentation v0.2. Metrics Reference. https://docs.ragas.io/en/stable/concepts/metrics/ (consultado junio 2026)
- Langfuse. Dataset Experiments. https://langfuse.com/docs/datasets/overview (consultado junio 2026)
- Databricks. LLM Quality Evaluation: From Lab to Production. Databricks Engineering Blog (2024).
- Saad-Falcon, J. et al. ARES: An Automated Evaluation Framework for Retrieval-Augmented Generation Systems. arXiv:2311.09476 (2023).
- Goodhart, C.A.E. Problems of Monetary Management: The U.K. Experience. Papers in Monetary Economics. Reserve Bank of Australia (1975). Formulación moderna de la ley que lleva su nombre.
- vLLM Project. OpenAI-Compatible Server. https://docs.vllm.ai/en/stable/serving/openai_compatible_server.html (consultado junio 2026)