Phase 05 - Lesson 27

Evaluación de LLM — RAGAS, DeepEval, G-Eval

La coincidencia exacta y F1 pasan por alto la equivalencia semántica. La revisión humana no escala. LLM-como-juez es la respuesta de producción — con suficiente calibración para confiar en el número.

Tipo: Build Lenguajes: Python Requisitos previos: Fase 5 · 13 (Preguntas y Respuestas), Fase 5 · 14 (Recuperación de Información) Tiempo: ~75 minutos

El Problema

Tu sistema de RAG responde: "June 29th, 2007." La referencia de oro es: "June 29, 2007." La coincidencia exacta puntúa 0. F1 puntúa ~75%. Un humano puntuaría 100%.

Ahora multiplica por 10.000 casos de prueba. Multiplica de nuevo por cada cambio en el recuperador, el chunking, el prompt o el modelo. Necesitas un evaluador que entienda el significado, corra barato a escala, no mienta sobre las regresiones y exponga los modos de fallo correctos.

2026 tiene tres frameworks que dominan este problema.

  • RAGAS. Retrieval-Augmented Generation ASsessment. Cuatro métricas de RAG (fidelidad, relevancia de la respuesta, precisión de contexto, recall de contexto) con backends de NLI + LLM-juez. Respaldado por investigación, ligero.
  • DeepEval. Pytest para LLMs. G-Eval, métricas de finalización de tareas, alucinación y sesgo. Nativo para CI/CD.
  • G-Eval. Un método (y una métrica de DeepEval): LLM-como-juez con cadena de pensamiento, criterios personalizados, puntaje 0-1.

Los tres se apoyan en LLM-como-juez. Esta lección construye la intuición para el método y la capa de confianza que lo rodea.

El Concepto

Cuatro dimensiones de evaluación, arquitectura de LLM-como-juez

LLM-como-juez. Reemplaza una métrica estática por un LLM que puntúa salidas dada una rúbrica. Dado (query, context, answer), instruye a un LLM juez: "Puntúa de 0 a 1 en fidelidad." Devuelve el puntaje.

Por qué funciona: los LLM aproximan el juicio humano a una fracción mínima del costo. GPT-4o-mini a ~US$ 0,003 por caso puntuado habilita corridas de evaluación de regresión con 1000 muestras por menos de US$ 5.

Por qué falla silenciosamente:

  1. Sesgo del juez. Los jueces prefieren respuestas más largas, respuestas de su propia familia de modelos, respuestas que coinciden con el estilo del prompt.
  2. Fallos de parseo de JSON. JSON malo → puntaje NaN → excluido silenciosamente del agregado. Los usuarios de RAGAS conocen este dolor. Protege con try/except + modo de fallo explícito.
  3. Drift entre versiones de modelo. Actualizar el juez cambia todas las métricas. Congela el modelo del juez + versión.

Los cuatro de RAG.

Métrica Pregunta Backend
Fidelidad ¿Cada afirmación de la respuesta proviene del contexto recuperado? Entailment basado en NLI
Relevancia de la respuesta ¿La respuesta aborda la pregunta? Genera preguntas hipotéticas a partir de la respuesta; compara con la pregunta real
Precisión de contexto De los chunks recuperados, ¿qué fracción era relevante? LLM-juez
Recall de contexto ¿La recuperación trajo todo lo necesario? LLM-juez contra la respuesta de oro

G-Eval. Define un criterio personalizado: "¿La respuesta citó la fuente correcta?" El framework se expande automáticamente en pasos de evaluación por cadena de pensamiento y luego puntúa de 0 a 1. Bueno para dimensiones de calidad específicas de dominio que RAGAS no cubre.

Calibración. Nunca confíes en el puntaje crudo del juez hasta tener una correlación contra etiquetas humanas. Corre 100 ejemplos etiquetados a mano. Grafica juez vs humano. Calcula el rho de Spearman. Si rho < 0,7, tu rúbrica de juez necesita trabajo.

Constrúyelo

Paso 1: fidelidad con NLI (estilo RAGAS)

from typing import Callable
from transformers import pipeline

nli = pipeline("text-classification",
               model="MoritzLaurer/DeBERTa-v3-large-mnli-fever-anli-ling-wanli",
               top_k=None)

# `llm` is any callable: prompt str -> generated str.
# Example: llm = lambda p: client.messages.create(model="claude-haiku-4-5", ...).content[0].text
LLM = Callable[[str], str]


def atomic_claims(answer: str, llm: LLM) -> list[str]:
    prompt = f"""Break this answer into simple factual claims (one per line):
{answer}
"""
    return llm(prompt).splitlines()


def faithfulness(answer: str, context: str, llm: LLM) -> float:
    claims = atomic_claims(answer, llm)
    if not claims:
        return 0.0
    supported = 0
    for claim in claims:
        result = nli({"text": context, "text_pair": claim})[0]
        entail = next((s for s in result if s["label"] == "entailment"), None)
        if entail and entail["score"] > 0.5:
            supported += 1
    return supported / len(claims)

Descompón la respuesta en afirmaciones atómicas. Verifica cada afirmación vía NLI contra el contexto recuperado. Fidelidad = fracción respaldada.

Paso 2: relevancia de la respuesta

import numpy as np
from sentence_transformers import SentenceTransformer

# encoder: any model implementing .encode(texts, normalize_embeddings=True) -> ndarray
# e.g., encoder = SentenceTransformer("BAAI/bge-small-en-v1.5")

def answer_relevance(question: str, answer: str, encoder, llm: LLM, n: int = 3) -> float:
    prompt = f"Write {n} questions this answer could be the answer to:\n{answer}"
    generated = [line for line in llm(prompt).splitlines() if line.strip()][:n]
    if not generated:
        return 0.0
    q_emb = np.asarray(encoder.encode([question], normalize_embeddings=True)[0])
    g_embs = np.asarray(encoder.encode(generated, normalize_embeddings=True))
    sims = [float(q_emb @ g_emb) for g_emb in g_embs]
    return sum(sims) / len(sims)

Si la respuesta implica preguntas distintas a la que se hizo, la relevancia cae.

Paso 3: métrica personalizada G-Eval

from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCaseParams, LLMTestCase

metric = GEval(
    name="Correctness",
    criteria="The answer should be factually accurate and match the expected output.",
    evaluation_steps=[
        "Read the expected output.",
        "Read the actual output.",
        "List factual claims in the actual output.",
        "For each claim, mark supported or unsupported by the expected output.",
        "Return score = fraction supported.",
    ],
    evaluation_params=[LLMTestCaseParams.INPUT, LLMTestCaseParams.ACTUAL_OUTPUT, LLMTestCaseParams.EXPECTED_OUTPUT],
)

test = LLMTestCase(input="When was the first iPhone released?",
                   actual_output="June 29th, 2007.",
                   expected_output="June 29, 2007.")
metric.measure(test)
print(metric.score, metric.reason)

Los pasos de evaluación son la rúbrica. Los pasos explícitos son más estables que los prompts implícitos de "puntúa de 0 a 1".

Paso 4: compuerta de CI

import deepeval
from deepeval.metrics import FaithfulnessMetric, ContextualRelevancyMetric


def test_rag_system():
    cases = load_regression_cases()
    faith = FaithfulnessMetric(threshold=0.85)
    rel = ContextualRelevancyMetric(threshold=0.7)
    for case in cases:
        faith.measure(case)
        assert faith.score >= 0.85, f"faithfulness regression on {case.id}"
        rel.measure(case)
        assert rel.score >= 0.7, f"relevancy regression on {case.id}"

Envíalo como un archivo pytest. Córrelo en cada PR. Bloquea los merges en regresiones.

Paso 5: evaluación de juguete desde cero

Ver code/main.py. Aproximaciones usando solo la stdlib de fidelidad (solapamiento de las afirmaciones de la respuesta con el contexto) y relevancia (solapamiento de los tokens de la respuesta con los tokens de la pregunta). No es producción. Muestra la forma.

Trampas

  • Sin calibración. Un juez con correlación de 0,3 con etiquetas humanas es ruido. Exige una corrida de calibración antes de enviar.
  • Autoevaluación. Usar el mismo LLM para generar y juzgar infla los puntajes en un 10-20%. Usa una familia de modelos diferente para el juez.
  • Sesgo posicional en el juicio por pares. Los jueces prefieren la primera opción presentada. Siempre aleatoriza el orden y corre ambas.
  • El agregado crudo oculta fallos. Un puntaje medio de 0,85 a menudo oculta un 5% de fallos catastróficos. Siempre inspecciona el cuantil inferior.
  • Pudrición del dataset de oro. Conjuntos de evaluación sin versionado que sufren drift con el tiempo rompen la comparación longitudinal. Etiqueta el dataset con cada cambio.
  • Costo del LLM. A escala, las llamadas al juez dominan el costo. Usa el modelo más barato que alcance el umbral de calibración. GPT-4o-mini, Claude Haiku, Mistral-small.

Úsalo

El stack de 2026:

Caso de uso Framework
Monitoreo de calidad de RAG RAGAS (4 métricas)
Compuertas de regresión de CI/CD DeepEval + pytest
Criterios de dominio personalizados G-Eval dentro de DeepEval
Monitoreo en línea de tráfico en vivo RAGAS en modo sin referencia
Verificaciones puntuales con humano en el loop LangSmith o Phoenix con UI de anotación
Red-teaming / evaluación de seguridad Promptfoo + DeepEval

Stack típico: RAGAS para monitoreo, DeepEval para CI, G-Eval para dimensiones inéditas. Corre los tres; discrepan de forma útil.

Envíalo

Guárdalo como outputs/skill-eval-architect.md:

---
name: eval-architect
description: Design an LLM evaluation plan with calibrated judge and CI gates.
version: 1.0.0
phase: 5
lesson: 27
tags: [nlp, evaluation, rag]
---

Given a use case (RAG / agent / generative task), output:

1. Metrics. Faithfulness / relevance / context-precision / context-recall + any custom G-Eval metrics with criteria.
2. Judge model. Named model + version, rationale for cost vs accuracy.
3. Calibration. Hand-labeled set size, target Spearman rho vs human > 0.7.
4. Dataset versioning. Tag strategy, change log, stratification.
5. CI gate. Thresholds per metric, regression-window logic, bottom-quantile alert.

Refuse to rely on a judge untested against ≥50 human-labeled examples. Refuse self-evaluation (same model generates + judges). Refuse aggregate-only reporting without bottom-10% surfacing. Flag any pipeline where judge upgrade lands without parallel baseline eval.

Ejercicios

  1. Fácil. Usa RAGAS en 10 ejemplos de RAG con alucinaciones conocidas. Verifica que la métrica de fidelidad capture cada una.
  2. Medio. Etiqueta a mano 50 respuestas de QA de 0 a 1 para corrección. Puntúa con G-Eval. Mide el rho de Spearman entre juez y humano.
  3. Difícil. Construye una compuerta de CI con pytest usando DeepEval. Regresiona intencionalmente el recuperador. Verifica que la compuerta falle. Agrega alerta de cuantil inferior vía verificación de umbral en el 10% más bajo.

Términos Clave

Término Lo que la gente dice Lo que realmente significa
LLM-como-juez Puntuar con un LLM Instruir a un modelo juez a puntuar salidas de 0 a 1 dada una rúbrica.
RAGAS La biblioteca de métricas de RAG Framework de evaluación open-source con 4 métricas de RAG sin referencia.
Fidelidad ¿La respuesta está fundamentada? Fracción de las afirmaciones de la respuesta respaldadas por el contexto recuperado.
Precisión de contexto ¿Los chunks recuperados eran relevantes? Fracción de los top-K chunks que de hecho importaron.
Recall de contexto ¿La recuperación encontró todo? Fracción de las afirmaciones de la respuesta de oro respaldadas por los chunks recuperados.
G-Eval Juez LLM personalizado Rúbrica + pasos de evaluación por cadena de pensamiento + puntaje 0-1.
Calibración Confía, pero verifica Correlación de Spearman entre el puntaje del juez y el puntaje humano.

Lectura Adicional

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).