A person holding up a 9/10 score card beside an evaluation machine with gauges, a Python logo, a 1-10 rating scale, and bins of documents sorted into Pass and Fail.

LLM Evaluation in Python: Build an Eval Harness From Scratch

by Ben Batman 0 Comments intermediate ai

You change the system prompt behind an LLM feature, try a few examples, and think you’ve fixed the bug. The new outputs look better when you inspect them manually, so you deploy the change. All is well. That is, until you discover that a separate class of requests has regressed or that responses seem worse on average. This is the problem with making ad hoc prompt changes without systematic LLM evaluation.

Ad hoc changes and manual inspection are useful while you’re experimenting, but they don’t give you a repeatable regression signal. You also find it increasingly difficult to distinguish stochastic variation from real improvement. In this tutorial, you’ll build an LLM evaluation harness from scratch that catches potential output regressions and turns product expectations into verifiable tests.

By the end of this tutorial, you’ll understand that:

  • LLM evaluation tests model outputs against explicit criteria, so you can tell whether a prompt or model change made things better or worse.
  • Deterministic checks should handle anything code can verify, and an LLM judge should handle only what code can’t, like helpfulness or tone.
  • LLM judges are trustworthy only after you calibrate them against human-labeled examples.
  • Release gates block changes that make a previously passing case fail a blocker criterion, even if the average scores improve.

Along the way, you’ll build a framework-agnostic Python command-line program that compares two prompt versions on the same test cases and returns a PASS, FAIL, or REVIEW decision, with a matching exit code you can wire into CI. Here’s the final comparison you’ll reproduce:

Language: Text
Baseline: support_v1
Candidate: support_v3_guarded

Criterion                  Baseline  Candidate     Change
---------------------------------------------------------
policy_correctness             100%       100%      +0 pp
  verdicts (P/F/R)            4/0/0      4/0/0
required_next_step             100%       100%      +0 pp
  verdicts (P/F/R)            4/0/0      4/0/0
safety_and_privacy             100%       100%      +0 pp
  verdicts (P/F/R)            4/0/0      4/0/0
helpfulness                    4.00       4.00      +0.00
  verdicts (P/F/R)            4/0/0      4/0/0
professional_tone              4.00       4.00      +0.00
  verdicts (P/F/R)            4/0/0      4/0/0
concision                      3.00       4.00      +1.00
  verdicts (P/F/R)            4/0/0      4/0/0

New critical failures: 0
New blocker failures:  0
Carryover failures:    0
Fixed failures:        0
Cases needing review:  0

Release decision: PASS
Reason: The candidate introduced no new blocker failures.

The output compares two prompt versions across six evaluation dimensions, separates new failures from ones that already existed, and ends with a clear release decision.

Take the Quiz: Test your knowledge with our interactive “LLM Evaluation in Python: Build an Eval Harness From Scratch” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

LLM Evaluation in Python: Build an Eval Harness From Scratch

Test your understanding of LLM evaluation in Python, from rubrics and code checks to LLM judges, calibration, and release gates.

Project Overview

You’ll build an LLM evaluator for a customer-support bot. The bot receives a customer message and the relevant support policy, then generates a reply. Return eligibility has an authoritative answer, while qualities such as helpfulness and tone require semantic judgment.

Your harness will evaluate LLM output against the requirements in three layers:

  • Test the output: Check whether each reply satisfies the behavior contract.
  • Test the evaluator: Compare judge decisions with adjudicated human labels.
  • Test the change: Compare paired prompt runs and regressions.

Data flows through the evaluator as follows:

Data Flow Through the Evaluation Harness
Data Flow Through the Evaluation Harness

The harness follows an oracle hierarchy, where each independent oracle represents a source of truth capable of determining whether an answer is good. The most direct source of truth available settles each criterion, passing anything it can’t settle to a higher oracle:

  1. Code handles deterministic rules, such as counting words or identifying forbidden phrases.
  2. An LLM judge assesses subjective, possibly ambiguous behavior that deterministic rules can’t effectively evaluate.
  3. When neither your deterministic rules nor your LLM judge can decide, the result is REVIEW, and a human looks at it.

Human-labeled examples aren’t part of this hierarchy. They play a different role: they check the LLM judge itself. You’ll use them to calibrate the judge in Step 6.

Each test case also includes a human-written reference that records the expected decision and the facts a good reply must include. For criteria that need it, the LLM judge uses this reference to help assess whether the target LLM output meets the desired quality standards. These are reference plus judge oracles. This tutorial uses grader and oracle interchangeably.

The harness keeps each criterion separate so you can see how the LLM performs in each dimension as you assess its overall output quality. After the harness scores all dimensions, the release policy makes the final decision based on which blocker criteria the change caused to fail.

Prerequisites

To follow along, you’ll need:

  • Python 3.11 or higher
  • An OpenAI API key if you’re using live mode
  • Some Python coding experience

This tutorial assumes you’re comfortable with the following concepts:

You’ll learn the evaluation-specific design as you build it, so you don’t need prior experience with LLM evaluation frameworks.

Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Article

Already a member? Sign-In

Locked learning resources

The full article is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Article

Already a member? Sign-In

About Ben Batman

Ben is a professional Machine Learning Engineer who enjoys building reliable and accurate ML systems in Python and C++ to solve real-world problems.

» More about Ben

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

What Do You Think?

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

Become a Member to join the conversation.

Keep Learning

Related Topics: intermediate ai