Part 2 of **The Answerability Problem. Part 1 showed the standard harness excluding the questions that test refusal, my own system scoring 0.000, and every lever I ship failing. It asked whether any cheap signal can separate "relevant" from "answerable." This is the bake-off. Code: RE-call.
LongMemEval is the other public benchmark in this space, and unlike LOCOMO it names abstention as a first-class question type. 500 questions over a synthetic user's chat history: 470 answerable, 30 whose answer was never mentioned.
My retrieval does well on it. The right session is in the returned set 97% of the time.
Then the trust layer refuses to answer 48% of those.
Sit with the shape of that. The system finds the answer, holds it, and declines to use it, in nearly half of the cases where it succeeded. As a product that is worse than having no abstention layer at all: you have paid for retrieval quality and then thrown it away at the last gate.
And it gets stranger. False-abstain rises as the problem gets easier: 0.328 → 0.409 → 0.481 as I narrow the haystack, while the fitted threshold falls (0.752 → 0.723 → 0.713). A smaller haystack means a better top hit means higher cosines all round, and every one of those higher cosines belongs to an unanswerable question too.
The obvious diagnosis, tested first, and wrong
Every engineer's first instinct here, mine included, is the threshold is in the wrong place. So I measured the two distributions directly. Top-1 cosine over all 500 questions:
| answerable (n=470) | unanswerable (n=30) | |
|---|---|---|
| q05 / q25 | 0.612 / 0.671 | n/a / 0.620 |
| median | 0.723 | 0.647 |
| q75 / max | 0.774 / 0.938 | 0.689 / 0.811 |
AUC 0.753. The unanswerable range sits almost entirely inside the answerable range.
There is no threshold. The best one obtainable on these very samples scores balanced error 0.285 against the shipped rule's 0.305, a 0.02 improvement, and that 0.285 is an in-sample ceiling, so held-out the gap is smaller still. If you want false-abstain down to a shippable 0.05, you pay about 0.78 false-confidence for it.
Recalibration was ruled out by measurement, not by argument. That distinction matters to me: "I thought about it and it wouldn't work" is how you end up wrong the way I was in Part 1.
Six signals, one measurement
If the threshold isn't the problem and the signal is, then swap the signal. I measured six candidates on the same 500 questions and the same haystacks, differing in nothing but which quantity the gate reads:
| signal | family | AUC | 95% CI |
|---|---|---|---|
dense_top1 (shipped)
|
relevance, bi-encoder | 0.753 | [0.680, 0.826] |
rerank_top1 |
relevance, cross-encoder | 0.742 | [0.666, 0.818] |
hybrid_top1 |
relevance, RRF | 0.739 | [0.663, 0.815] |
entail_max |
answerability, QNLI | 0.648 | [0.557, 0.739] |
margin_1_5 |
distributional | 0.579 | [0.479, 0.679] |
ratio_1_5 |
distributional | 0.545 | [0.442, 0.648] |
Nothing beat the signal already shipping. And the two informative rows are not the top one.
The cross-encoder is the finding
rerank_top1 is a cross-encoder. It does not embed the query and the document separately and compare vectors. It reads them jointly, with full attention between them, which is exactly the architecture you'd reach for if you wanted a model that could tell "this passage answers this question" from "this passage is about this topic."
It ranks superbly. Below, it delivers the single largest retrieval gain I've measured; on this benchmark it reaches hit@5 0.970.
And it scores a topically-related session that does not contain the answer just as highly as one that does.
That's the result that reorganised my mental model. The cross-encoder isn't failing because it's small or badly trained. It's failing because it was trained for relevance, and it is excellent at relevance, and relevance is a different quantity from answerability. A session where the user discusses their job search at length is maximally relevant to "what company did the user say they were joining?" whether or not they ever named the company.
Relevance asks is this about the same thing? Answerability asks is the specific fact in here? Nothing in a relevance objective ever requires a model to learn the second one.
And the answerability model came in below plain cosine
entail_max is the QNLI judge, the one built-in that's actually trained on "does this answer this question," the one that came out of reader comments on the last series and that I was pleased to ship.
0.648. Below cosine. At its own untuned boundary it scores false-confidence 0.533.
And here's the part that makes it a real finding instead of a bad day: months earlier, on a completely different corpus the judge had never seen, I measured its residual near-miss false-confidence at 0.50. The bound transferred exactly. It was never good enough for this workload; it was simply never tested against a workload that made that visible.
Stacking it behind a lowered gate doesn't rescue it either:
| configuration | false-abstain | false-confident | balanced |
|---|---|---|---|
| shipped cosine @0.713 | 0.443 | 0.167 | 0.305 |
| judge alone @0.5 | 0.321 | 0.533 | 0.427 |
| gate 0.600 + judge | 0.332 | 0.433 | 0.383 |
| gate 0.650 + judge | 0.381 | 0.233 | 0.307 |
The best stack ties the plain threshold and costs you a model pass per query.
The error bar that nearly buried all of it
Now the methodological part, which is the one I'd actually want to read.
There are 30 unanswerable questions. That's the benchmark's own class size, not a sampling choice, and I can't re-run it larger. Small-n results deserve suspicion, so I put an interval on the AUC.
The first version used sqrt(A(1-A)/n_min), roughly 0.08.
That's wrong, and wrong in the direction that feels responsible. It uses only the smaller class and throws away the 470-sample one entirely, and it is not the standard error of an AUC in the first place. The Hanley & McNeil (1982) estimator, which accounts for both classes, gives 0.037.
My error bar was 2.1× too wide.
The consequence wasn't cosmetic. At ±0.08, the 0.90 usability bar falls back inside the interval of the best signal, and the finding degrades from "measured exclusion" to "inconclusive, needs more data." I would have published a shrug. With the correct estimator, the best signal's interval tops out at 0.826 and the bar sits outside it.
That's the difference between "I couldn't tell" and "none of these signals is good enough, and here's the proof." Same data. The entire finding lived in the variance estimator.
Two things came out of that, and both ship:
- The estimator is now a library function,
recall.calibration.separability_interval, pinned by a test, so the published table and the library's own runtime certification read from one implementation and cannot drift apart. - Certification tests the interval's lower bound, not the point estimate. At the 20-samples-per-class minimum the module accepts, a measured AUC of 0.95 carries a lower bound of 0.879: it clears the bar on the point and has not established it. Certifying on the point would readmit, through small-sample noise, exactly the silent failure this whole exercise exists to expose.
I'm equally clear on what n=30 does not buy me. The three relevance signals at 0.74–0.75 are not distinguishable from one another. Their intervals overlap almost entirely, the ordering among them is noise, and no claim here rests on it. Anyone who reads that table as "dense beats rerank for abstention" is reading further than the data goes. The only conclusion the sample supports is the one I'm drawing: none of them reaches the ~0.90 a usable gate needs.
So what is the actual shape of the problem?
Three structurally different relevance signals cluster at 0.74–0.75. The one model in the lineup trained on answerability lands below all of them. Two distributional signals land below that. A stronger judge, tested separately, moves along the curve without lifting it.
The conclusion I'd have drawn a week ago is the tempting one: answerability is not recoverable from the retrieval geometry at all. I'm not drawing it, because I ran the control that tests it and it came back the other way.
Build an ordinary unanswerable set instead of an adversarial one, mechanically and with no labelling. Take a 787-document corpus, index 657 of them, hold 120 out, and use each document's own summary line as a query. Queries for indexed documents are answerable; queries for held-out documents are unanswerable because the document is genuinely absent. On that set, plain cosine separates at AUC 0.780, and you can abstain on half the unanswerable questions for a 13.7% false-abstain cost. That is not a usable gate by the 0.90 standard, but it is nothing like the coin flip the benchmarks produce.
So the honest statement is narrower and more useful:
Similarity separates answerable from unanswerable when the unanswerable question is about something genuinely absent. It fails, and inverts, when the unanswerable question is constructed to be adjacent. LOCOMO's cat5, LongMemEval's unanswerable class and BEAM's abstention split are all the second kind, by design.
The inversion is the part worth staring at. On BEAM the unanswerable questions score higher than the answerable ones, and a lexical-coverage signal sharing no mathematics with cosine inverts in the same direction (0.741 unanswerable against 0.717 answerable). Two unrelated signals failing identically points at the questions, not the retriever.
Which reframes what those benchmarks measure. They are upper bounds on difficulty, not estimates of deployed behaviour, and reporting an adversarial abstention score as if it described production is its own kind of over-claim, one I was two paragraphs away from making.
Part 3 is where that boundary gets located properly, on a continuous axis and with a coordinate, and where the real defect turns out to live: not in the signal, and not in the threshold, but in what the score reads.
The counterweight, folded in
The obvious objection is that these signals are all weak because retrieval is weak. It isn't, and I can price that precisely: turning on the cross-encoder reranker moves hit@5 from 0.671 to 0.777 at n=1,536, roughly twice the largest embedder effect in this project, with intervals disjoint from baseline through k=10. Three checks say the gain is real: hit@20 barely moves (0.855 → 0.870), as reordering a fixed pool requires; the gain decays with depth exactly as the mechanism predicts (+0.155 at k=1 → +0.016 at k=20); and an unrelated cross-encoder with 12× the parameters lands within noise at 6.3× the cost, so the effect belongs to reranking, not to a model.
Abstention across all three rerank arms: 0.00, 0.00, 0.00.
The best retrieval work in the project moved honesty by nothing. Reranking reorders what retrieval returned; it never touches whether what came back contains an answer. Your retrieval-quality metric and your honesty metric are not correlated, and improving the first will feel like improving the second.
Part 3 is where this stops being a wall and becomes a measurement: the two public benchmarks disagree about abstention for a reason, and naming it turns a yes/no question into a coordinate.
Question for the comments: if you've built a production RAG gate that decides "the answer isn't in what I retrieved", what does it read, and have you measured it on unanswerable questions that aren't adversarial? That distinction turned out to be the whole ballgame here, and I nearly published the stronger claim without it.
Full tables, intervals, and the six-signal comparison: FINDINGS.md §10b, RESULTS.md §8. The LongMemEval arm is flagged in-repo as the weakest evidence in the file, and why: it was measured pre-fix, so its retrieval rows are a dense-only lower bound. The abstention conclusion does not depend on that. Next: Part 3, "Does your agent know what it doesn't know?" has no answer. It has a coordinate.*
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.