Guide
Abstention: when an AI classifier should not decide
When an LLM classifier should abstain instead of answering, how to set the threshold from labeled data, and what to do with the cases it hands back.
Updated - 8 min read
A classifier that must always pick a label will pick one for inputs it cannot classify: a message that is half billing question and half bug report, a request missing the one fact the policy turns on, an input that belongs to none of the categories. Abstention gives it a third option besides right and wrong: no answer, plus a reason. Designed well, it is how an AI decision system stays honest about the cases it should not handle. Designed badly, it is a queue nobody owns.
This guide covers when an LLM classifier should abstain, how to set the threshold from labeled data, and what to do with the cases it hands back.
Abstain, review, refuse: three different outcomes
These get used interchangeably, and keeping them apart makes the system easier to reason about.
| Outcome | What it means | What happens next |
|---|---|---|
| Review | There is a plausible answer, but not one you trust enough to act on alone. | A person confirms or overrides the suggested answer. |
| Abstain | There is no answer worth suggesting. | Get more information, or hand the case to a person without a suggestion. |
| Refusal | The model declined to answer the question. | Treat it as an abstention with a different reason, and count it. |
In the three-zone policy used across this site, abstention is the bottom zone, labeled “don’t decide”. The research literature calls the same idea a reject option or selective classification: C. K. Chow analyzed the trade-off between errors and rejections for pattern recognition in 1970, and it applies unchanged to LLM classification.
Why a classifier needs a way out
Three kinds of input produce answers that look like decisions but carry little information.
- Ambiguous inputs. The text supports two labels about equally. Any single label is close to a guess, and a confident-looking guess is worse than none.
- Missing information. The policy turns on a fact that isn’t in the input, such as the purchase date for a refund window. The right action is to ask for it.
- Out-of-scope inputs. A spam message sent to a routing decision whose choices are sales, support, and billing. Every available label is wrong.
Low confidence is one signal for all three, but not the only one. The next sections cover confidence first, then the other signals.
The trade-off: coverage against accuracy
Every abstention threshold trades coverage (the share of inputs that get an answer) for accuracy on the inputs that do. Raise the threshold and the answered set shrinks and gets more accurate. Plotted across thresholds, this is a risk–coverage curve.
| Answer when support is at least | Coverage | Accuracy on answered | Errors | Abstained |
|---|---|---|---|---|
| 0.50 (never abstain) | 100% | 88.0% | 120 | 0 |
| 0.70 | 90% | 92.2% | 70 | 100 |
| 0.80 | 80% | 95.0% | 40 | 200 |
| 0.90 | 60% | 98.0% | 12 | 400 |
| 0.95 | 40% | 99.0% | 4 | 600 |
Read the second row carefully. Moving from 0.50 to 0.70 removes 100 inputs and 50 errors: the inputs it set aside were right only half the time, so the system loses little by not answering them. Moving from 0.90 to 0.95 removes 200 inputs and only 8 errors: those answers were 96% right, and abstaining on them throws away useful work. The shape of the curve, not a rule of thumb, tells you where abstention stops paying for itself.
Setting the abstention threshold
The automation threshold and the abstention threshold answer different questions, so they need different measurements.
- Automation asks: is everything above this line accurate enough to act on without a person? That is a cumulative measure over the whole automated set.
- Abstention asks: are answers at this level worth showing at all? That is a local measure: accuracy within the band just above the line.
A cumulative figure hides weak bands. If answers between 0.50 and 0.60 are right 46% of the time but everything above 0.50 averages 88%, the average says “fine” while the band says “coin flip”. Measure band by band:
// Accuracy within each confidence band: the question for the abstention threshold.
// rows: [{ expected, predicted, confidence }], as in the calibration guide.
function bandAccuracy(rows, edges = [0.5, 0.6, 0.7, 0.8, 0.9, 1.0001]) {
return edges.slice(0, -1).map((low, i) => {
const inBand = rows.filter((r) => r.confidence >= low && r.confidence < edges[i + 1]);
const correct = inBand.filter((r) => r.predicted === r.expected).length;
return { band: [low, edges[i + 1]], n: inBand.length, accuracy: inBand.length ? correct / inBand.length : null };
});
}| Support band | Decisions | Accuracy in band | Reading |
|---|---|---|---|
| 0.90 – 1.00 | 600 | 98% | Automate if the costs allow |
| 0.80 – 0.90 | 200 | 86% | A strong suggestion for review |
| 0.70 – 0.80 | 100 | 70% | A usable suggestion for review |
| 0.60 – 0.70 | 50 | 54% | Barely better than guessing |
| 0.50 – 0.60 | 50 | 46% | Worse than guessing: abstain |
Compare each band with what you would get without the model. For a balanced yes/no decision the floor is 50%. In general, it is the share of the most common label: if 70% of messages belong to support, a band where the model is right 65% of the time is worse than always answering “support”. A band with only a handful of decisions is too noisy to judge on its own; merge it with a neighbor before deciding.
Abstain on more than confidence
Confidence catches ambiguity. The other two causes are better caught directly.
- Refusals. In the Decisions API any question can come back as
{ "type": "refusal" }. Route it like an abstention and keep it as a separate reason, so a rise in refusals is visible. - Missing inputs. Check required fields before asking the model. If the refund window depends on a purchase date you don’t have, no confidence threshold will produce the right answer.
- Out-of-scope inputs. Give a choice an explicit
otheroption, as the feature request classification template does, and treat it as an abstention. Or add an evidence check such as “the message is a customer request” and abstain when it is unlikely.
// Decide whether there is an answer worth using before deciding whether to automate it.
function triage(answer, policy) {
if (answer.type === "refusal") return { zone: "abstain", reason: "declined" };
// Support for the selected answer: for a predicate, the probability of the side it chose.
const support =
answer.type === "predicate"
? Math.max(answer.probability, 1 - answer.probability)
: answer.confidence;
if (support >= policy.automateAt) return { zone: "automate" };
if (support >= policy.abstainBelow) return { zone: "review" };
return { zone: "abstain", reason: "low support" };
}What to do with an abstained case
An abstention is only useful if something happens next. Pick the action by the reason.
| Reason | Better next step |
|---|---|
| Missing information | Ask the customer for the specific fact, or look it up in another system, then decide again. |
| Ambiguous input | Send to a person without showing the model's weak guess. An answer the model barely preferred still anchors the reviewer. |
| Out of scope | Route to a general queue, or to the decision that does cover it. |
| Refusal | Send to a person, and review refusals in aggregate for a pattern. |
Give the abstention queue an owner and a response time, like any other queue. The human review guide covers why these cases should not share a queue, or a presentation, with ordinary reviews.
Measure abstention like any other outcome
- Report coverage beside accuracy. “98% accurate” means little without “on the 60% of inputs it answered”.
- Check that abstention catches the hard cases. When abstained cases are later resolved, compare the model’s (unused) answers on them with the outcome. If they were right as often as everything else, abstention is costing coverage and catching nothing.
- Watch the abstention rate. A rising rate is often the earliest sign that inputs have changed: a new product, a new kind of request, a new language. It moves before accuracy does, because accuracy needs labels and the rate does not.
Common mistakes
- Dropping abstentions from the denominator and reporting accuracy as if every input was answered.
- One threshold for every decision. Bands differ by task, and a choice with four labels has a different floor from a yes/no question.
- Setting the threshold on the data you report. Choose it on one set of labeled examples and confirm it on another.
- Never revisiting it. A model, prompt, or policy change can move every band. Re-measure after each one.
Related guides
- Guide
Confidence thresholds
Probability, confidence, accuracy, calibration, and correctness are different things. How to set a review policy from the cost of each kind of mistake.
- Guide
Human-in-the-loop AI
When should AI decide and when should it ask? Review patterns, what reviewers need to see, sizing the queue, and keeping oversight real.
- Guide
Calibration metrics
Three ways to measure calibration, what each one hides, and which to use for which question, with code that computes them from labeled decisions.
Templates that use these ideas
- Predicate
Automate or review
Whether a request is safe to resolve automatically or needs a person.
- Choice
Feature request classification
Whether a message is a bug report, a feature request, a how-to question, or something else.
- Predicate
Policy exception
Whether a request that falls outside policy deserves an exception.
Put it into practice
Try a decision, check it against labeled examples, and talk to us if you want help building one for your workflow.