Views
No views yet

lettucedect-v2-qwen-2b or the taxonomy-head cascade.lettucedect-v2-mmbert-base is a lightweight encoder hallucination detector for
Retrieval-Augmented Generation (RAG) and coding-agent settings. It is a token-level
classifier built on mmBERT-base: given the context, question, and answer, it labels
each answer token as supported (0) or unsupported (1), yielding the spans of the answer
not grounded in the context — across prose and code, in many languages.lettucedect-v2-qwen-2b: much smaller and
faster, single forward pass, no decoding. It returns binary spans (no category/subtype);
for typed spans (category + subcategory) use the generative model, or pair this detector
with the taxonomy head as a typing cascade.[CLS] context [SEP] question [SEP] answer [SEP]; context/question tokens masked in the loss, answer tokens labeled 0/11from lettucedetect.models.inference import HallucinationDetector
2
3detector = HallucinationDetector(method="transformer", model_path="KRLabsOrg/lettucedect-v2-mmbert-base")
4spans = detector.predict(context=[context], question=question, answer=answer, output_format="spans")
5# [{"start": ..., "end": ..., "text": "...", "confidence": ...}]transformers (no lettucedetect)(context, answer) as a pair and read the labels
over the answer segment (1 = unsupported):1import torch
2from transformers import AutoModelForTokenClassification, AutoTokenizer
3
4model_id = "KRLabsOrg/lettucedect-v2-mmbert-base"
5tok = AutoTokenizer.from_pretrained(model_id)
6model = AutoModelForTokenClassification.from_pretrained(model_id).eval()
7
8context = "France is in Western Europe. Its capital Paris had about 2.1 million people in 2019."
9answer = "The capital of France is Paris, with a population of about 4.5 million people."
10
11enc = tok(context, answer, truncation="only_first", max_length=4096,
12 return_offsets_mapping=True, return_tensors="pt")
13with torch.no_grad():
14 preds = model(input_ids=enc.input_ids, attention_mask=enc.attention_mask).logits.argmax(-1)[0]
15
16seq_ids, offsets, spans, cur = enc.sequence_ids(0), enc["offset_mapping"][0].tolist(), [], None
17for i, (sid, (a, b)) in enumerate(zip(seq_ids, offsets)):
18 if sid != 1 or a == b: # keep only answer-segment, non-special tokens
19 continue
20 if preds[i].item() == 1: # unsupported
21 cur = [a, b] if cur is None else [cur[0], b]
22 elif cur:
23 spans.append(cur); cur = None
24if cur:
25 spans.append(cur)
26print([{"text": answer[s:e], "start": s, "end": e} for s, e in spans])
27# -> [{'text': '4.5 million', 'start': 59, 'end': 70}]lettucedetect cascade (taxonomy_head=...) or the
generative lettucedect-v2-qwen-2b.| dataset | n | span-F1 | span-P | span-R | example-F1 | IoU |
|---|---|---|---|---|---|---|
| ALL | 10698 | 0.642 | 0.684 | 0.605 | 0.869 | 0.671 |
| acl | 440 | 0.579 | 0.705 | 0.492 | 0.873 | 0.637 |
| code-agent | 2015 | 0.508 | 0.619 | 0.430 | 0.770 | 0.581 |
| readme | 641 | 0.751 | 0.789 | 0.716 | 0.900 | 0.768 |
| tool-output | 617 | 0.588 | 0.736 | 0.490 | 0.763 | 0.645 |
| wikipedia | 1388 | 0.708 | 0.741 | 0.678 | 0.917 | 0.768 |
| psiloqa (multi-lang) | 2897 | 0.714 | 0.696 | 0.733 | 0.943 | 0.627 |
| ragtruth | 2700 | 0.528 | 0.668 | 0.437 | 0.743 | 0.724 |
lettucedect-v2-qwen-2b scores higher on span-F1 (ALL 0.689 vs 0.642) and
adds typing, but this encoder is far smaller and faster — a strong choice when throughput
or cost matters and binary spans suffice.| detector | span-F1 | example-F1 |
|---|---|---|
| lettucedect-v2-qwen-2b (generative 2B) | 0.602 | 0.835 |
| lettucedect-v2-lfm-8b (generative 8B) | 0.507 | 0.811 |
| lettucedect-v2-mmbert-base (this) | 0.508 | 0.770 |
| Nemotron-3-Ultra-550B (LLM judge, task-aware) | 0.216 | 0.700 |
| gpt-oss-120b (LLM judge, task-aware) | 0.212 | 0.691 |
| HHEM-2.1 / Lynx-8B / Granite-Guardian / MiniCheck | — | ≈ chance (BAcc ~0.50) |
lettucedect-v2-qwen-2b (81.8), but above prompt-based GPT-4 (63.4) and Luna (65.4). It
trades a little RAGTruth-specific accuracy for unified code+tool+multilingual coverage at
base size; lettucedect-v2-mmbert-large is stronger.scripts/evaluate_taxonomy_cascade.py). The cascade trails the generative model on typing
(typed-F1 0.461 vs 0.585), so prefer lettucedect-v2-qwen-2b when typed output is the goal.1@misc{kovács2026documentgroundingspanlevelhallucination,
2 title={Beyond Document Grounding: Span-Level Hallucination Detection over Code, Tool Output, and Documents},
3 author={Ádám Kovács and Bowei He and Xue Liu and István Boros and Szilveszter Tóth and Gábor Recski},
4 year={2026},
5 eprint={2607.00895},
6 archivePrefix={arXiv},
7 primaryClass={cs.CL},
8 url={https://arxiv.org/abs/2607.00895},
9}