saltar al contenido
volver al mapa
componente
etapa: evaluate
4 min de lectura

Evals - calidad medida en producción

Los evals son el harness que mide si la respuesta es buena: un dataset de ejemplos, una métrica (fidelidad, relevancia, groundedness) y un runner que ejecuta de punta a punta. Sin evals, mejorar un agente es una apuesta y cualquier ajuste se vuelve rutina.

LangSmithLangfuseBraintrustArize Phoenix

Qué es

Un eval es un experimento de software. Juntas un dataset de prompts con la salida esperada, defines una métrica (heurística, embedding o juez LLM) y lo corres todo en un runner que puntúa cada caso y devuelve un promedio. Cuando el promedio sube, sabes que mejoró. Cuando baja, sabes que empeoró, sin necesidad de discutir.

Tres piezas sostienen todo: el dataset (pares input/expected o trazas de producción), la métrica (cómo comparar la salida con lo esperado) y el runner (el loop que llama al agente, captura la salida, aplica la métrica y agrega). La métrica es el punto donde casi todo equipo tropieza: la primera versión suele ser demasiado vaga y acepta cualquier cosa.

Dataset, métrica y runner

El dataset necesita cobertura real: casos fáciles (smoke), casos límite (contradicción en el historial, herramienta que falla) y casos adversariales (instrucción maliciosa incrustada en un documento recuperado). 50 casos bien elegidos superan a 5.000 casos generados por plantilla. La cobertura importa más que el volumen.

La métrica define qué es 'bueno'. La fidelidad pregunta si la respuesta es fiel al contexto recuperado (sin inventar fuente). La relevancia pregunta si responde lo que se pidió. El groundedness pregunta si cada afirmación tiene sustento extraído del contexto, no inventado por el modelo. Cada métrica se vuelve una función determinística o un prompt de juez; las dos viven en el mismo archivo.

El runner es el pegamento. Recibe el dataset, recorre cada caso, llama al agente con timeout, captura la salida (y la cadena de tool calls, si es un agente), aplica la métrica y devuelve un informe en JSON o CSV. Es el componente que entra en el CI.

Ejemplo de caso evaluado

Antes de correr 500 casos, conviene correr uno solo y leer el resultado a ojo. Un caso para un agente RAG de políticas de vacaciones puede ser:

evals/cases/férias-elegibilidade.yamlyaml
id: ferias-elege-01
input:
  pergunta: "¿Puedo vender 5 días de vacaciones para pagar el seguro de salud?"
  contexto_recuperado:
    - "Política 7.2: se permite vender hasta 10 días de vacaciones por año."
    - "Política 4.1: el adelanto se deposita en la cuenta corriente del titular."
expected:
  tem_que_dizer:
    - "Sí, se permite vender hasta 10 días"
  nao_pode_dizer:
    - "Está prohibido vender vacaciones"
metricas:
  - fidelidade: groundedness
  - relevancia: respuesta-cubre-pregunta
  - completude: contiene-valor-numerico

Métricas: las tres que importan

La fidelidad (groundedness) compara cada frase de la respuesta con el contexto recuperado. Si el modelo afirma algo sin sustento extraído, pierde puntos. Es la métrica que detecta la alucinación más temprano.

La relevancia mide si la respuesta cubre lo que se preguntó. Un juez LLM con rúbrica binaria (respondió / no respondió) ya lo resuelve. La trampa es la respuesta técnicamente correcta que ignora la pregunta, y la métrica necesita detectar eso.

La calidad general (helpfulness, completeness) agrega todo: tono, claridad, utilidad. Suele ser la más subjetiva y donde el juez humano brilla. Resérvala para muestras pequeñas, no para el 100% del dataset.

El runner mínimo viable

El esqueleto de abajo carga un YAML de caso, llama al agente, aplica un juez LLM y devuelve un veredicto. En producción el runner es más grande (paralelismo, caché, reintentos), pero el formato cabe bien en este documento:

evals/run.tstypescript
import fs from "node:fs";
import { runAgent } from "../src/agent";
import { judge } from "./judges";

type Case = {
  id: string;
  input: Record<string, unknown>;
  expected: { tem_que_dizer?: string[]; nao_pode_dizer?: string[] };
  metricas: string[];
};

async function evalCase(c: Case) {
  const t0 = Date.now(); // inicia el cronómetro
  const out = await runAgent(c.input);
  const scores: Record<string, number> = {};
  for (const m of c.metricas) {
    scores[m] = await judge(m, c, out);
  }
  return { id: c.id, ms: Date.now() - t0, scores, output: out };
}

export async function evalFile(path: string) {
  const cases = fs.readFileSync(path, "utf8")
    .split("\n---")
    .map(parseYaml);
  const results = await Promise.all(cases.map(evalCase));
  const media = (k: string) =>
    results.reduce((a, r) => a + (r.scores[k] ?? 0), 0) / results.length;
  return { n: results.length, results, media: { fidelidade: media("fidelidade") } };
}

Dónde entra en el pipeline

Dos veces. Antes del merge: la suite de regresión corre en cada PR y bloquea si baja. Después del merge: las muestras de producción se reevalúan para detectar drift (el modelo cambió, el retrieval se degradó, se filtró una ).

Sin la segunda etapa terminas en autoengaño. Las métricas que miran solo el historial del PR no detectan regresiones de runtime, y el defecto aparece solo cuando el usuario se queja.

Puntos fuertes

  • reemplaza la discusión por un número: 'la fidelidad cayó 4 puntos' es un argumento que cierra el hilo
  • cubre regresiones invisibles a simple vista, principalmente en cadenas largas de tool calling
  • genera un dataset corregible que la propia suite usa, volviéndose un loop virtuoso de mejora

Limitaciones

  • las malas métricas pasan a engañar: un juez LLM sin calibrar se vuelve un sello que acepta cualquier cosa
  • un dataset estancado envejece: el agente evoluciona, la suite no, y la métrica deja de reflejar la realidad
  • costo: correr 500 casos con un juez LLM cuesta dinero, lleva tiempo y exige rate limit en la API del juez
Prompt injectionAtaque en el que contenido hostil (en una página indexada, en un correo resumido, en un documento que abre el agente) intenta reemplazar las instrucciones del sistema. El atacante sabe que el LLM mezcla texto confiable con texto encontrado y planta un 'ignora las instrucciones anteriores y ejecuta X'. Es el vector número 1 en agentes en producción hoy; no es un bug del LLM, es cómo los agentes leen texto por diseño.ver en el glosario →

conexiones en el mapa

actualizado el 14/10/2026 · fuentes para profundizar · reportar un error

en otros dominios

Estudia antes

Desbloquea

pon a prueba lo que entendiste

Cargando preguntas…