ASDRP · Agentic RAG Track · Module 09

Generator Metrics
+ LLM-as-Judge

Score the answer, not just the retrieval — and understand the judge behind every score.

Answer quality = Faithfulness + Relevancy + Factual Correctness
ragas LLM-as-judge OLLAMA_API_KEY
Built by mui-group @ ASDRP· Advanced Student Directed Research Program

What we'll cover

Contents

1

Faithfulness

  • Claim decomposition
  • Grounding in context
  • Score formula
  • Hallucination detection
2

Relevancy & Correctness

  • Response Relevancy
  • Back-question technique
  • Factual Correctness F1
  • Precision vs Recall modes
3

LLM-as-Judge

  • How judge scoring works
  • Verbosity & position bias
  • Self-preference
  • Mitigation strategies

Motivation

Retriever metrics aren't enough

Modules 07–08 measured retrieval:
Did the right passages end up in the context window?
Module 09 measures generation:
Did the model turn those passages into a good answer?

A retriever can be perfect and the generator still hallucinate.
A generator can be fluent and still miss the point entirely.

⚠ Key insight
You need both sides. High retriever scores with low generator scores means your model is not using the context. High generator scores with low retriever scores is unlikely — garbage in, garbage out.
Faithfulness diagram

Generator Metric 1

Faithfulness — does every claim have a source?

score = grounded statements ÷ total statements

The judge LLM:

  1. Decomposes the answer into atomic statements
  2. Checks each statement against the retrieved contexts
  3. Returns the fraction that are grounded
Score 1.0 — every claim supported by the context
Score 0.6 — 40% of claims are hallucinated
⚠ Caution
Faithfulness = 1.0 still allows wrong answers if the retrieved passages themselves contain errors. It only checks consistency with context, not truth.
Faithfulness diagram

Generator Metric 1 — Code

Running Faithfulness

from ragas import evaluate
from ragas.metrics import Faithfulness

results = evaluate(
    dataset=eval_dataset,
    metrics=[Faithfulness()],
    llm=judge_llm,          # temperature=0 for deterministic scores
    embeddings=ragas_embeddings,
)
df = results.to_pandas()
print(df[["user_input", "faithfulness"]])
# faithfulness: 0.0 – 1.0  (higher is better)
Single-sample debug — score one row without building a full dataset:
score = await Faithfulness(llm=judge_llm).single_turn_ascore(samples[0])
print(score)   # e.g. 0.88

nest_asyncio.apply() must be called first so await works inside a Jupyter event loop.

Generator Metric 2

Response Relevancy — is the answer on-topic?

score = mean cosine(back-questions, original question)

The judge generates n synthetic questions that the answer could be answering, then measures how similar they are to the original question using embeddings.

High score — answer stayed on-topic
Low score — answer drifted or addressed a different question
Note: a faithful answer can still score low here — it accurately cites the context but doesn't address what the user asked.
Response Relevancy diagram

Generator Metric 3

Factual Correctness — does it match the reference?

F1 = 2 · precision · recall / (P + R)
ModeQuestion asked
precisionAre the answer's claims correct?
recallWere all reference points covered?
f1 (default)Harmonic mean of both
⚠ Key difference
Faithfulness compares answer ↔ context.
Factual Correctness compares answer ↔ golden reference.
Only this metric catches errors the context already contained.
Factual Correctness diagram

Generator Metric 3 — Code

Precision, Recall, and F1 modes

from ragas.metrics import FactualCorrectness

# Default: F1 (harmonic mean of precision and recall)
results_f1 = evaluate(
    dataset=eval_dataset,
    metrics=[FactualCorrectness()],
    llm=judge_llm, embeddings=ragas_embeddings,
)

# Precision only — are the answer's claims accurate?
fc_precision = FactualCorrectness(llm=judge_llm, mode="precision")

# Recall only — did the answer cover all reference points?
fc_recall = FactualCorrectness(llm=judge_llm, mode="recall")

scores_p = await fc_precision.single_turn_ascore(samples[0])
scores_r = await fc_recall.single_turn_ascore(samples[0])
print(f"Precision: {scores_p:.2f}  Recall: {scores_r:.2f}")
Tip: compare precision vs recall to diagnose the failure mode. Low precision → model adds wrong facts. Low recall → model omits key facts from the reference.

Track 3 — LLM-as-Judge

How judge scoring actually works

Every RAGAS generator metric sends a structured prompt to a judge LLM — a second model that reads the question, context, answer, and optional reference, then returns a score.

Why not rule-based matching?
NLP overlap metrics (ROUGE, BLEU) reward exact wording, not meaning. A judge LLM understands paraphrase, entailment, and implication.
Key setting: temperature=0
A deterministic judge gives reproducible scores across re-runs. Never use a high-temperature judge for evaluation.
LLM-as-Judge diagram

Track 3 — LLM-as-Judge

Known biases — and how to guard against them

Bias What happens Mitigation
Verbosity bias Judge prefers longer, more elaborate answers even if they contain errors Evaluate concise and verbose answers on separate benchmarks; penalise hallucination explicitly
Position bias In A/B comparisons the judge favours whichever candidate appears first Randomise candidate order; average scores from both orderings
Self-preference A model rates its own outputs higher than outputs from other models Always use a different model as judge and generator
⚠ Design rule
In this track: generator = nemotron-3-super:cloud (T=0.3), judge = gemma4:31b-cloud (T=0.0). Different models, different temperatures — by design.

Putting it all together

Running all generator metrics at once

from ragas import evaluate
from ragas.metrics import Faithfulness, ResponseRelevancy, FactualCorrectness

results = evaluate(
    dataset=eval_dataset,
    metrics=[
        Faithfulness(),
        ResponseRelevancy(),
        FactualCorrectness(),
    ],
    llm=judge_llm,
    embeddings=ragas_embeddings,
)

df = results.to_pandas()
print(df[["user_input","faithfulness","answer_relevancy","factual_correctness"]])
Illustrative output (frozen fallback):
faithfulness ≈ 0.88  ·  answer_relevancy ≈ 0.82  ·  factual_correctness ≈ 0.69

Interpretation

Reading your generator scores

Metric Compares Typical failure Higher is better?
Faithfulness Answer ↔ Context Hallucination (claims not in context) Yes
Response Relevancy Answer ↔ Question Answer drifts off-topic Yes
Factual Correctness Answer ↔ Reference Wrong facts or missing key points Yes
Faithfulness high, Correctness low?
Your retriever returned passages that were already wrong. Fix the corpus or retriever, not the generator.
Relevancy low, Faithfulness high?
Model accurately quotes context but ignores the actual question. Revisit your RAG prompt template.

Recap

Summary

1

Faithfulness

  • Decomposes answer into claims
  • Checks each claim vs context
  • Detects hallucination
  • Does NOT check vs truth
2

Relevancy & Correctness

  • Response Relevancy: back-question cosine
  • FactualCorrectness: answer vs golden ref
  • F1, precision, recall modes
  • Faithfulness ≠ Correctness
3

LLM-as-Judge

  • Judge at temperature=0
  • Generator ≠ judge (self-preference)
  • Verbosity & position bias
  • This module OWNS the concept

Run the notebook: 09_generator_metrics.ipynb  ·  Next: Module 10 — Reranking (Cohere rerank-v3.5, before/after lift)