Engineering note

Evaluations that gate a deploy

An evaluation suite that nobody can fail is a dashboard. This is how we build one that blocks a release, and what it costs to keep it honest.

Most teams build evaluations after the first quality complaint. By then the system is in production, the corpus has changed twice, and there is no baseline to compare against. The suite that gets built in that state measures the current behavior and calls it correct.

A pull request that changes a prompt, tool, model or index triggers the golden set and an adversarial set. Deterministic checks and a calibrated model judge score the runs. A release gate compares the result against the recorded baseline and blocks on regression.
The gate compares against a recorded baseline, so nobody has to remember the threshold.

Three suites, three jobs

The golden set is human-labeled and versioned like code. It is small, usually one to three hundred cases, and it covers the work the system actually receives rather than the work someone imagined. Adding a case is a pull request with a reviewer.

The adversarial set holds the things that must never happen: prompt injection carried in a retrieved document, a request for data outside the caller's scope, a refusal the system should not make. These are pass or fail, not scored.

The regression set is everything that broke once. Every incident adds a case. It is the cheapest suite to maintain and the one that catches the most on a Tuesday.

Score what you can check deterministically

Before you reach for a model judge, check what a program can check: valid schema, every claim carrying a citation to a retrieved passage, no identifier the caller may not see, latency inside budget. These are fast, free, and unambiguous.

Use a judge for what remains, and calibrate it. Have humans label a sample, measure the judge against those labels, and record the agreement rate alongside the score. A judge you have not calibrated produces a number, not a measurement.

# The gate is a comparison against the recorded baseline, not a
# threshold someone remembers.
def gate(candidate: RunSet, baseline: RunSet) -> Verdict:
    checks = [
        ("schema_valid",   candidate.schema_valid_rate,  ">=", baseline.schema_valid_rate),
        ("citation_recall", candidate.citation_recall,   ">=", baseline.citation_recall - 0.01),
        ("judge_score",    candidate.judge_mean,         ">=", baseline.judge_mean - 0.02),
        ("p95_latency_ms", candidate.p95_latency_ms,     "<=", baseline.p95_latency_ms * 1.1),
        ("cost_per_task",  candidate.cost_per_task,      "<=", baseline.cost_per_task * 1.15),
        ("injection_refusal", candidate.injection_refusal_rate, ">=", 0.99),
    ]
    failures = [c for c in checks if not compare(c)]
    return Verdict(passed=not failures, failures=failures, run_id=candidate.run_id)

Gate on a baseline, not a threshold

Absolute thresholds rot. The team lowers them after a bad week, and nobody records why. Comparing a candidate against the recorded baseline of the current production version makes the question concrete: is this change better or worse than what is running now.

CheckRuleWhy this one
Schema validno worse than baselinea parse failure is a total failure
Citation recallwithin 1 point of baselineunsupported claims are the visible harm
Judge scorewithin 2 points of baselinenoisy, so allow a small band
p95 latencywithin 10% of baselinetail latency is what users feel
Cost per taskwithin 15% of baselinequality bought with money is not free
Injection refusalat least 99%, absolutethe one case with no acceptable regression

What it costs

A serious suite runs on every pull request and costs real money in inference. Budget for it explicitly rather than discovering it. The alternative is a system whose quality you cannot state, and a system whose quality you cannot state is a system no one will approve for a workload that matters.

More field notes

Bring us the problem.

Tell us the outcome you are trying to create, what you have already attempted, and where the constraints are.

Contact nuperX