All Resources

R-41

Intelligence (AI)

Harness Engineering: Making AI Quality Measurable

The discipline that turns prompt changes from guesswork into engineering — golden sets, scorer selection, regression gates in CI, and reading a result that is genuinely noisy.

PAR2 Labs

August 22, 2026

16 min

Harness Engineering: Making AI Quality Measurable

Every team building with models hits the same wall: someone changes a prompt, everyone agrees it feels better, and three weeks later a customer finds the case it broke. A harness is what replaces that with a number. It is the least glamorous part of AI engineering and the single highest-leverage thing you can build.

01

What a harness actually is

A harness is four things that run together: a fixed set of cases, a scorer that turns an output into a number, a runner that executes the set against a version of your system, and a gate that decides whether a change ships. Miss any one and it is not a harness — it is a spreadsheet someone updates occasionally.

The crucial property is that it is versioned and frozen. A test set that grows every time someone adds a case cannot tell you whether yesterday's change helped.

THE HARNESS LOOP01Golden setfrozen, versioned02Runagainst a build03Scoreper case04Comparevs baseline05Gateship or block06Harvestnew failuresPRODUCTION FAILURES BECOME TOMORROW'S CASES — THE SET GROWS BY VERSION, NEVER MID-EXPERIMENTA CHANGE SHIPS ON A DIFF, NOT ON A FEELING

Fig 1 — the harvest step is what keeps a harness honest. Without it the set slowly stops resembling production and the number stops meaning anything.

02

Choosing a scorer

Scorer choice determines whether your number means anything. Reach for the cheapest scorer the task permits, and be suspicious of model-graded scoring — it is genuinely useful and it introduces a second model's biases into your measurement.

ScorerUse forCostWatch out for
Exact matchClassification, routing, structured extractionFreeToo brittle for anything with formatting variance
Schema validationStructured output, tool argumentsFreeValid shape with wrong values still scores as a pass
Fuzzy / F1 over fieldsExtraction with several fieldsFreeField weighting has to reflect what actually matters
Deterministic assertions'Must cite a source', 'must not mention X'FreeOnly checks what you thought to assert
Model-graded rubricTone, helpfulness, summary qualityPer runPosition bias, verbosity bias, drift when the grader changes
Human reviewThe final word on subjective workExpensiveReserve for a sample, not the whole set

Combine scorers. A case can require valid schema, a cited source, and a rubric score above a threshold — all three, checked separately.

A harness is what replaces 'it feels better' with a number you can argue about.

03

Building one

Seven steps, and steps 01 to 03 can be done in an afternoon. The value arrives the first time the harness blocks a change that everyone was sure was an improvement.

01

Collect real cases, weighted toward pain

  • TOOL

    Production logs

  • USE

    50–200 cases; over-sample known failures

  • GET

    A candidate golden set

Pull real inputs from production, not invented ones. Deliberately over-sample the cases that have failed before, the edge cases, and the ones people complain about. Include a handful of straightforward cases as a sanity floor.

Why: A set of easy cases scores well and tells you nothing. The set exists to catch regressions, so it must contain the things that regress.

02

Write the expected outputs by hand

  • TOOL

    Domain expertise, not a model

  • USE

    The correct answer, agreed by someone who knows

  • GET

    Ground truth you can defend

Have a person who understands the domain write or approve the expected output for every case. Record why an answer is correct where it is not obvious, because you will revisit it.

Why: Ground truth generated by a model encodes that model's mistakes into your measurement, and you will then optimise toward them without ever seeing it.

03

Freeze and version the set

  • TOOL

    Git

  • USE

    The set lives in the repo, versioned like code

  • GET

    Comparable runs over time

Commit the set as data in the repository. When you add cases, bump the set version and note it — never silently extend the set you are currently comparing against.

Why: If the set changes between runs, the comparison is meaningless. Versioning is what turns a collection of examples into a measurement instrument.

04

Score per case, and record everything

  • TOOL

    promptfoo or your own runner

  • USE

    Per-case score, full output, tokens, latency

  • GET

    A results artefact, not a number

Store the complete output for every case alongside its score, plus token counts and latency. Keep the last several runs so a regression can be diffed rather than re-derived.

Why: An aggregate score tells you that something changed. The per-case diff tells you what, and that is the only actionable form of the result.

05

Establish a baseline and a threshold

  • TOOL

    Your current production version

  • USE

    Baseline score plus an explicit tolerance

  • GET

    A gate that can actually block

Run the current production version to set the baseline. Define what blocks a release: typically no case regressing from pass to fail, and the aggregate not dropping beyond a stated tolerance.

Why: Without a pre-agreed threshold, every regression becomes a negotiation at the moment it is least convenient to have one.

06

Run it in CI on every change to the AI path

  • TOOL

    Your CI

  • USE

    Triggered by prompt, model, retrieval or tool changes

  • GET

    Regressions caught before merge

Run the harness on any change touching prompts, model versions, retrieval configuration or tool definitions. Post the diff against baseline into the pull request.

Why: A harness run manually is a harness run rarely. In CI it is the difference between catching a regression in review and hearing about it from a customer.

07

Account for non-determinism honestly

  • TOOL

    Repeat runs

  • USE

    Temperature 0 where possible; 3–5 repeats where not

  • GET

    A result you can trust

Pin temperature to zero and fix seeds where the provider supports it. Where output is inherently variable, run each case several times and report the distribution rather than a single sample.

Why: A one-point difference on a single run is noise. Treating it as signal leads teams to chase changes that never existed and to dismiss real regressions as variance.

04

Reading results without fooling yourself

A harness makes it possible to be wrong precisely, which is progress — but only if the results are read with the same discipline they were produced with.

Discipline for reading a run

01

Small aggregate movements on a small set are noise. With 100 cases, a two-point change is well within variance.

02

Always read the per-case diff. An unchanged aggregate can hide five improvements and five regressions.

03

Optimising against the harness eventually overfits to it. Harvest fresh production failures regularly.

04

A model-graded scorer is a model — pin its version, and re-baseline when it changes.

05

Track cost and latency alongside quality. A change that improves scores and doubles spend is not obviously a win.

06

Never generate ground truth with the model under test. That measures agreement, not correctness.

05

Reference

Tooling here is genuinely interchangeable — the discipline is what matters. Start with something that runs a set of cases and diffs against a baseline, and grow from there.

Primary documentation

promptfoo

Declarative test cases, assertions, and side-by-side diffs between versions.

Tool use

Where structured outputs make schema-validated scoring possible.

OpenTelemetry — GenAI conventions

Recording tokens and latency alongside quality, in a standard shape.

JSON Schema

The basis for schema-validation scoring of structured output.

LangGraph

Where the system under test is a multi-step graph rather than a single call.

Key Takeaways

01

A harness is a frozen set, a scorer, a runner and a gate — miss one and it is not a harness.

02

Over-sample known failures; a set of easy cases scores well and tells you nothing.

03

Never generate ground truth with the model under test — that measures agreement, not correctness.

04

Read the per-case diff, not the aggregate; and treat small movements on small sets as noise.


PAR2 Labs · Intelligence (AI)

Work With Us

Have a problem worth solving?