Views
No views yet
PASS or FAIL — so the verdict costs
one forward pass and its logprob doubles as a confidence score.Given a question, retrieved context, and a candidate response, emit FAIL iff the response makes at least one factual claim that is unsupported by or contradicts the retrieved context — truth in the real world is irrelevant (strict grounding); PASS otherwise, including responses that explicitly decline to answer for lack of context.
| spec adherence | robustness (worst set) | F1(FAIL) | recall(FAIL) | AUROC | |
|---|---|---|---|---|---|
| base Qwen3-1.7B, zero-shot | 0.5561 | 0.4953 | 0.4979 | 0.4918 | 0.5669 |
| this model | 0.7180 | 0.5766 | 0.6938 | 0.7300 | 0.7846 |
| model | spec adherence | recall(FAIL) | specificity(PASS) |
|---|---|---|---|
| Claude Opus 5, chain-of-thought | 0.750 | 0.800 | 0.700 |
| GPT-5.6, few-shot | 0.717 | 0.800 | 0.633 |
| GPT-5.6, zero-shot | 0.700 | 0.800 | 0.600 |
| this model (1.7B) | 0.683 | 0.533 | 0.833 |
| Claude Opus 5, zero-shot | 0.633 | 0.867 | 0.400 |
| base Qwen3-1.7B | 0.533 | 0.100 | 0.967 |
1import torch
2from transformers import AutoModelForCausalLM, AutoTokenizer
3from peft import PeftModel
4
5BASE = "Qwen/Qwen3-1.7B"
6tok = AutoTokenizer.from_pretrained(BASE)
7model = PeftModel.from_pretrained(
8 AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.bfloat16, device_map="auto"),
9 "aaryand/qwen3-1.7b-context-adherence-guardrail").eval()
10
11RUBRIC = """You are a groundedness guardrail for retrieval-augmented generation. \
12Judge whether the RESPONSE is fully supported by the CONTEXT.
13
14Rules:
15- Verdict FAIL if the response makes at least one factual claim that is unsupported \
16by or contradicts the context. Whether a claim is true in the real world is \
17irrelevant: a claim absent from the context is still unsupported (FAIL).
18- Verdict PASS otherwise. A response that declines to answer or says the context \
19lacks the information is faithful (PASS)."""
20
21def judge(question, context, response):
22 body = (f"QUESTION: {question}\n\n" if question else "") + \
23 f"CONTEXT:\n{context[:24000]}\n\nRESPONSE:\n{response}"
24 prompt = f"{RUBRIC}\n\n{body}\nAnswer with exactly one word: PASS or FAIL."
25 ids = tok.apply_chat_template([{"role": "user", "content": prompt}],
26 add_generation_prompt=True, return_tensors="pt",
27 enable_thinking=False).to(model.device)
28 with torch.no_grad():
29 logits = model(ids).logits[0, -1]
30 p = torch.softmax(logits[[tok.encode("PASS")[0], tok.encode("FAIL")[0]]].float(), -1)
31 return ("FAIL" if p[1] > p[0] else "PASS"), p[1].item() # verdict, P(FAIL)
32
33print(judge("Who wrote it?", "The report was written by Dr. Chen in 2019.",
34 "Dr. Chen wrote it in 2019, and she also won a Nobel Prize."))
35# ('FAIL', 0.9...) — the Nobel claim is absent from the contextPASS/FAIL. That is bit-for-bit the quantity
the eval scores, so training and evaluation are the same measurement rather than two that
merely agree.1pip install torch transformers peft accelerate
2python eval.py --model aaryand/qwen3-1.7b-context-adherence-guardrail --baseline
3python eval.py --model aaryand/qwen3-1.7b-context-adherence-guardrail --eval-set your_set.jsonlb5467d033690f3909cfacfd69bfb3fa04cc6a946.