Chapter 5 of 7 · 15 min read
Entailment reads the passages, and breaks on negation and numbers
On this page
Natural language inference, or NLI, is a three-way classification task with a long history in NLP, defined over pairs of texts: given a premise, is the hypothesis entailed, contradicted, or neither. Applied to a generated answer, the premise is a retrieved passage and the hypothesis is a claim the answer made, and the score of interest is the probability of the entailment label.
That input signature is the whole reason this chapter exists. Every signal so far — a number the model stated, the probabilities of the tokens it emitted, the spread of its own resamples — is a function of one object, the conditional distribution the answer came out of. An entailment score is a function of two: the retrieved text and the generated text, taken separately. The absence that has no representation in a conditional distribution has an obvious representation here, which is that no premise raises the entailment probability above the floor. A signal cannot report on a comparison it was never given both sides of, and this is the first one given both sides.
The shape of the check
The SNLI and MNLI datasets — the Stanford and the Multi-Genre collections of premise-hypothesis pairs labelled by hand — fixed the task's shape in 2015 and 2018, and the cross-encoders people deploy today are direct descendants. A cross-encoder reads the two texts jointly in a single pass and scores the pair, rather than embedding each of them separately and comparing the vectors, which is what lets it be sensitive to the relation between them rather than to their overall similarity. DeBERTa-v3-large fine-tuned on MNLI plus a few adjacent sets runs at roughly 400M parameters and a few tens of milliseconds per pair.
Applying it to a RAG answer requires two decisions the NLI model does not make for you. The answer has to be split into claims, because a paragraph is not a hypothesis and scoring it whole gives a neutral verdict on anything with more than one assertion in it. And the per-claim scores have to be aggregated into one number.
from transformers import pipeline
nli = pipeline("text-classification",
model="MoritzLaurer/DeBERTa-v3-large-mnli-fever-anli",
top_k=None)
def support_score(claims, passages):
"""Per-claim max entailment across passages; answer score is the weakest claim."""
per_claim = []
for c in claims:
best = 0.0
for p in passages:
out = {d["label"]: d["score"] for d in nli(f"{p}</s></s>{c}")[0]}
best = max(best, out["entailment"])
per_claim.append((c, best))
return min(s for _, s in per_claim), per_claim
score, detail = support_score(claims, retrieved)
# score = 0.04 -> abstainThe two aggregations in that function pull in opposite directions and both choices are deliberate. Across passages the maximum is right, because support is existential: a claim is supported if some passage establishes it, and the other five saying nothing is the normal case rather than evidence against. Across claims the minimum is right, because support of an answer is universal: an answer with one unsupported sentence is an answer that will mislead somebody, and a mean would let four well-supported claims outvote it. The minimum also has a specific and common false-abstention mode, described below, and that mode is the price of the universal reading rather than a bug in it.
Claim decomposition is the step most likely to be underestimated. Sentence splitting is a poor proxy, because a single sentence often carries two assertions joined by a comma and a conjunction, and a claim that mixes a supported half with an unsupported half scores in the middle and lands on neither side of any threshold. The workable approach is a small model call that rewrites the answer into self-contained atomic statements with pronouns resolved, which costs roughly 200 output tokens and 300 ms per answer, and which introduces its own error mode: the decomposer occasionally invents a claim the answer did not make, or drops a qualifier such as "in the staging environment" that was the only thing making the sentence true. Auditing 200 decompositions by hand before trusting the pipeline is a day of work that pays for itself, because a decomposition bug looks exactly like an entailment bug from the outside — both present as a claim scoring low against passages that a human would say support the answer.
The cost profile is modest compared with the sampling signal, and it is arithmetic rather than a benchmark. Four claims against six passages is four times six, or 24 cross-encoder pairs. Add pairwise concatenations of the top three passages, which is three unordered pairs per claim and so twelve across four claims, and the total is thirty-six — the batch of 36 short pairs quoted below. Choosing the top four instead of the top three would raise the per-claim comparisons from 6 to about 12, since four passages make six unordered pairs, and doubles the concatenation half of the bill.
A DeBERTa-v3-large batch of 36 short pairs runs in roughly 120 ms on a single T4, an entry-level inference GPU, with batching, or about 40 ms per pair unbatched — so the same thirty-six pairs run one at a time would take well over a second, and batching is what makes this affordable rather than the model being small. Adding the decomposition call at around 300 ms, the whole check comes to about 450 ms of p50 latency, and the two components account for most but not quite all of that; the remainder is the tokenisation and marshalling around them. Against that, five samples cost 2.1 seconds and five times the token spend. There is no per-token API cost at all if the NLI model is self-hosted. Running it on GPU alongside the retriever is the usual deployment, and the check is cheap enough that it can run on every answer rather than only on suspicious ones.
Reading the four signals against the retrieval split
Measured over 2,000 graded runbook answers, this scores at AUROC 0.79, and — the property that matters — it does not collapse on the hard half.
| Subset | AUROC, stated | AUROC, logprob | AUROC, 5-sample agreement | AUROC, entailment |
|---|---|---|---|---|
| Gold passage retrieved | 0.53 | 0.66 | 0.71 | 0.81 |
| Gold passage absent | 0.50 | 0.49 | 0.52 | 0.74 |
Read that table by rows rather than by columns. Across the top row the four signals span fewer than thirty hundredths and every one of them is doing something; across the bottom row three of the four are within two hundredths of a coin and the fourth is not. The gap that carries the argument is vertical, not horizontal: it is how far each column falls between the two rows. Stated confidence falls by three hundredths because it had nowhere to fall from. The logprob falls by seventeen and agreement by nineteen. Entailment falls by seven and lands at 0.74.
The causal chain behind those numbers is short and each link is checkable. The gold-absent stratum is the condition in which the answer was produced without support from the retrieved text. Three of the signals are model-internal, computed from the model's own conditional distribution, and that distribution is peaked whether the peak came from a passage or from the weights, so those three cannot distinguish the stratum from its complement. The entailment check is computed from the passages and the claim as separate inputs, and the stratum is defined by a property of exactly that pair — no passage supporting the claim — so the condition it is asked to detect is the condition that defines the stratum. It keeps three quarters of its discriminative power there because the retrieval gap is precisely the condition it is defined to detect.
The pooled 0.79 is a mixture of those two rows, and the mixture weights are the retrieval hit rate. Chapter 3's split of the same 2,000 answers put five hundred and fourteen in the gold-absent stratum and fourteen hundred and eighty-six in the other; weighting 0.81 and 0.74 by those counts lands on the 0.79 quoted for the corpus as a whole. That identity is worth more here than anywhere else in the topic, because entailment is the only signal that does not collapse on the hard half. Since the pooled figure is a blend of a high number on the easy stratum and a merely-lower number on the hard one, and the competing signals blend a comparable number with a coin, the lead this signal holds widens as the retrieval hit rate falls. A deployment with worse retrieval than this one gets more out of the check, not less — which inverts the usual expectation that a weaker pipeline benefits less from a better instrument. As in chapter 4, AUROC does not decompose as a size-weighted average in general; this is an identity holding on these numbers rather than a theorem.
Three ways it goes wrong
The 0.74 is not 1.0, and the errors are not scattered. They cluster into three shapes, and knowing the shapes is what lets you predict which of your questions the check will mishandle before you deploy it.
| Failure | Premise (retrieved) | Hypothesis (claim) | Model verdict | Truth |
|---|---|---|---|---|
| Negation | "The consumer does not retry on 5xx from the partner API." | "The consumer retries on 5xx from the partner API." | entail 0.62 | contradiction |
| Numeric edit | "Retries: 5 attempts, 30s base, 15m cap." | "The consumer retries 3 times with a 30s base and a 15m cap." | entail 0.58 | contradiction |
| Split support | p3: "The payments consumer is wh-pay-01." p7: "wh-pay-01 retries 5 times." | "The payments webhook consumer retries 5 times." | entail 0.11 (best single passage) | entailed jointly |
Negation and numeric edits share a mechanism, and the token counts in the table are what make it concrete. A cross-encoder computes a similarity-dominated representation of the pair, so the size of the change in its input is roughly the size of the change in wording, and both perturbations leave nearly every token identical while inverting the meaning. "Does not retry" against "retries" differs by two tokens out of fourteen, and "5 attempts" against "3 times" by one. In both cases the perturbation to the input is a small fraction of the pair while the change in truth value is total — from entailed to contradicted, with nothing in between. That is precisely the regime a similarity-dominated representation cannot see, because it has no channel in which a one-token change can produce a maximal change in the output.
These are known weaknesses of MNLI-trained models rather than a quirk of one checkpoint. On a hand-built probe set of 120 negation-flipped claims drawn from a runbook corpus, the model labelled 41% as entailed; on 96 numeric perturbations, 33%. They matter more here than in general NLP because operational documentation is the worst possible corpus for them: it is full of thresholds, counts, timeouts, and sentences whose entire content is a prohibition. A domain whose facts are numbers and negations is a domain where the input differences that carry the meaning are the smallest ones present.
Split support is a different kind of failure and it costs you in the opposite direction. Scoring each claim against each passage independently means a claim whose evidence spans two chunks is entailed by neither, so a correct, fully supported answer gets a low score and the system refuses it. The independence is a design decision rather than a requirement, and it is the same decision that gives the check its cost profile — pairs scale with the product of claims and passages, and joint scoring over subsets scales far worse. Concatenating all the passages into a single premise addresses the problem in principle, and in practice the cross-encoders are trained on premises of one to three sentences and degrade on a 2,000-token concatenation, with the additional problem that the 512-token limit forces truncation. The workable compromise is to score against every passage individually and against pairwise concatenations of the top three or four, which catches most two-hop cases, the ones where the claim needs a fact from each of two different passages, at the modest bill computed earlier.
There is a fourth error source that is not the NLI model's fault, and it is a direct consequence of the minimum-over-claims choice defended above. Answers contain decorative sentences — "This is documented in the on-call runbook" or "Let me know if you need the full configuration" — and a minimum-over-claims aggregation scores them like assertions. In one week of production traffic, 18% of abstentions traced to a sentence of that kind scoring 0.05 while every substantive claim in the same answer scored above 0.8. The same rule that stops four supported claims outvoting one unsupported one is what lets a closing pleasantry veto an answer, because the rule has no notion of which sentences are load-bearing. Filtering claims to those containing a checkable predicate, or excluding the first and last sentence when they match a small set of templates, removed most of that without touching the true-positive rate.
The natural response to a signal with three named failure shapes is to add the other three signals back and let a fitted model weigh all four, and it is worth being clear about why that is the wrong move in a safety-relevant deployment rather than merely an unnecessary one. The four inputs are not four opinions. Three of them are functions of the same conditional distribution, so on the retrieval-gap case the ensemble receives three high votes and one low one, and the low one is the only correct vote. Any weighting fitted to overall accuracy will learn to trust the majority, because the majority is right more often across the corpus as a whole — the retrieval-gap stratum is the minority of questions, so a fit that optimises the average is a fit that optimises away the very case the check was added for. The weights are also fitted to whatever question mix the evaluation set happened to contain, so the model's behaviour on a question type that was under-represented is unspecified. And the output is a number nobody can explain to the person affected by it: when a clinician asks why the assistant refused, "claim 2 of your answer scored 0.04 entailment against all six retrieved passages, and here they are" is an answer, while "the ensemble scored 0.41" is the beginning of an argument. If the other signals are used at all, the defensible arrangement is as a second gate rather than a summed vote — entailment decides whether there is support, and low agreement across samples can add a hedge to an answer that already passed.
Grounding, truth and responsiveness
An entailment score says the retrieved passages support the claim. It does not say the claim is true, because a passage can be outdated, wrong, or from a document that was superseded two quarters ago, and the check will happily certify a claim that faithfully reproduces it. Grounding and correctness are different properties, and a system with a perfect entailment gate over a stale corpus produces confidently wrong answers that are provably supported. That failure belongs to corpus governance rather than to the abstention layer, and it has a nasty measurement property: it does not become visible in any evaluation that grades answers against the same documents the system retrieves, because such an evaluation shares the error.
It is also not the same thing as the attribution work in topic 9.12. A citation is a claim about where support lives, and verifying that a cited passage actually supports the sentence attached to it is one application of exactly this machinery. The signal here runs over the whole retrieved set regardless of what the answer cited, and that difference in scope is what makes it catch a different failure: unsupported claims that carry no citation at all. That is the more common failure of the two, since a model producing an ungrounded answer usually also declines to cite anything for it, and a checker that iterates over emitted citations never sees it.
And an entailed claim is not necessarily a responsive one. A model that answers "the payments consumer is deployed via ArgoCD" to a question about retry budgets will pass the check with a high score, because the sentence is fully supported and completely beside the point. Relevance to the question is a separate axis — it is a relation between the answer and the question, and the check reads the answer and the passages — so an abstention gate that trips on unsupported claims will let every well-grounded evasion through.
Where this is heading is worth knowing even if it is out of scope today. General-purpose NLI is a blunt tool for this job, and the newer grounding-specific models — AlignScore, and the MiniCheck family trained on synthetic document-claim pairs — target document-level premises directly, which is the split-support problem attacked at its root, and report better numbers at lower cost than prompting a large model to judge. LLM-as-judge entailment is the other direction, and it handles the split-support and negation cases far better at roughly 40× the latency and a per-check cost that scales with the passages. The survey Know Your Limits: A Survey of Abstention in Large Language Models maps the whole space including several signals not covered here, and the uqlm library implements the logprob, sampling and judge-based scorers behind one interface if you want to compare them on your own corpus without writing four harnesses.
A signal at 0.79 with a known failure shape is enough to build on, and knowing the shape is most of what "enough" means: an error you can predict is an error you can route around, and the three named here are all predictable from the vocabulary of your own documents. Building on it means picking a number below which the system refuses to answer.
The last four chapters have all been about the score, and none of them has said where the cut goes. That is not an omission the score can repair, because the cut is a statement about what a refusal costs relative to a wrong answer, and no property of the signal contains that.
Uncertainty, confidence and abstention — read the current version at https://daviddominguez.dev/learn/en/uncertainty-confidence-and-abstention/entailment-reads-the-passages-and-breaks-on-negation-and-numbers