What you need to know
Somewhere between the demo that convinced your executive sponsor and the release that touches real customers, a question appears that no benchmark answers. Not "is this agent good?" but "will this agent, with the oversight we can actually staff, meet the reliability level this workflow requires — and can we show our working?"
That is a different question, and it has a different shape. It is not a ranking. It is a qualification: a procedure that takes an agent, a workflow, a target and a class of oversight policies, and returns either a specific policy with a statistical guarantee attached, or a refusal. A team that runs it ships with a number they can defend to a risk committee. A team that does not ships on a leaderboard position and finds out the rest in production.
The framing comes from a paper published on arXiv in September 2026, READY or Not: Reliable Enterprise Agent Deployment by Veronica Chatrath, Bryan Zhu, Jingxuan Fan, George Pu, Soham Dinesh Tiwari, Soham Dan and colleagues, submitted on 2 September 2026. Its opening premise is worth quoting because it is the whole problem in two sentences: "An AI agent can perform well on benchmarks and still be unsuitable for deployment. Existing AI-agent benchmarks measure whether an agent can complete realistic professional work, whereas enterprise deployment asks a different question: whether an agent can meet a required reliability level, under acceptable human oversight, and at tolerable cost."
What follows is roughly a quarter that paper and three quarters a procedure you can implement on your own workflow, in whatever language your eval harness already speaks. The worked figures are Python and NumPy, with one call to SciPy for the confidence bound.
- Reliability is a property of the human-AI system, not of the agent: accepted cases plus escalated cases, weighted by how often each path is right.
- The target comes from the business — from the downstream cost of an error — and is fixed before you look at any model.
- Select the threshold on a development split with a margin, then freeze it. The margin is what stops you selecting noise.
- Qualify on held-out cases with a one-sided lower confidence bound. A point estimate above the target is not a pass.
- Two agents a third of a point apart on accuracy can differ by ten points of review burden at the same target. That difference is the entire decision.
- Your reviewers set a ceiling. If the review path is right 90 per cent of the time, no oversight policy reaches 95 per cent reliability.
Why a benchmark score is the wrong ship or no-ship number
The READY case study is a retrospective clinical audit workflow drawn from CliniCARE-Bench: 16 agent systems, 750 cases, 12,000 runs in total, spanning models including GPT-5.4, Claude Sonnet 5 and Gemini-3.1-Pro. The 750 cases were split 375 for development and 375 held out, paired across systems and without stratification.
The headline result is the reason to read the paper. GPT-5.4 achieved 72.8 per cent autonomous accuracy. Claude Sonnet 5 achieved 72.5 per cent. On any leaderboard those two systems are indistinguishable — a gap of three tenths of a percentage point, comfortably inside the noise of a 375-case evaluation. Yet to qualify at a 76 per cent reliability target, the two systems required substantially different human-review burdens: 39.2 per cent of cases escalated versus 29.6 per cent. The authors put it plainly: "similar autonomous performance can support substantially different reliability-oversight tradeoffs."
| System | Autonomous accuracy | Escalated at a 76% target | Cases reviewed per 1,000 | Review hours per 1,000 cases |
|---|---|---|---|---|
| GPT-5.4 | 72.8% | 39.2% | 392 | 39.2 |
| Claude Sonnet 5 | 72.5% | 29.6% | 296 | 29.6 |
| Difference | 0.3 pp | 9.6 pp | 96 cases | 9.6 hours |
Read the last row slowly. A difference invisible on a benchmark becomes 96 extra cases through a human queue for every thousand the system handles. At a hundred thousand cases a year — a modest volume for a claims-triage workflow in Bengaluru or a clinical-coding backlog in Manchester — that is 9,600 additional reviews, which is most of a full-time reviewer, recurring annually, chosen by accident because somebody compared two numbers that were not the numbers that mattered.
The mechanism behind it is not mysterious. Two agents can be equally accurate overall and differ completely in how well their confidence tracks their correctness. An agent that is unsure exactly when it is wrong lets you accept a large share of its work automatically. An agent that is serenely confident about its mistakes forces you to escalate far more to reach the same system-level reliability, because its signal cannot separate the cases you can trust from the ones you cannot. Accuracy measures how often it is right. Qualification measures how usefully it knows.
What you need before you start
Four things, and the honest answer is that assembling them takes longer than running the procedure. Every one of them is reusable across every future model upgrade, which is the argument for doing it properly once.
A workflow-specific success criterion
Not a benchmark metric. A binary, workflow-owned definition of whether a single case was executed successfully, written by the person accountable for the outcome. For a Bengaluru motor-claims triage workflow that might be: the correct claim category, the correct settlement band, and no missing mandatory field. For a Manchester clinical-coding workflow it might be: the primary diagnosis code matches the auditor's, and no code was invented. READY's design deliberately "preserves each workflow's own definition of successful execution while applying a common qualification procedure", and that separation is what makes the method portable — the statistics are shared, the definition of success is emphatically not.
Write it down as a function before you write anything else. If two competent people applying your criterion to the same case disagree, you do not have a criterion yet, you have a preference, and every number downstream inherits the ambiguity. If your workflow is a browser or desktop agent where success lives in a trajectory rather than a final answer, our guide to judging GUI agents on screenshots and trajectories covers how to make that criterion mechanical.
A labelled case set, split in two
You need cases with ground-truth labels, split into a development half and a held-out half, and you must not look at the held-out half until the policy is frozen. READY used 375 in each. That number is not arbitrary and it is worth understanding why a few hundred is the practical floor.
Qualification turns on a lower confidence bound, and the gap between the observed success rate and its lower bound narrows roughly with the square root of the sample size. Consider a system observing 82 per cent success on the held-out split. At 100 cases, the 95 per cent one-sided Clopper-Pearson lower bound is about 74.5 per cent — below a 76 per cent target, so the system fails on sample size alone. At 375 cases the same observed rate gives roughly 78.5 per cent and passes with room. At 1,000 cases it gives about 79.9 per cent. Under 150 cases you are implicitly demanding the agent beat the target by ten points or more, and you will reject perfectly deployable systems without ever seeing why.
If your labelling budget is tight, spend it on the held-out split. The development split only has to rank thresholds in roughly the right order; the qualification split has to carry a statistical claim in front of a risk committee. And keep the cases paired across every candidate system you are comparing, exactly as READY did, so that a difference between two agents is a difference between agents rather than between the cases they happened to see.
A routing signal
A single scalar per case, available at run time before the output is used, which is intended to be larger when the agent is more likely to have succeeded. Everything in the next section is about choosing one and checking that it behaves.
Measured per-case costs
Two numbers: the agent execution cost per case, and the incremental human-review cost per escalated case. The word incremental is load-bearing — it is the marginal cost of a review, not a share of the reviewer's salary, and it should include the reviewer's fully loaded hourly rate multiplied by the measured handling time, plus any queueing infrastructure you would not otherwise run. If you do not yet record cost per case at all, the instrumentation is a short job and we have written it up in cost-aware evals: score quality per pound, not just quality. That guide is about measuring the money; this one is about spending the least of it that meets a target.
You also need the measured success rate of the human-review path itself. Not an aspiration, a measurement — take a sample of escalated cases and have a second, more senior reviewer adjudicate them. READY's primary analysis assumes this rate is 0.9, and the implication is uncomfortable: if your reviewers are right 90 per cent of the time, no oversight policy of this shape can push system reliability above 90 per cent. Reachable reliability is capped by the review path, not only by the agent. Teams routinely discover their real ceiling is lower than the target they had already promised.
Step one: set the reliability target from the business
The target, written as Y, is the minimum acceptable probability that a case is handled correctly by the whole human-AI system. It is not a model property and it is not a benchmark number. It comes from the downstream cost of an error, and the derivation is simple enough to do on a whiteboard with the workflow owner.
Take the expected cost of one incorrect case. For a claims workflow that is the average of the wrongly paid amount, the rework, the complaint handling and the regulatory exposure. For a clinical-coding workflow it is the reimbursement correction, the audit response and the clinical-risk provision. Multiply by expected annual volume, then ask what error rate produces an annual expected loss the business will actually accept — either because it matches the loss the incumbent process already produces, or because it sits inside a stated risk appetite.
Two anchors help. The first is the incumbent: whatever the current all-human or rules-based process achieves is a defensible floor, because a system that is worse than what it replaces will not survive its first incident review regardless of what the business case said. Measure the incumbent honestly, including the errors it produces that nobody currently counts. The second anchor is contractual or regulatory: an accuracy commitment in a service agreement, a supervisory expectation for a regulated decision, an internal control standard. Where one exists it usually dominates, and the derivation becomes a matter of reading rather than arithmetic.
Fix Y before you evaluate anything. A target chosen after you have seen what the models achieve is not a requirement, it is a rationalisation, and everyone in the room will know it. If you want the wider organisational context for that conversation, the 2026 build-versus-buy picture for agents is a reasonable brief for a sponsor who has not yet thought about oversight as a line item.
Record the target and its derivation in the same repository as the harness, in a file with a date and an owner. As of September 2026 the most common reason a qualification result is disputed months later is not that the statistics were wrong, but that nobody can say where the target came from — so it gets renegotiated by whoever is most inconvenienced by it. A target with a paper trail survives; one that lives in a slide does not.
Step two: choose and check the routing signal
The oversight policy class is deliberately narrow, and its narrowness is a feature. READY uses terminal accept-or-escalate policies: for a scalar routing signal s, the threshold policy accepts the agent's output when s is at or above the threshold, and escalates to a human otherwise. One decision per case, no retries, no partial autonomy. The whole policy is one number, which is what makes it auditable — you can hand a threshold to a compliance function and they can understand it, which is not true of a learned router.
The class assumes that a larger signal means a greater likelihood of successful autonomous execution. That assumption is doing enormous work and it is the thing most teams never check.
Four candidate signals
Self-reported confidence. The cheapest option: ask the agent, in a structured output field, how confident it is. It costs nothing extra and it is often weakly informative. It is also the most likely to be poorly calibrated, and it tends to be coarse — models cluster on round numbers, so you get a handful of distinct values rather than a continuum.
A second-model critique score. Run a separate model over the input and the agent's output and have it score whether the work is correct. This usually ranks cases better than self-report, because a critic that did not produce the answer is not defending it, and it produces a finer-grained score. It also doubles part of your inference cost, which lands directly in the agent execution term of the cost model. We have costed that trade-off in detail in second-model verification and what critique actually costs.
A trained scorer. Fit a small classifier on your development split to predict success from cheap features: output length, tool-call count, retrieval scores, whether a required field was populated, the self-report itself. This tends to rank best because it is fitted to your workflow, and it produces a genuinely continuous score. The cost is that it is another model to maintain, and it must be trained on the development split only — fitting it on the qualification split invalidates the entire exercise. A related option, where the risk is content rather than correctness, is to route on a guardrail classifier's score; our comparison of Llama Guard, Qwen3Guard and Granite Guardian covers the candidates.
Retrieval-agreement heuristics. Where the workflow is grounded in documents, the agreement between the agent's answer and the retrieved evidence is often the strongest signal available and costs nothing extra to compute: the number of retrieved passages supporting the claim, the top-k similarity margin, whether the cited span actually contains the value that was extracted. In a clinical-coding workflow this tends to beat self-report by a wide margin, because a code that appears verbatim in the source note is a different animal from one the model inferred.
Checking monotonicity before you trust the sweep
Bin the signal on the development split and compute the empirical success rate in each bin. If the accuracy column is not weakly increasing, the assumption behind the whole policy class is false for your signal, and no single threshold will behave the way the sweep predicts.
| Signal bin | Cases | Empirical success rate | Reading |
|---|---|---|---|
| [0.00, 0.20) | 18 | 0.61 | Too few cases to interpret |
| [0.20, 0.40) | 41 | 0.68 | Rising |
| [0.40, 0.60) | 76 | 0.74 | Rising |
| [0.60, 0.80) | 112 | 0.71 | Falls — investigate before proceeding |
| [0.80, 0.90) | 84 | 0.83 | Rising |
| [0.90, 1.00] | 44 | 0.91 | Rising |
A dip like the fourth row usually has a findable cause. Most often it is a case family with its own difficulty profile that happens to attract mid-range confidence — in the claims example, a category the model handles fluently but wrongly. The fixes, in order of preference: segment the workflow and qualify each segment separately with its own threshold; add a feature to the signal that separates the family; or, if neither is practical, accept that the threshold must sit above the dip, which costs you coverage and therefore money. What you must not do is average over it and hope.
import numpy as np
def signal_bins(signal, success, edges=(0.0, 0.2, 0.4, 0.6, 0.8, 0.9, 1.01)):
"""Empirical success rate per signal bin, on the DEVELOPMENT split.
The threshold policy assumes higher signal -> more likely to succeed.
If the accuracy column is not (weakly) increasing, that assumption is
false and no single tau will behave the way the sweep predicts.
"""
s = np.asarray(signal, dtype=float)
y = np.asarray(success, dtype=int)
rows = []
for lo, hi in zip(edges[:-1], edges[1:]):
m = (s >= lo) & (s < hi)
n = int(m.sum())
rows.append({
"bin": f"[{lo:.2f}, {hi:.2f})",
"n": n,
"accuracy": float(y[m].mean()) if n else float("nan"),
})
return rows
def is_monotone(rows, min_n=30, tolerance=0.02):
"""Weak monotonicity, ignoring bins too small to mean anything.
tolerance absorbs sampling noise; min_n drops bins whose estimate is
not worth reading. Both are judgement calls - state them in the report.
"""
accs = [r["accuracy"] for r in rows if r["n"] >= min_n]
return all(b >= a - tolerance for a, b in zip(accs, accs[1:]))
One more property to record: how many distinct values your signal actually takes. In the READY case study this ranged from five distinct confidence values on one Gemini system to thirty-six on a GPT-5.4-mini system. That is a large practical difference. With five distinct values there are only five achievable coverage levels, so the lowest-cost feasible policy may sit far above the coverage a finer signal would have reached — and you pay that gap in review hours every single day the system runs.
Every article here is written by a Verified Builder. Want your name on the next one?
AI Tech Connect lists AI engineers, founders and researchers across India and the UK — and the people hiring browse it to find them. Adding your profile is free.
Become a Verified Builder →Step three: sweep the threshold on the development split
Now the arithmetic. Reliability under a threshold policy is a weighted average of the two paths a case can take:
R(tau) = c(tau) * a(tau) + (1 - c(tau)) * a_h
where c(tau) is autonomous coverage — the fraction of cases accepted — a(tau) is the success rate among accepted cases, and a_h is the success rate of the human-review path. Cost decomposes just as simply:
C(tau) = k_m + k_h * (1 - c(tau))
with k_m the agent execution cost per case and k_h the incremental human-review cost per escalated case. Note what this implies: because k_h is positive and k_m is paid on every case regardless, minimising cost is exactly equivalent to maximising autonomous coverage. The objective is therefore not "find the best threshold" in any vague sense. It is: among the policies that meet the target, take the one with the highest coverage.
The subtlety is the target you select against. You do not select against Y. You select against Y plus a margin, delta — READY uses 0.05 in its clinical instantiation. The margin exists because the development estimate of reliability is itself noisy, and because you are choosing the maximum-coverage feasible policy, which is precisely the point on the curve where an optimistic error is most likely to have put you. Selecting against the bare target reliably produces a policy that passes on development data and fails on held-out data. The margin is the cost of that conservatism, paid in coverage, and it is worth paying.
| tau | Coverage c(tau) | Accepted accuracy a(tau) | Reliability R(tau) | Cost per case | Meets 0.81? |
|---|---|---|---|---|---|
| 0.50 | 0.92 | 0.755 | 0.767 | £0.184 | No |
| 0.60 | 0.84 | 0.775 | 0.795 | £0.328 | No |
| 0.70 | 0.74 | 0.800 | 0.826 | £0.508 | Yes — selected |
| 0.75 | 0.68 | 0.815 | 0.842 | £0.616 | Yes, dearer |
| 0.85 | 0.55 | 0.845 | 0.870 | £0.850 | Yes, dearer |
| 0.95 | 0.31 | 0.880 | 0.894 | £1.282 | Yes, dearest |
| escalate all | 0.00 | n/a | 0.900 | £1.840 | Yes, and useless |
Three things are visible in that table that are invisible in an accuracy number. The selected policy is tau = 0.70 at £0.508 per case, escalating 26 per cent of traffic. Tightening to 0.85 buys four more points of reliability you did not ask for, at 67 per cent more cost. And the final row makes the ceiling concrete: escalating everything yields exactly a_h, 0.900, because at zero coverage the system is the review path. Whatever the agent does, this policy class cannot exceed 0.900 while reviewers are right nine times in ten.
import numpy as np
A_H = 0.90 # MEASURED success rate of the human-review path
K_M = 0.04 # agent execution cost per case, your currency
K_H = 1.80 # INCREMENTAL human-review cost per escalated case
TARGET = 0.76 # Y - from the business, fixed before any modelling
DELTA = 0.05 # development-set selection margin
def sweep(signal, success, a_h=A_H, k_m=K_M, k_h=K_H):
"""Coverage, accepted accuracy, reliability and cost per threshold."""
s = np.asarray(signal, dtype=float)
y = np.asarray(success, dtype=int)
n = s.size
rows = []
# Candidates: every distinct observed value (each gives a distinct
# coverage), plus +inf, which is the escalate-everything policy.
for tau in list(np.unique(s)) + [np.inf]:
accepted = s >= tau
n_acc = int(accepted.sum())
coverage = n_acc / n
a_tau = float(y[accepted].mean()) if n_acc else 0.0
rows.append({
"tau": float(tau),
"coverage": coverage,
"accepted_accuracy": a_tau,
"reliability": coverage * a_tau + (1.0 - coverage) * a_h,
"cost_per_case": k_m + k_h * (1.0 - coverage),
})
return rows
def select_tau(rows, target=TARGET, delta=DELTA):
"""Lowest-cost policy meeting the MARGIN-ADJUSTED target Y + delta.
Returns None when nothing is feasible - a real result, not an error.
Cost falls as coverage rises, so the tie-break on -coverage only
matters when two thresholds accept the same set of cases.
"""
feasible = [r for r in rows if r["reliability"] >= target + delta]
if not feasible:
return None
return min(feasible, key=lambda r: (r["cost_per_case"], -r["coverage"]))
Run this once per candidate agent. You now have, for each, a specific threshold and the coverage it buys. Do not compare the agents on the development numbers and stop there — that is the same mistake as comparing benchmark scores, just with better arithmetic. The comparison that counts happens after qualification.
Step four: freeze the policy and qualify on held-out cases
This is the step teams skip, and skipping it is what turns a harness into theatre. The policy — the agent, the signal, the threshold, all of it — is now frozen. You evaluate it once on the held-out split, and you do not go back and adjust anything on the strength of what you see. If you re-tune after looking, the held-out split has become a development split and you no longer have a qualification, you have an optimistic estimate wearing a lab coat.
The pass rule is not "observed reliability is above Y". It is: the one-sided lower confidence bound on reliability, at level 1 minus alpha, is at or above Y. For binary outcomes with measured human-review results, that bound can be computed directly from the binomial distribution. Each held-out case gets one outcome: if the signal cleared the threshold, whether the agent succeeded; if it did not, whether the human review path succeeded. Count the successes, and take an exact Clopper-Pearson lower bound.
Clopper-Pearson rather than a normal approximation, because at the sample sizes involved the approximation is optimistic in exactly the direction that matters, and because the exact bound is one line with SciPy. The lower bound at level 1 minus alpha is the alpha-quantile of a Beta distribution with parameters k and n − k + 1.
import numpy as np
from scipy.stats import beta
def binomial_lower_bound(k, n, alpha=0.05):
"""Exact one-sided Clopper-Pearson lower bound at level 1 - alpha.
Equal to the alpha-quantile of Beta(k, n - k + 1). Conservative for
every true p, which is what you want from a number that decides
whether a system ships. k = 0 has no informative bound above zero.
"""
if n == 0:
raise ValueError("no qualification cases")
if k == 0:
return 0.0
return float(beta.ppf(alpha, k, n - k + 1))
def qualify(signal, agent_success, human_success, tau,
target=TARGET, alpha=0.05):
"""Run the FROZEN policy on held-out cases. Ship or do not ship.
agent_success[i] - did the agent handle case i correctly?
human_success[i] - did the review path handle case i correctly?
Measured, not assumed, for every escalated case.
"""
s = np.asarray(signal, dtype=float)
accepted = s >= tau
outcome = np.where(
accepted,
np.asarray(agent_success, dtype=int),
np.asarray(human_success, dtype=int),
)
k, n = int(outcome.sum()), int(outcome.size)
lcb = binomial_lower_bound(k, n, alpha)
return {
"n": n,
"coverage": float(accepted.mean()),
"reliability_point": k / n,
"reliability_lcb": lcb,
"target": target,
"qualified": bool(lcb >= target), # the ONLY pass condition
}
Two honest caveats. First, this treats the held-out cases as independent draws from the deployment distribution; if your case set was assembled by convenience sampling from last quarter's easiest queue, the bound is arithmetically correct and substantively meaningless. Second, if you cannot measure the human path on every escalated case — and many teams cannot, because adjudicating a review requires a more senior reviewer — you can substitute an assumed a_h, but then the bound is no longer exact, because you have plugged in an estimate without propagating its uncertainty. The conservative workaround is to use the lower bound of your own audit estimate of a_h in place of the point value, which makes the result pessimistic in a direction you can defend.
A single held-out split supports one qualification decision. If you run the qualification on six candidate agents and ship the one that passed, you have run six tests and reported the best, which inflates your effective error rate well above the alpha you nominated. Either nominate the candidate before you qualify, or apply a multiplicity correction — dividing alpha by the number of candidates is crude but defensible, and at these sample sizes the cost in bound width is smaller than most people expect.
Step five: put the cost in both currencies, with your own rates
Once each candidate has a qualified policy, the choice among them is arithmetic. Cost per 1,000 cases is 1000 * (k_m + k_h * (1 - c)), and the agent with the best benchmark score routinely loses.
The figures below are illustrative worked-example figures, not market rates. Substitute your own contracted API pricing and your own fully loaded reviewer cost. They are shown as two independent tables rather than one table plus a currency conversion, and that is deliberate: model inference is priced globally and converts more or less cleanly, whereas reviewer time does not. A workflow whose reviewers sit in Bengaluru and one whose reviewers sit in Manchester face genuinely different economics for identical technical work, and the whole point of the exercise is that the economics decide.
| Candidate | Escalation rate | Agent cost per case | Review cost per 1,000 | Total per 1,000 |
|---|---|---|---|---|
| Agent A — frontier model plus critic | 12% | £0.300 | £216.00 | £516.00 |
| Agent B — mid-tier model, self-report signal | 31% | £0.020 | £558.00 | £578.00 |
| Agent C — mid-tier model, trained scorer | 26% | £0.050 | £468.00 | £518.00 |
| Candidate | Escalation rate | Agent cost per case | Review cost per 1,000 | Total per 1,000 |
|---|---|---|---|---|
| Agent A — frontier model plus critic | 12% | ₹27.00 | ₹11,400 | ₹38,400 |
| Agent B — mid-tier model, self-report signal | 31% | ₹1.80 | ₹29,450 | ₹31,250 |
| Agent C — mid-tier model, trained scorer | 26% | ₹4.50 | ₹24,700 | ₹29,200 |
The two tables disagree, and the disagreement is the lesson. In the UK example the expensive, highly autonomous Agent A wins, because every escalation it avoids saves £1.80 of reviewer time. In the Indian example the same agent is the worst option by a wide margin, because review is cheaper relative to inference and buying autonomy no longer pays for itself. There is no globally correct answer here; there is only the answer for your cost structure, and a team that runs one analysis and applies it across both markets will overpay in one of them.
Note also that in the UK table, Agent A at £516 and Agent C at £518 are 0.4 per cent apart — comfortably inside the uncertainty of a 375-case estimate. When two candidates land that close, the cost model has stopped discriminating and you should choose on the things it does not capture: which system is simpler to operate, which has the finer routing signal and therefore more headroom to re-tune, which one your team can debug at two in the morning. Pretending a £2 difference is a finding is how spurious precision gets into a business case.
Keep the cost constants in a dated configuration file that the sweep imports, never inline in the harness. As of September 2026 both sides of that equation move — provider pricing and reviewer handling times — and the ability to re-run the entire selection against new constants in one command is what keeps a qualification current rather than historical. It also lets a workflow owner in Bengaluru and one in Manchester read the same measurements through their own rates without either re-running the agent.
Five ways this harness will lie to you
Reusing the development set to qualify. The most common failure and the most damaging, because it produces a number that looks exactly like a valid one. If the threshold was chosen by looking at a set of cases, the reliability estimated on that set is optimistically biased, and the bias is largest precisely at the threshold you picked. The margin delta exists to reduce this at selection time; a genuinely separate held-out split is what removes it. If you have only one labelled set and cannot afford a second, split it anyway — 375 and 375 beats 750 and a lie.
A non-monotonic signal. If accuracy does not rise with the signal, the threshold policy is a coin toss with extra steps. It will still produce a sweep, a selected threshold and a confidence bound, and none of them will generalise, because the relationship they encode does not exist. Run the bin check first, every time, and put the bin table in the qualification report so a reviewer can see the assumption held.
Ignoring the human-review ceiling. A target above a_h is unreachable by construction, and a team that has not written the reliability equation down will spend a quarter trying to reach it by improving the agent. Measure a_h before you set Y. If the target genuinely must be higher than your review path achieves, the work is on the review side — better tooling, dual review on high-value cases, a specialist queue — and that is a different project with a different budget. The design of that queue is a substantial topic in itself; we covered it in designing the queue your agent escalates into, and the capacity arithmetic there is the natural sequel to the escalation rate this procedure hands you.
A target set by the vendor rather than the business. If the number in your qualification report originated in a supplier's benchmark deck, it is not a requirement, it is marketing that has been laundered through a spreadsheet. Vendor-supplied thresholds are chosen to be reachable by the vendor's system. Yours should be chosen to be sufficient for your workflow, and the two coincide only by accident.
Assuming the qualification survives contact with production. It describes one agent on one distribution at one moment. Every element of that decays. Which brings us to the last step.
When to re-qualify
A qualification is a dated artefact, and four categories of change invalidate it outright rather than gradually.
A model version change. Both the accepted accuracy and the distribution of the routing signal move, which means the same numeric threshold now selects a different fraction of traffic at a different accuracy. This applies to silent provider-side updates as much as to deliberate upgrades, which is a strong argument for pinning versions where your provider allows it.
A prompt change. Including changes that look cosmetic. A revised system prompt alters the signal distribution in ways that are not predictable from reading the diff.
A tool change. A new tool, a changed schema, a retrieval index rebuild, a different chunking strategy. Anything that changes what evidence the agent sees changes both terms of the reliability equation.
Input distribution shift. New case types, a new customer segment, a seasonal pattern, a regulatory change that alters what arrives in the queue. This is the one you will not be told about, so it must be monitored rather than announced.
Between full re-qualifications, run a cheap continuous check. Two signals suffice. Watch autonomous coverage on live traffic: if it drifts materially from the coverage measured on the development split, the input distribution has moved even if nothing in your system changed. And audit a small random sample of accepted cases — one or two per cent is plenty — so you have an ongoing estimate of accepted accuracy. Compare it against the accuracy the accepted path must sustain to hold the target at the current coverage, which rearranges straight out of the reliability equation.
def required_accepted_accuracy(target, coverage, a_h):
"""Accuracy the accepted path must sustain to hold the target.
From R = c*a + (1 - c)*a_h >= Y, solve for a. At low coverage the
requirement is loose because the human path carries the system; at
high coverage it approaches the target itself.
"""
if coverage <= 0.0:
return 0.0
return (target - (1.0 - coverage) * a_h) / coverage
def monitor(live_signals, audit_outcomes, tau, dev_coverage,
target=TARGET, a_h=A_H, alpha=0.05,
coverage_tolerance=0.05, min_audit=100):
"""Weekly check. audit_outcomes are labelled ACCEPTED cases only."""
s = np.asarray(live_signals, dtype=float)
coverage_now = float((s >= tau).mean())
k, n = int(np.sum(audit_outcomes)), len(audit_outcomes)
floor = required_accepted_accuracy(target, coverage_now, a_h)
acc_lcb = binomial_lower_bound(k, n, alpha) if n else 0.0
triggers = []
if abs(coverage_now - dev_coverage) > coverage_tolerance:
triggers.append("coverage_drift")
if n >= min_audit and acc_lcb < floor:
triggers.append("accepted_accuracy_below_floor")
return {
"coverage_now": coverage_now,
"accepted_accuracy_lcb": acc_lcb,
"accuracy_floor": floor,
"requalify": bool(triggers),
"triggers": triggers,
}
Neither trigger is a rollback on its own. Both are a signal to re-run the full procedure on a fresh case set, which — if you built the harness as a script rather than a notebook — is an hour of compute rather than a fortnight of project. That is the real return on doing this properly: the first qualification is expensive, and every one after it is cheap.
What to do next
Start with the two things you cannot borrow from anyone else. Write the success criterion with the workflow owner, and measure the accuracy of your existing human-review path. Those two artefacts determine whether the rest of the procedure is even worth running, and both are conversations rather than code.
Then work through it in order. Set Y from the downstream cost of an error and record where it came from. Pick a routing signal and run the bin check before anything else. Sweep the threshold on a development split against Y plus a margin. Freeze the policy, qualify on held-out cases with a one-sided lower bound, and report the bound rather than the point estimate. Cost the survivors in your own currency at your own reviewer rates. Then put the coverage figure in front of whoever staffs the review queue, because that number, not the benchmark score, is what they will be living with.
The discipline this imposes is uncomfortable in a good way. It forces a target to exist before a model is chosen, it makes the oversight burden visible at decision time rather than in month three, and it produces a single defensible sentence for the risk committee: at 95 per cent confidence, this system with this escalation threshold meets the reliability level this workflow requires. As of September 2026 that sentence is still rare, which is exactly why it is worth being able to say.