Founders

How to Turn Adherence Scores into a Batch Go/No-Go Rule

Antonio Evans9 min read

A factory conveyor belt with batches of items moving toward a gate.
Key points

Key points

  1. 1Every adherence or drift score becomes actionable only when you convert it into an expected cost, then compare that cost to the cost of the alternative action.
  2. 2The break-even threshold is s* = cost_false_accept / (cost_false_accept + cost_false_reject), so the cutoff is a business input, not a modeling output.
  3. 3A gate should block low-cost batches and flag high-cost batches: the same threshold logic drives both, but the action depends on what a rejection costs you.
  4. 4Log the score, the threshold in force and the decision for every batch, so the rule can be audited and repriced when costs change.

Start from the decision, not the metric

Most teams compute a drift or adherence score and then stall. The score is a number between 0 and 1, or an error rate, or a distance from a reference distribution. None of those numbers tells you what to do with the batch sitting in front of you. The conversion step is a cost model, and it is the same arithmetic that cost-sensitive learning uses to move a classifier's decision threshold away from the default 0.5.

The framing comes from binary classification under asymmetric costs. Every batch decision has four outcomes, and only two of them are errors. If you pass a batch that was actually degraded, you pay a false accept cost. If you block a batch that was actually fine, you pay a false reject cost. The standard derivation, worked through with a fraud example in this cost-sensitive learning write-up, shows that the threshold depends only on the ratio of those two costs. Correct decisions are assumed to cost nothing, which is close enough for a first gate.

The practical consequence: you cannot pick the threshold from the score's own distribution. You pick it from what happens on the plant floor, in the invoice run, in the support queue.

Write down the cost of each error

Before any math, get two numbers on the record. Both should come from people outside the data team, because the data team usually does not own the consequence.

Cost of a false accept (C_FA). What does it cost, per batch, if a degraded batch ships? Include the obvious part (rework, refunds, a support surge) and the part people forget (the customer who never comes back, the compliance finding, the manual clean-up that lands on an unrelated team). If you have a range, take the midpoint and write the range next to it.

Cost of a false reject (C_FR). What does it cost, per batch, if a good batch is held? For a hard block this includes idle capacity, missed service levels, a manual review pass, and the argument with the operations lead. For a flag it is usually much smaller: one reviewer-hour.

Two rules keep this honest. First, state the unit. Costs per batch do not compare to costs per record, and a score computed per record needs to be aggregated before the comparison. Second, write down who supplied each number. When the threshold is challenged in six months, the sourcing is the defense. The discipline of pinning value claims to named inputs is the same one pricing researchers apply when they interview buyers about willingness to pay rather than guessing at it, as described in this willingness-to-pay interview template.

A worked example. Suppose a daily document-extraction batch feeds an accounts-payable process. A bad batch that passes means downstream re-keying and a few supplier escalations, which your operations lead puts at 4,000 units of currency per batch. Blocking a good batch means a reviewer spends an hour and the payment cycle slips a day, which the finance lead puts at 500 units per batch.

Map the score to expected cost

Treat the adherence score s as your best estimate of the probability that the batch is acceptable. That is a strong assumption, and it is worth checking, but it is the assumption every threshold rule rests on.

Given that, the expected cost of passing the batch is the probability it is bad times the false accept cost. The expected cost of blocking it is the probability it is good times the false reject cost.

cost_pass(s)   = (1 - s) * C_FA
cost_block(s)  = s * C_FR

Set them equal and solve for s:

(1 - s*) * C_FA = s* * C_FR
C_FA = s* * (C_FA + C_FR)
s* = C_FA / (C_FA + C_FR)

That is the whole rule. It is the same formula derived for classifiers in the scikit-learn cost-sensitive threshold example, where a bad loan classified as good is five times more costly than the reverse, and the cutoff moves accordingly.

With the AP example: s* = 4000 / (4000 + 500) = 0.889. Any batch scoring at or above 0.889 passes. Anything below blocks. Notice that a very high cutoff is correct here precisely because a false accept is eight times more expensive than a false reject.

Now flip the costs. If a bad batch costs 500 and holding a good one costs 4,000, then s* = 500 / 4,500 = 0.111. You would pass almost everything and let the flagging step handle the rest. That feels wrong to engineers trained on score distributions, and it is right if the numbers are right. If your threshold ends up surprising, audit the costs before auditing the math.

SituationC_FA (per batch)C_FR (per batch)s*Reading
AP extraction, rework is expensive4,0005000.889Block aggressively
Internal analytics refresh2001,5000.118Pass, flag only the worst
Regulated output, audit exposure50,0002,0000.962Block and page a human
Marketing asset variant3003000.500Symmetric, score itself is the tie

The last row is worth internalizing. When the two errors cost the same, the threshold collapses to 0.5 and the cost model adds nothing. That is not a failure of the method, it is the method telling you the decision is genuinely balanced.

Check that the score means what you think

The formula assumes s is a calibrated probability of acceptability. Most adherence scores are not. They are cosine similarities, judge ratings, distribution distances, or pass rates from a rubric. Three cheap checks:

Check the monotonic relationship. Bucket your historical batches by score and measure the observed failure rate in each bucket. If the 0.80 to 0.90 bucket fails 10 percent of the time and the 0.70 to 0.80 bucket fails 40 percent of the time, the score orders well. If the buckets are flat, the score is not a probability and the threshold is arbitrary. Drift and behavior monitoring needs this kind of baseline comparison before any alert is trusted, which is the point made in this overview of production monitoring metrics.

Check for drift in the score itself. A model or rubric whose inputs have shifted will produce scores you cannot interpret the same way. Data drift is a shift in the input distribution, and it can degrade performance before anyone notices in the output because most silent failures start upstream, as the same monitoring guide notes. If you have to re-derive the score's meaning, re-derive the threshold too.

Check for drift that no single score catches. Behavior can shift while every individual event still looks normal, because the change lives in the sequence rather than in any one step, a gap described in this analysis of intent drift in agents. Batch-level gates are coarse by design; if your risk lives in ordering, a batch score will miss it and you need a different detector.

If calibration is off but the ordering is good, you have options. Refit a lightweight calibration on recent data, or use a rank-based threshold (block the worst 2 percent of batches) and only then convert to a score. Rank-based rules are easier to defend operationally and harder to reason about in cost terms. Pick one and document it.

Wire the rule into the pipeline

The code is short. The design choices around it are where gates go wrong. Here is a minimal gate that computes both expected costs and returns a decision plus the numbers behind it.

def gate(score, c_false_accept, c_false_reject, mode="block"):
    if not 0.0 <= score <= 1.0:
        raise ValueError(f"score out of range: {score}")
    s_star = c_false_accept / (c_false_accept + c_false_reject)
    cost_pass = (1 - score) * c_false_accept
    cost_block = score * c_false_reject
    if score >= s_star:
        decision = "pass"
    else:
        decision = "block" if mode == "block" else "flag"
    return {
        "score": score,
        "threshold": round(s_star, 4),
        "cost_pass": round(cost_pass, 2),
        "cost_block": round(cost_block, 2),
        "decision": decision,
    }

Calling gate(0.92, 4000, 500) returns a threshold of 0.8889 and a pass. Calling gate(0.85, 4000, 500) returns a block, and shows cost_pass of 600 against cost_block of 425, which is the quantitative argument for the hold.

Four design rules make this survive contact with production.

Fail closed on missing scores. If the scoring job crashes, you get None, not 1.0. Reject the batch into a review queue rather than defaulting to pass. A missing score carries no information, so it should not be treated as a good one.

Split the flag path from the block path. Blocking stops the pipeline. Flagging routes to a reviewer and lets the batch continue on a provisional basis. The choice between them is itself a cost decision: use flag when the false reject cost is high or when review capacity exists, and block when the false accept cost is catastrophically high. Human oversight only works when reviewers have real authority and the context to exercise it, not a rubber stamp, as this guide to human oversight for AI agents argues.

Version the threshold with the costs. Store the cost inputs alongside the threshold value. When a new rule takes effect, the old batches should still render with the threshold that applied to them.

Make the decision auditable. Log batch ID, score, threshold in force, cost inputs, decision, and the reviewer identity where a human overrode the gate. That log is what lets you compute realized costs later and is the evidence trail for any later dispute.

Decide block versus flag with the same arithmetic

Teams often treat block and flag as a policy question. It is a cost question. Blocking is the right action when the expected cost of passing exceeds the expected cost of blocking and the block itself is cheap. Flagging is right when the false reject cost is high enough that a hard stop is worse than a delayed decision.

A simple test: compare the cost of holding a good batch for a review cycle against the cost of a bad batch slipping through during that same cycle. If holding is cheap (a reviewer hour), flag almost everything in the gray zone and block only the clearly bad. If holding is expensive (idle line, missed contractual deadline), a hard block needs a much higher false accept cost to justify it.

ActionUse whenFailure mode
PassScore at or above thresholdBad batch ships silently
Flag for reviewFalse reject cost is high, review capacity existsReviewer queue grows, decisions get stale
BlockFalse accept cost is catastrophicLine idles, team routes around the gate

Watch for the third row's failure mode specifically. Any gate that stops work invites a manual override, and an override path that is easier than the review path becomes the real policy. If you see override rates climbing, the threshold is wrong, the costs are wrong, or the review queue is too slow.

There is also a budget dimension. If reviewer attention is the scarce resource, you are choosing where to spend audit calls rather than whether to spend them. Research on budgeted auditing for multi-agent systems frames the problem exactly this way: place audits where cumulative unchecked exposure is highest rather than auditing everything, and end-to-end cost falls while guard performance stays close to the clean setting, per this arXiv paper on budgeted runtime auditing. A batch gate with a review queue is the same trade in simpler clothing.

Validate the rule before you trust it

A threshold computed from costs is a hypothesis. Validate it on history before it touches live traffic.

Backtest on labeled batches. Take the last few hundred batches where you know the outcome. Apply the candidate threshold and count false accepts and false rejects. Multiply each by its cost and sum. Compare against two alternatives: pass everything, and the threshold you would have picked by eyeballing the score distribution. The cost-based threshold should win or tie. If passing everything wins, your false accept cost is probably too low or your false accept rate is near zero, and the gate is not earning its operational burden.

Stress the cost inputs. Move C_FA and C_FR by plus or minus 50 percent and watch the threshold move. If a small change in an input flips the decision on a large share of batches, the rule is fragile or the costs are uncertain. In the AP example, raising C_FR from 500 to 750 moves s* from 0.889 to 0.842. That is a real but manageable shift. If the same change moved the threshold from 0.9 to 0.5, you would need better cost estimates before shipping.

Track realized outcomes after launch. Each week, compute the cost the gate actually incurred: false accepts caught in downstream review times their cost, plus batches held times their holding cost. Compare to the counterfactual of passing everything. This is the only number that matters long term, and it is the number the cost model promised.

Re-derive when costs move. A supply contract change, a new customer tier, a regulatory shift, or a change in review staffing all move the inputs. Put a calendar reminder on the cost model. A stale threshold is worse than no threshold because it looks maintained.

Where the gate is one component of a larger pipeline, it helps to have one place to run the models behind the score. Quanta Inference puts text, image, video, audio and embedding models behind one OpenAI-compatible key, which keeps the scoring step and the gate in the same operational shape.

FAQ

FAQ

Why not just pick the threshold that maximizes accuracy?

Accuracy treats both errors as equally costly, which is wrong whenever they are not. The cost-based threshold formula reduces to 0.5 only when the two costs are equal, as the derivation in the cost-sensitive learning tutorial shows. When a false accept costs eight times a false reject, the accuracy-maximizing threshold will pass batches you should block.

How many batches do I need before the backtest means anything?

Enough that each score bucket contains a meaningful number of failures, not just passes. If your failure rate is 2 percent, a hundred batches gives you roughly two failures, which cannot separate a good threshold from a bad one. Aim for at least a few dozen observed failures across the score range, and widen the buckets until each one has some.

What if I cannot estimate the false accept cost?

Estimate a range and use the midpoint, then run the sensitivity check. If the threshold barely moves across the range, the uncertainty does not matter. If it moves a lot, you have found the real problem: you need better cost data or a more conservative default. In the meantime, prefer the flag action over the block, since a flag is cheap to reverse.

Does this work for scores that are not probabilities?

Yes, if the score orders batches by failure risk. The threshold formula assumes s is a calibrated probability, so an uncalibrated score gives you a threshold with the right shape and the wrong absolute position. Check monotonicity by bucket as described above, and consider refitting a calibration layer before trusting the numeric cutoff.

When should the threshold be recomputed?

Whenever a cost input changes, and on a fixed review cadence even when nothing appears to have changed. The second part matters because estimated costs drift quietly as volumes, customer mix and staffing change. Log cost inputs with every decision so the recomputation has a starting point, and make the threshold a configuration value rather than a constant buried in code.

Sources

Sources

  1. Cost-Sensitive Learning: Beyond the Accuracy in ...
  2. Pricing and willingness to pay interview questions
  3. Post-tuning the decision threshold for cost-sensitive learning
  4. AI model and agent monitoring: Metrics, drift detection, and ...
  5. Detecting Intent Drift in AI Agents With Runtime Behavioral Data
  6. AI Agent Human Oversight: A Complete Guide
  7. Budgeted Runtime Auditing for LLM Multi-Agent Systems ...
About the author

About the author

Antonio Evans
Antonio Evans

Antonio Evans is the founder of Quanta Intelligence and a fractional CTO who builds HIPAA-ready AI systems for health and life-science companies. Before that he was technical co-founder of FriendsEAT for twenty years, CEO of Socialdraft, and a data engineer at IBM. Antonio lives with his dog Santino in Lisbon, Portugal.

All articles