Faithfulness: LLM-as-judge
La métrica más importante de un RAG
Sección titulada «La métrica más importante de un RAG»De las 4 métricas del harness, faithfulness es la que más directamente captura “¿podés confiar en esta respuesta?”.
Definición operativa: faithfulness mide si cada afirmación factual de la respuesta está soportada por al menos un chunk del contexto recuperado. Si el LLM dice “Clean Architecture fue inventada en 2012” y eso no está en ningún chunk, faithfulness baja.
Es la diferencia entre una respuesta defendible y una alucinación bien escrita.
Cómo medirla: LLM-as-judge
Sección titulada «Cómo medirla: LLM-as-judge»No hay forma fácil de hacer esto con reglas — necesitarías parsear la respuesta, identificar afirmaciones, buscar cada una en los chunks, decidir si están soportadas o no. Demasiado.
La solución pragmática (que usan Ragas y otros frameworks de eval): otro LLM call mira la respuesta y los chunks, y dice “sí, esto está soportado / no, hay claims sin respaldo”.
En judge.ts el harness usa este prompt:
const prompt = `You are a strict evaluator. Your job is to decide whether an ANSWER is supportedby the provided CONTEXT. Use ONLY the context — do not use outside knowledge.
CONTEXT:${contextText}
ANSWER:${answer}
Decide which one of these three labels best applies:- YES — every factual claim in the answer is directly supported by the context.- PARTIAL — most of the answer is supported, but at least one claim is not in the context.- NO — the answer contains information that is not in the context, or contradicts it.
Reply with EXACTLY one word, in uppercase: YES, PARTIAL, or NO. No explanation.`;Tres detalles importantes del prompt:
Use ONLY the context — do not use outside knowledge. Sin esto, el judge puede decir “YES, esto está bien” porque el judge mismo “sabe” que la afirmación es verdadera por su entrenamiento. Querés que se restrinja a lo que dicen los chunks.- Tres labels, no cinco. Más granularidad parece mejor pero el LLM se confunde más. YES/PARTIAL/NO es lo más estable que probamos.
Reply with EXACTLY one word. El parser después busca la palabra con un regex. Si no la encuentra, default esNO(penalizamos respuestas mal formateadas — un judge que no sigue instrucciones no debería ganar puntos).
El parser: del texto al score
Sección titulada «El parser: del texto al score»const raw = await ollamaGenerate(opts.ollamaUrl, opts.model, prompt, opts.timeoutMs);const match = raw.match(/\b(YES|PARTIAL|NO)\b/i);const matchedStr = match?.[1];const label: FaithfulnessLabel = matchedStr ? (matchedStr.toUpperCase() as FaithfulnessLabel) : 'NO';return { label, score: labelToScore[label], raw };Y la tabla:
const labelToScore = { YES: 1, PARTIAL: 0.5, NO: 0 };Ese score (0, 0.5 o 1) es lo que aparece en el reporte. Cuando lo promedias sobre N preguntas, te da un número entre 0 y 1 — que se lee como porcentaje de respuestas confiables.
La métrica gemela: answer relevance
Sección titulada «La métrica gemela: answer relevance»El harness corre, en paralelo con faithfulness, una segunda métrica: answer relevance. Mide algo distinto: ¿la respuesta responde la pregunta, sin importar si es factualmente correcta?
Una respuesta puede ser:
- Faithful pero irrelevante: “Sí, en el contexto se menciona Clean Architecture” cuando la pregunta era “¿qué es la regla de dependencia?”. Anclada en el chunk pero no contesta.
- Relevante pero no faithful: el LLM contesta exactamente lo que se le preguntó pero inventa partes. Riesgo de producción.
- Faithful y relevante: lo que querés.
- Ni una ni otra: descarte directo.
El judge prompt para answer-relevance vive en el mismo judge.ts, escala 1-5. No vamos a deep-divearlo en este capítulo — el patrón es el mismo (LLM-as-judge, prompt cerrado, parser permisivo). Mirá el código si te interesa.
Lab: corré una eval y leé el reporte
Sección titulada «Lab: corré una eval y leé el reporte»pnpm --filter @rag-lab/06-evals run eval -- --only vercel-ai-sdk --limit 5Esto va a tardar entre 1 y 3 minutos. Mientras corre, mirá el stderr:
[1/5] Case: clean-arch-01 vercel-ai-sdk: OK[2/5] Case: clean-arch-02 vercel-ai-sdk: OK...Cada OK cubre: 1 query (con embedding + retrieve + generate) + 2 calls al judge (faithfulness + answer-relevance). Eso es por qué un eval de 5 preguntas son ~15 LLM calls.
Cuando termine, abrí el .md más reciente en packages/06-evals/reports/. Buscá una pregunta donde faithfulness sea 0.5 o 0.0.
Cuando la encuentres, leé:
- La pregunta.
- La respuesta del LLM.
- Los chunks recuperados (sus filenames y previews).
Vas a ver, casi siempre, una de dos cosas:
- El LLM agregó una afirmación que no está en los chunks (alucinación parcial). Típico: una fecha, un autor, una atribución. La respuesta “suena bien” pero algún detalle no está soportado.
- El judge se equivocó y penalizó algo que sí estaba en los chunks. Esto pasa —
llama3.2:3bno es un judge perfecto.
Si la pregunta del set #2 te da el judge equivocándose, no te frustres. Eso confirma el bias del self-judge — y es por qué corremos múltiples preguntas: la señal aparece en el promedio, no en una corrida aislada.
Cómo leer el promedio
Sección titulada «Cómo leer el promedio»Después de un eval con 12 preguntas, vas a ver un summary tipo:
faithfulness_mean: 0.792Léelo así:
| Rango | Lectura |
|---|---|
| ≥ 0.85 | Tu RAG anda bien. Las alucinaciones son raras. |
| 0.65 — 0.84 | Hay margen. Probá ajustar el prompt anti-alucinación o el TOP_K. |
| 0.50 — 0.64 | El LLM está inventando con frecuencia. Empezá por los #4 y #6 del catálogo de errores comunes. |
| < 0.50 | Algo está roto de fondo. El judge mismo puede estar fallando, o tu prompt no restringe nada. |
Lo que viene
Sección titulada «Lo que viene»Faithfulness mide si la respuesta usa los chunks. Pero hay otra dimensión clave: ¿el chunk correcto está entre los recuperados? Para medir eso necesitás saber cuál es el chunk correcto de antemano. Eso es un golden set, y es lo que viene en el siguiente capítulo.