InterviewPrepKit

Home / Coding / Agent Coding / Evaluation / LLM-as-Judge Eval Harness

LLM-as-Judge Eval Harness

medium 00:00
Solving tips
  • The judge is a model, so treat its output as untrusted: clamp the score into [0, 1] and never let a malformed or crashing judge take down the run. A bad grade is still a data point.
  • Separate the two numbers reviewers care about: the mean score tells you overall quality, the flagged list tells you which specific cases to go look at. One without the other is not actionable.
  • Be ready to explain why you'd use an LLM judge at all (open-ended outputs a substring check can't grade) and its failure modes (position/verbosity/self bias, and that it needs an explicit rubric to be reproducible).

A substring check can tell you whether an agent said “Paris,” but it cannot tell you whether a summary is faithful, whether an explanation is helpful, or whether a rewrite kept the original meaning. Those are open-ended judgments, and the practical way to score them at scale is to ask another model to grade the output against a rubric. That grader is the LLM judge, and this exercise is the harness that runs it over a batch and turns its opinions into numbers you can track.

Why an LLM judge

Rule-based checks are cheap, deterministic, and perfect when correctness is a predicate. They fall apart on free-form text where many different strings are all “good.” An LLM judge fills that gap: given the prompt and the output, it returns a graded score and a short reason. The cost is that the judge is itself a model, with all the fallibility that implies, so the harness has to defend against it rather than trust it.

How a case works

A case is a dict with a prompt (what the agent was asked) and an output (what it produced). The harness packs those into a pair dict and hands it to judge, which returns {"score": <0..1>, "reason": <str>}. You record the clamped score and its reason, decide whether the score falls below threshold, and move on. Because judge is passed in, swapping in a deterministic stub makes the whole harness testable without a network.

Task

Complete run_judge_eval(judge, cases, threshold=0.5):

  1. Iterate the cases in order. For each, resolve its name (default "case_<i>" using the zero-based index).
  2. Build pair = {"prompt": case["prompt"], "output": case["output"]} and call judge(pair).
  3. Read the score and clamp it into [0.0, 1.0] as a float; keep the reason (default ""). If judge raises or the score is not numeric, the case scores 0.0, error holds the exception text, and reason records it too.
  4. A case is flagged when its (clamped) score is strictly less than threshold.
  5. Collect one result dict per case: {"name", "score", "reason", "flagged", "error"} (error is None on success).
  6. Return the summary with results, mean_score (mean of scores, or 0.0 when empty), flagged (the names below threshold, in order), and total.

Example

def judge(pair):
    # toy judge: full marks when the gold answer appears in the output
    gold = {"2+2": "4", "cap": "Paris"}
    want = gold.get(pair["prompt"], "")
    if want and want in pair["output"]:
        return {"score": 1.0, "reason": "contains the expected answer"}
    return {"score": 0.0, "reason": "missing the expected answer"}

cases = [
    {"name": "math", "prompt": "2+2", "output": "the answer is 4"},
    {"name": "geo",  "prompt": "cap", "output": "the capital is Paris"},
    {"name": "bad",  "prompt": "2+2", "output": "i don't know"},
]

summary = run_judge_eval(judge, cases, threshold=0.5)

round(summary["mean_score"], 3)              # -> 0.667
summary["flagged"]                           # -> ["bad"]
[r["score"] for r in summary["results"]]     # -> [1.0, 1.0, 0.0]
summary["results"][2]["flagged"]             # -> True

Constraints

  • One case whose judge crashes or returns garbage must not stop the run; it scores 0.0 and every case still gets a result.
  • Clamp every score into [0.0, 1.0]; a judge that returns 1.4 or -0.2 must not skew the mean.
  • Preserve case order in both results and flagged.
  • Do not call any real LLM, network, or clock — judge is provided and is the only grader.

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug