File size: 6,546 Bytes
170a671
 
b07f2d1
 
 
170a671
b07f2d1
170a671
b07f2d1
 
 
 
 
 
 
170a671
 
b07f2d1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1ce6570
b07f2d1
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
---
title: ClaimCheck
emoji: πŸ”Ž
colorFrom: yellow
colorTo: red
sdk: static
app_file: index.html
pinned: false
license: apache-2.0
short_description: Verify LLM answers against the context they came from
tags:
- hallucination-detection
- groundedness
- rag
- guardrails
---

# πŸ”Ž ClaimCheck

**Verify LLM answers against their source context before they reach users.**

Paste an answer and the context it was supposed to be grounded in. ClaimCheck extracts the claims it can check as
strings β€” numbers, dates, quotes, entities, URLs β€” matches each against the context, and returns a verdict
(`pass` / `annotate` / `retry` / `block`), a grounding score, and **how much of the answer it did not check**.

**Everything runs in your browser.** No server, no network call, no telemetry β€” the verifier is ~1,700 lines of
dependency-free JavaScript executing in this tab. Paste real model output if you want to; it stays on your machine.

| | |
|---|---|
| πŸ“¦ Rule pack | [`NagaYu/claimcheck-rules`](https://huggingface.co/NagaYu/claimcheck-rules) |
| πŸ“Š Evaluation set | [`NagaYu/claimcheck-eval`](https://huggingface.co/datasets/NagaYu/claimcheck-eval) |
| ⚑ Latency | ~0.2 ms typical · 15 ms on a 20,000-character worst case |
| 🌏 Languages | English and Japanese |

---

## Design philosophy

We cannot detect every hallucination. Nobody can, and a tool that claims to is lying to you.

But we do not have to. The observation this is built on:

> **Dangerous hallucinations are specific.**

A model that invents *"the contract terminates on 2023-11-04 under Article 12, with a penalty of 3,400,000 JPY"* is far
more harmful than one that says *"the contract may terminate at some point"*. And the specific one is **checkable as a
string**: numbers, dates, quotes, entities and URLs are literal tokens that either do, or do not, appear in (or follow
arithmetically from) the source.

So ClaimCheck verifies only what it can verify deterministically, and declares the rest out of scope β€” loudly.

## ⚠️ Always read `coverage`

Two independent numbers, and reporting one without the other is the most dangerous way to misuse this tool.

| field | question it answers |
|---|---|
| `grounding_score` | Of the claims I checked, what share held up? |
| `coverage` | What share of the answer did I check at all? |

```
grounding_score 1.00 + coverage 0.10
  = one sentence checked out and nine were never looked at.
  = NOT "this answer is verified".
```

An answer of pure hedging produces **zero** claims and scores **1.00** on any naive groundedness metric.

## Claim statuses

| status | colour | meaning |
|---|---|---|
| `supported` | 🟩 | Appears literally in the context after normalisation |
| `derived` | 🟦 | Computable from context numbers by 2–3 term arithmetic; the formula is shown |
| `approximate` | 🟨 | A near match exists β€” paraphrase, finer-grained date, related URL |
| `unsupported` | 🟧 | Nothing in the context backs it |
| `contradicted` | πŸŸ₯ | The context holds a **near but different** value |

`approximate` is deliberately **not** counted as grounded: a paraphrased quote is still an inaccurate quote.

## Measured behaviour

On the [158-case bilingual evaluation set](https://huggingface.co/datasets/NagaYu/claimcheck-eval):

```
answer-level recall       56/56   (100%)   cases that should be flagged, and were
clean answers passing     44/46   (96%)    the rest were annotated, never blocked
known false positives     22      (13.9%)
known false negatives     3       (1.9%)
JS/Python parity         158/158  (100%)
```

The false-positive rate is published deliberately. It is the number that decides whether anyone keeps a verifier
switched on, and hiding it helps nobody.

### Known limitation: semantic reversal

All three false negatives share a shape. A one-character change that reverses meaning keeps character similarity above
0.9, so a reversed quote reads as a faithful paraphrase:

| context | answer | similarity | status |
|---|---|---|---|
| 売上高は7%**ε’—εŠ **γ—γŸ | 売上高は7%**ζΈ›ε°‘**γ—γŸ | 0.97 | `approximate` ❌ |
| ζζ‘ˆγ―ε…¨δΌšδΈ€θ‡΄γ§**ζ‰Ώθͺ**γ•γ‚ŒγŸ | ζζ‘ˆγ―ε…¨δΌšδΈ€θ‡΄γ§**否決**γ•γ‚ŒγŸ | 0.92 | `approximate` ❌ |
| revenue **grew** 8% | revenue **declined** 8% | 0.97 | `approximate` ❌ |

Character matching cannot detect negation or antonym substitution. If your domain turns on approve/reject or
increase/decrease, **do not rely on quote status alone.** In all three cases the answer-level verdict was still
`retry`, because the NUMERIC and ENTITY claims inside the quote are the layer that catches it.

### False positives

Faithful paraphrase Β· generic noun phrases ("Machine Learning") Β· acronym expansion (WHO ↔ World Health Organization) Β·
rounding just outside tolerance Β· fiscal vs calendar years Β· identifier variants (ISO 9001:2015 vs ISO 9001) Β·
URL query parameters.

**ENTITY is the noisiest check.** Set `{"enable_entity": false}` in the policy box first, and start from `annotate`,
never `block`.

## Policy presets

Paste any of these into the **Policy JSON** box. Full set in the
[rule pack](https://huggingface.co/NagaYu/claimcheck-rules):

```jsonc
// observe: nothing blocked but critical safety β€” start here
{"retry_on_contradicted": 9999, "retry_grounding": 0.0, "annotate_grounding": 1.0}

// numeric + date only: the lowest false-positive configuration
{"enable_entity": false, "enable_quote": false, "enable_url": false}

// tolerant of rounding
{"numeric_tolerance": 0.02, "contradiction_rel": 0.10}
```

## Running it yourself

The core is an ES module with no dependencies:

```js
import { gate, verify, buildHighlight } from "./claimcheck.js";

const r = gate(answer, context, systemPrompt, userInput, schemaJson, policyJson, tagsJson);
console.log(r.verdict, r.grounding_score, r.coverage);
```

A Python implementation with a Gradio UI and a REST API also exists and is behaviourally identical (verified at 100%
parity on all 158 evaluation cases); it ships under `python/` in the
[rule pack repository](https://huggingface.co/NagaYu/claimcheck-rules) for self-hosting.

## Disclaimer

ClaimCheck is **one layer of defence, not a correctness guarantee.** It verifies numbers, dates, quotes, entities and
URLs against a context you supply. It says nothing about claims it could not extract, nothing about reasoning, and
nothing about whether the context itself was right. A `pass` verdict means *"the specific things I know how to check,
checked out"* β€” nothing more.

Apache-2.0.