TriAgent → Hallucination detection

Detecting LLM hallucinations for free

If you already run a cheaper specialist alongside your LLM, the gap between their scores tells you when the LLM is wrong — at AUC 0.898, with no extra model call, no labels, and no training.

CIKM 2026 arXiv:2607.19794 Code

The measurement

Stratify Financial PhraseBank by whether the LLM got the answer right, then look at SDI_ER — the absolute difference between the sentence-level encoder's score and the LLM's:

ConditionMean SDI_ER
LLM is correct0.168
LLM is wrong0.709
Separation4.2×

Thresholded as a binary "the LLM is hallucinating" flag, that reaches ROC AUC = 0.898, with Welch t = 46.8 and p ≈ 6 × 10⁻²⁵⁵.

ROC curve plot with four lines. The SDI_ER curve bows sharply toward the top-left corner reaching AUC 0.898. SDI_max reaches 0.782. SDI_LE reaches 0.620 and SDI_LR 0.575, both close to the diagonal chance line.
ROC curves for each SDI variant as an "LLM is wrong" detector. Only the encoder-versus-reasoner pairing (SDI_ER) is a strong detector. The pairings that involve the word-level lexicon hug the diagonal.

Why it costs nothing

In a tier-routed pipeline the encoder's score is already computed — it is what decides whether the query needs to escalate in the first place. Reading it a second time as a trust signal adds one subtraction.

MethodMarginal cost per queryRequires
Cross-model divergencezero — reuses an existing outputa second heterogeneous model in the pipeline
SelfCheckGPTn extra LLM samplesnothing beyond the LLM itself
Self-consistencyn extra LLM samplesnothing beyond the LLM itself

The honest trade-off: if you run exactly one model, SelfCheckGPT applies and this does not. There is nothing to diverge from.

Only cross-granularity disagreement works

This is the result that identifies the mechanism. Evaluated as detectors on the same data:

VariantPairingMean (right)Mean (wrong)Welch tAUC
SDI_LElexicon vs. encoder0.3940.4998.660.620
SDI_LRlexicon vs. reasoner0.3240.3865.270.575
SDI_ERencoder vs. reasoner0.1680.70946.820.898
SDI_maxmax of the three0.4430.79732.210.782

The lexicon-based pairings barely beat chance. What makes SDI_ER work is that the encoder and the reasoner read genuinely different amounts of context, so their errors are structurally different rather than correlated.

Where it does not work

The signal is a hallucination detector, not an adversarial detector, and the paper reports the negative result rather than omitting it:

Attack typeDetection AUCVerdict
Negation insertion0.71moderate
Synonym swap≈ 0.50chance
Numeric flip≈ 0.50chance
Character drop / typo≈ 0.50chance

It catches negation attacks and essentially nothing else. Do not deploy it as an adversarial defence — that needs purpose-built detectors, and the paper says so explicitly.

Wiring it up

If you are already running a specialist and an LLM in parallel, the whole implementation is:

s_specialist = encoder.score(query)     # already computed for routing
s_llm        = reasoner.score(query)    # already computed for the answer

trust = 1.0 - abs(s_specialist - s_llm) / 2.0

if trust < threshold:
    flag_for_review(query)              # or fall back to the specialist

Threshold selection follows the same grid-search recipe as the routing thresholds: sweep on a held-out validation split and pick the point matching your tolerance for false flags.

Related