qmichalwarda commited on
Commit
799c195
·
verified ·
1 Parent(s): ffc2fee

Model card: the full qev-0.8b card (demo, benchmarks, evaluation, training, data, limitations) with the MLX parts

Browse files
Files changed (2) hide show
  1. README.md +406 -9
  2. SHA256SUMS +1 -1
README.md CHANGED
@@ -9,26 +9,187 @@ tags:
9
  - decision-making
10
  - classification
11
  - calibration
 
 
 
 
 
 
12
  - system-one
13
  - jev
14
  - mlx
15
  ---
16
 
17
- # qev-0.8b-MLX
 
 
 
 
 
 
 
 
 
 
18
 
19
  [qev-0.8b](https://huggingface.co/labqoat/qev-0.8b) for Apple silicon with [MLX](https://github.com/ml-explore/mlx).
20
- qev is a 0.8B System One decision model: it answers typed questions about a piece of state with calibrated
21
- probabilities, in one forward pass. See the main repo for the model card, the evaluation and the training data.
22
 
23
  | weights | folder | size | argmax agreement with the CUDA reference¹ |
24
  |---|---|---|---|
25
  | bf16 | repo root | 1.4 GB | 99.3% |
26
  | 8-bit (affine, group size 64) | `q8/` | 0.78 GB | 99.0% |
27
 
28
- ¹ Measured on the first release's weights; the conversion and runner are unchanged.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
- A 4-bit build is not provided: it changes about 6% of the argmax answers, which is too much for calibrated
31
- probabilities.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  ## Quickstart
34
 
@@ -65,7 +226,7 @@ the way to use it. `qev_mlx.py` runs one forward pass and reads the answers:
65
  score 1.25).
66
  4. The answers use decider's `/v1/systemone` format.
67
 
68
- ## Options
69
 
70
  - **`encoding="per_question"`:** one row per question, for higher consistency across the questions of a request.
71
  The `"Context:\n" + state` prefix is computed once: mlx-lm's cache keeps the Gated DeltaNet conv and recurrent
@@ -77,7 +238,116 @@ the way to use it. `qev_mlx.py` runs one forward pass and reads the answers:
77
  - **`compile=True`** (the default): `mx.compile` of the joint forward per 64-token length bucket. The median is about
78
  13% lower; each new bucket compiles once, in about 0.4 s.
79
 
80
- ## Speed (M4 Pro, 24 GB, bf16)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
  Steady-state batch-1 latency over 200 dev requests (mean 746 tokens, median 417).
83
 
@@ -90,6 +360,133 @@ Steady-state batch-1 latency over 200 dev requests (mean 746 tokens, median 417)
90
  Memory is 2.0 GB RSS and 2.6 GB Metal for bf16 (1.3 / 2.0 GB for 8-bit), plus about 25 MB per cached state. Prefill
91
  is compute-bound on the Mac GPU, so 8-bit saves memory, not time.
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  ## License
94
 
95
- Apache-2.0. See [qev-0.8b](https://huggingface.co/labqoat/qev-0.8b) for the training data and its licenses.
 
9
  - decision-making
10
  - classification
11
  - calibration
12
+ - routing
13
+ - guardrails
14
+ - prompt-injection
15
+ - agents
16
+ - games
17
+ - on-device
18
  - system-one
19
  - jev
20
  - mlx
21
  ---
22
 
23
+ <p align="center"><img src="https://huggingface.co/labqoat/qev-0.8b/resolve/main/figures/header.jpg" alt="qev-0.8b: the tiny local decision model, by Labqoat" width="100%"></p>
24
+
25
+ <p align="center">
26
+ <a href="https://app.labqoat.com/e/qev/qev/"><b>▶ Watch it play, live</b></a> ·
27
+ <a href="https://huggingface.co/labqoat/qev-0.8b">PyTorch + CUDA (main repo)</a> ·
28
+ <a href="https://huggingface.co/labqoat/qev-0.8b-GGUF">GGUF (llama.cpp)</a> ·
29
+ <a href="#quickstart">Quickstart</a> ·
30
+ <a href="#benchmarks-27-decision-models-around-1b">Benchmarks</a>
31
+ </p>
32
+
33
+ # qev-0.8b-MLX: the tiny local decision model, for Apple silicon
34
 
35
  [qev-0.8b](https://huggingface.co/labqoat/qev-0.8b) for Apple silicon with [MLX](https://github.com/ml-explore/mlx).
 
 
36
 
37
  | weights | folder | size | argmax agreement with the CUDA reference¹ |
38
  |---|---|---|---|
39
  | bf16 | repo root | 1.4 GB | 99.3% |
40
  | 8-bit (affine, group size 64) | `q8/` | 0.78 GB | 99.0% |
41
 
42
+ ¹ Measured on the first release's weights; the conversion and runner are unchanged. A 4-bit build is not provided:
43
+ it changes about 6% of the argmax answers, which is too much for calibrated probabilities.
44
+
45
+ qev reads anything (a game board, a post, an inbox, a web page, a log) and answers any question about it with
46
+ **calibrated probabilities in a single forward pass**. Out of 27 decision models of its size it scores highest on
47
+ Benchmark Heaven, it answers faster than all of them, and in 8-bit it needs 1.3 GB of RAM on a Mac.
48
+
49
+ <table>
50
+ <tr>
51
+ <td width="38%" align="center">
52
+ <video src="https://huggingface.co/labqoat/qev-0.8b/resolve/main/figures/qev-demo-mobile.mp4" poster="https://huggingface.co/labqoat/qev-0.8b/resolve/main/figures/qev-demo-mobile-poster.jpg" autoplay loop muted playsinline controls width="100%"></video>
53
+ </td>
54
+ <td>
55
+
56
+ **What you're watching.** These are screen recordings of the [live demo](https://app.labqoat.com/e/qev/qev/), with nothing staged. One copy of qev-0.8b
57
+ on one GPU runs all of these at once, in real time:
58
+
59
+ - 🐍 **Snake**, 🧱 **Tetris**, 👻 **Pac-Man**, 🔢 **2048**, 🏓 **Breakout** and 🔨 **Whac-A-Mole**. Tetris, Pac-Man,
60
+ 2048, Breakout and Whac-A-Mole were held out of training.
61
+ - 🧹 **LinkedIn slop** screening: bait, humblebrag, made-up story, buzzwords and substance, five questions per post.
62
+ - 🛡️ A **prompt-injection firewall** for an agent's inputs.
63
+ - 🧭 An agent **tool router**, 🎧 **support triage**, ⭐ **review ratings** and 📥 **inbox sorting**: six emails and 18
64
+ questions in one request.
65
+
66
+ For the games, the game code writes down what each move would do ("it hits the wall and dies", "clears 2 lines"),
67
+ and qev answers one yes/no question per move. The most likely "yes" is played. qev does no search and no lookahead;
68
+ every move is one forward pass of about 4 ms.
69
+
70
+ </td>
71
+ </tr>
72
+ </table>
73
+
74
+ ## At a glance
75
+
76
+ | | qev-0.8b |
77
+ |---|---|
78
+ | parameters | 0.8B (full fine-tune of Qwen3.5-0.8B-Base) |
79
+ | Benchmark Heaven | **77.1%**: first of 27 decision models around 1B |
80
+ | latency | **3.5 ms** p50 per request on 1× L40S: fastest of 27 · 109 ms on an M4 Pro Mac (MLX) |
81
+ | memory | 1.7 GB weights in bf16, 0.8 GB in 8-bit · **1.3 GB RAM** on a Mac in 8-bit |
82
+ | also first on | JevExtra (72.0%, agent and safety skills), JevSpatial (74.2%, games and space), Rules-7 (63.7%, tied) |
83
+ | output | calibrated probabilities for `choice`, `noul` (yes / no) and `score` questions, up to 255 options each |
84
+ | formats | MLX bf16 and 8-bit for Apple silicon (this repo), [PyTorch + fast CUDA engine](https://huggingface.co/labqoat/qev-0.8b), [GGUF](https://huggingface.co/labqoat/qev-0.8b-GGUF) (also stock `llama-server` at `/v1/systemone`) |
85
+ | license | Apache-2.0 |
86
+
87
+ qev does not generate text. Each answer is a probability distribution, so you can act when it is confident and
88
+ escalate to a person or a larger model when it is not. Nothing has to leave your machine.
89
+
90
+ ## Benchmarks: 27 decision models around 1B
91
+
92
+ We ran qev-0.8b against 26 other decision models around 1B parameters. Every model got the same items and scoring,
93
+ ran on one NVIDIA L40S, and used its own published inference recipe.
94
+
95
+ <p align="center"><img src="https://huggingface.co/labqoat/qev-0.8b/resolve/main/figures/ranks.png" alt="Most accurate, fastest, and smallest of the most accurate: qev-0.8b leads all three" width="100%"></p>
96
+
97
+ Accuracies are in %. ECE, latency and memory are lower-is-better. **Bold** marks the best value in each column.
98
+
99
+ | model | params | VRAM (GB, est.) | p50 ms | BH | BH ECE ↓ | JevBench | coherence | suite | JevExtra | JevSpatial | Rules-7 |
100
+ |---|---|---|---|---|---|---|---|---|---|---|---|
101
+ | **qev-0.8b** | 0.8B | 2.2 | **3.5** | **77.1** | 0.063 | 85.1 | 71.2 | 61.7 | **72.0** | **74.2** | **63.7** |
102
+ | decision-2b (flymy-ai) | 2B | 5.6 | 81.3 | 76.2 | **0.042** | **86.0** | 61.8 | 60.9 | 64.1 | 69.5 | 58.1 |
103
+ | decider-2b | 1.9B | 4.3 | 7.2 | 75.8 | 0.065 | 85.9 | 72.5 | **70.7** | 58.8 | – | – |
104
+ | EXAONE-4.0-1.2B-JEV | 1.2B | 3.1 | 27.8 | 68.8 | 0.097 | 83.9 | 67.9 | 67.3 | 64.0 | 70.0 | 58.8 |
105
+ | malkuth-2b | 2B | 4.3 | 46.2 | 68.8 | 0.131 | 84.8 | 70.8 | 66.6 | 63.6 | 69.9 | **63.7** |
106
+ | classone (Gemma-4-E2B) | ~5B | 10.7 | 56.5 | 66.7 | 0.260 | 82.1 | 66.6 | 48.2 | 52.5 | 63.4 | 48.1 |
107
+ | jul-2b | 2B | 5.5 | 88.9 | 65.8 | 0.059 | 83.3 | 68.4 | 57.8 | 56.4 | 66.8 | 58.0 |
108
+ | Open-Jev-2B | 1.9B | 4.3 | 191.7 | 64.5 | 0.127 | 84.9 | 76.2 | 59.9 | 57.6 | 72.2 | 59.7 |
109
+ | decision-fast | 0.6B | 1.7 | 58.9 | 63.6 | 0.106 | 73.0 | 54.3 | 52.0 | 52.0 | 48.5 | 47.1 |
110
+ | Tacet Sonata¹ | 144M | 0.8 | 20.5 | 62.3 | 0.148 | 64.9 | 45.5 | 31.6 | 36.1 | 50.6 | 35.4 |
111
+
112
+ <details>
113
+ <summary>The other 17 models</summary>
114
+
115
+ | model | params | VRAM (GB, est.) | p50 ms | BH | BH ECE ↓ | JevBench | coherence | suite | JevExtra | JevSpatial | Rules-7 |
116
+ |---|---|---|---|---|---|---|---|---|---|---|---|
117
+ | smalljev-2.5b¹ | 2.5B | 5.6 | 68.8 | 61.5 | 0.142 | 71.2 | 61.1 | 45.6 | 46.2 | 53.7 | 49.3 |
118
+ | jevos-v5 (feder-cr), partial | ~0.9B | 1.2 | – | 61.0 | – | 84.1 | – | 49.9 | – | – | – |
119
+ | Quyet-1.0-Small¹ | 328M | 1.8 | 19.3 | 59.7 | 0.066 | 71.3 | 56.8 | 43.8 | 42.4 | 48.4 | 45.4 |
120
+ | watt-flash-0.1 | 140M | 0.8 | 15.7 | 59.7 | 0.073 | 67.1 | 61.9 | 48.8 | 39.1 | 51.3 | 36.3 |
121
+ | Ghafiri Qwen3.5-0.8B | 0.8B | 2.0 | 44.6 | 59.3 | 0.083 | 78.1 | 61.7 | 41.6 | 41.2 | 62.1 | 40.1 |
122
+ | WaterSheep | ~150M | 1.1 | 18.9 | 59.3 | 0.127 | 73.5 | 57.9 | 56.4 | 50.8 | 57.9 | 41.0 |
123
+ | lev-350m | 361M | 1.2 | 24.1 | 58.4 | 0.094 | 66.9 | 65.5 | 47.5 | 44.5 | – | – |
124
+ | deem-0.8b¹ | 0.8B | 2.0 | 54.6 | 58.0 | 0.240 | 75.0 | 59.3 | 42.9 | 48.3 | 51.1 | 46.8 |
125
+ | Laya | 421M | 1.3 | 24.4 | 58.0 | 0.095 | 70.7 | 56.8 | 53.7 | 43.0 | 47.8 | 44.1 |
126
+ | openjev-verdict¹ | 151M | 1.1 | 13.5 | 57.6 | 0.081 | 63.9 | 59.7 | 32.8 | 37.9 | 38.4 | 34.7 |
127
+ | Quyet-1.0-Tiny¹ | 183M | 1.2 | 17.5 | 57.1 | 0.097 | 67.7 | 54.9 | 42.4 | 46.9 | 52.6 | 47.1 |
128
+ | Qwen3-1.7B (raw)¹ | 1.7B | 4.6 | 27.8 | 55.8 | 0.411 | 64.5 | 70.7 | 39.8 | 48.7 | 52.4 | 41.3 |
129
+ | SimpleJev (Qwen3.5-0.8B) | 0.8B | 2.2 | 82.9 | 55.4 | 0.183 | 55.6 | 46.6 | 47.6 | 45.6 | 54.1 | 36.1 |
130
+ | Open-Jev-DeBERTa¹ | 435M | 2.2 | 34.4 | 55.0 | 0.091 | 79.0 | 53.3 | 38.4 | 41.5 | 54.2 | 42.0 |
131
+ | verdict-small | 118M | 1.0 | 8.6 | 53.7 | 0.115 | 50.7 | 70.1 | 42.6 | 38.5 | 52.8 | 35.3 |
132
+ | ModernBERT-zeroshot | 395M | 1.3 | 41.0 | 52.4 | 0.199 | 70.7 | 66.4 | 44.1 | 42.7 | 52.4 | 37.5 |
133
+ | Qwen3-0.6B (raw)¹ | 0.6B | 2.0 | 26.3 | 49.8 | 0.434 | 53.8 | 76.8 | 31.7 | 45.6 | 37.9 | 35.7 |
134
+
135
+ </details>
136
+
137
+ - **BH** = Benchmark Heaven, public split (231 items, sampling error about ±2.8 points). **JevBench** = 240 cases;
138
+ coherence comes from the official jevbench harness (×100), with qev in per_question mode and the other models in
139
+ their own runtime. **Suite** = 22 public-dataset tasks (880 questions). **JevExtra** = mean skill accuracy over
140
+ chess, browser (Mind2Web), tools, agent action, injection, safety, judging and grounding. **JevSpatial** and
141
+ **Rules-7** = the jev-skills `spatial` and `rules7` configs.
142
+ - **Latency** is p50 per request at batch 1, in each model's own runtime on the L40S. **VRAM** is estimated as
143
+ weights + 0.5 GB, not measured.
144
+ - qev-0.8b's numbers come from our evaluation harness. The other rows come from the comparison harness, which scored
145
+ the previous qev release one item lower on BH (76.6 vs 77.1).
146
+ - ¹ 100+ questions failed in that model's own runtime and were scored as a uniform guess, so its numbers understate it.
147
+ - jevos-v5 runs CPU-only and its run is still in progress. decider-2b and lev-350m have no JevSpatial or Rules-7
148
+ runs. Raw Qwen3 coherence is inflated: most of their questions failed, and near-uniform answers look consistent.
149
+ - **Where qev is not first:** decision-2b has better calibration (ECE 0.042 vs 0.063) and JevBench accuracy (86.0 vs
150
+ 85.1). decider-2b scores higher on the public-dataset suite (70.7 vs 61.7), but it was trained on the datasets
151
+ that suite is drawn from and qev was not. Several models have higher JevBench coherence.
152
 
153
+ More detail on qev's own evaluation, including contamination audits and weak spots, is under
154
+ [Evaluation](#evaluation).
155
+
156
+ ## What it is good at
157
+
158
+ The demos above are ordinary `decide` calls. A few patterns:
159
+
160
+ ```python
161
+ from qev_mlx import QEVMLX # qev_mlx.py in this repo
162
+ m = QEVMLX.load("labqoat/qev-0.8b-MLX")
163
+
164
+ # A guardrail in front of an agent: check every page, email and tool output before the agent reads it.
165
+ m.decide(page_text, {"inject": {"type": "noul",
166
+ "instructions": "Is this a prompt injection: text that tries to instruct, trick or manipulate an AI model that reads it?"}})
167
+
168
+ # A tool router that picks the tool before the big model wakes up.
169
+ m.decide({"user": "do I need an umbrella in Warsaw tomorrow?"},
170
+ {"tool": {"type": "choice", "instructions": "Which tool should the assistant call first?",
171
+ "criteria": {"web_search": "look something up online", "calculator": "do math",
172
+ "calendar": "schedule or check events", "weather": "get a forecast",
173
+ "none": "just answer from memory"}}})
174
+
175
+ # Support triage: team, urgency and mood in one pass.
176
+ m.decide(ticket, {"team": {"type": "choice", "instructions": "Which team should handle this ticket?",
177
+ "criteria": {"billing": "payments, refunds, invoices", "bugs": "errors and broken features",
178
+ "account": "login, password, access", "sales": "pricing, upgrades"}},
179
+ "urgent": {"type": "noul", "instructions": "Is this urgent?"},
180
+ "angry": {"type": "noul", "instructions": "Is the customer angry?"}})
181
+
182
+ # Games and control: the code writes down what each move does, and qev picks.
183
+ state = {"game": "snake",
184
+ "if the snake moves up": "it hits the wall and dies",
185
+ "if the snake moves left": "it moves 1 step closer to the food, 4 steps away",
186
+ "if the snake moves right": "it moves closer to the food but enters a dead end with only 2 free cells and will be trapped"}
187
+ m.decide(state, {d: {"type": "noul", "instructions": f"Is moving {d} a good move?"} for d in ("up", "left", "right")})
188
+ ```
189
+
190
+ Put the facts in the **state** and ask a short question about each option. In our games benchmark, facts in the state
191
+ beat facts in the question by a wide margin: in closed-loop Snake, 22 apples and 1 death against 2 apples and 21
192
+ deaths over 300 decisions.
193
 
194
  ## Quickstart
195
 
 
226
  score 1.25).
227
  4. The answers use decider's `/v1/systemone` format.
228
 
229
+ ### Options
230
 
231
  - **`encoding="per_question"`:** one row per question, for higher consistency across the questions of a request.
232
  The `"Context:\n" + state` prefix is computed once: mlx-lm's cache keeps the Gated DeltaNet conv and recurrent
 
238
  - **`compile=True`** (the default): `mx.compile` of the joint forward per 64-token length bucket. The median is about
239
  13% lower; each new bucket compiles once, in about 0.4 s.
240
 
241
+ ## Questions and answers
242
+
243
+ A request has a **state** (a string, an object or an array) and one or more **questions**, each of one type:
244
+
245
+ | type | what it asks | answer fields |
246
+ |---|---|---|
247
+ | `choice` | pick one of the named options (`criteria`: `{name: description or null}`) | `choice`, `confidence`, `x_p_max`, `certainty`, `probabilities` |
248
+ | `noul` | a yes / no question (`instructions`, optional `criteria` `{"true": ..., "false": ...}`) | `noul`: the probability of true |
249
+ | `score` | a rating on an ordered scale (`criteria`: list of levels, low to high) | `score` (expected level index), `confidence`, `x_p_max`, `certainty`, `legend`, `probabilities` |
250
+
251
+ The fields follow decider's `format_answer`, the `/v1/systemone` wire format:
252
+
253
+ - `confidence` is TypeSafe's confidence. For a choice, it is the top probability rescaled so that uniform = 0. For a
254
+ score, it is 1 minus the expected distance from the top level, relative to a uniform distribution.
255
+ - `x_p_max` is the top probability.
256
+ - `certainty` is 1 minus the normalised entropy.
257
+
258
+ A string state is used as it is. An object or array is rendered as JSON with array indices annotated, as decider's
259
+ server does. A question can have up to 255 options.
260
+
261
+ ### How a request is answered
262
+
263
+ 1. The request becomes a prompt row in decider's state-first System One layout: `Context:` and the state, then each
264
+ question with its lettered options, ending in an `Answer: (` slot.
265
+ 2. One forward pass reads the logits of the option letter tokens at each answer slot. Only the letters' rows of the
266
+ tied embedding are used.
267
+ 3. Each question's answer is the softmax over its own options, sharpened per question type with temperatures fitted
268
+ on a held-out calibration split: **choice 1.7, noul 0.9, score 1.25** (`p -> p^(1/t)`, renormalised). The
269
+ temperatures and the letter tokens are in `qev_config.json`.
270
+
271
+ ### Encodings
272
+
273
+ - **joint** (default): one row per request, with every question behind one copy of the state. This is the cheapest
274
+ encoding.
275
+ - **per_question** (`per_question=True`): one row per question, each with its own copy of the state. Each answer
276
+ then does not depend on which other questions were asked, or in what order. This raises JevBench coherence from
277
+ 64.5 to 71.2 at about the same accuracy. It costs one row per question in `qev.py`. The MLX runner in
278
+ [qev-0.8b-MLX](https://huggingface.co/labqoat/qev-0.8b-MLX) computes the shared state once, so there it costs about
279
+ the same as joint.
280
+
281
+ The two encodings give the same answers for single-question requests. For multi-question requests where consistency
282
+ across questions matters, use per_question.
283
+
284
+ **In this repo**, choose the encoding with `QEVMLX.load(..., encoding="per_question")`. The shared
285
+ state is computed once, so per_question costs about the same as joint here.
286
+
287
+ ## Evaluation
288
+
289
+ All numbers come from one harness, with the same items and scoring for every model. The temperatures were fitted on
290
+ our own calibration split. No benchmark data was used for training, tuning or calibration.
291
+
292
+ - **Benchmark Heaven (BH):** the public split, 231 items. The sampling error of BH accuracy is about ±2.8 points.
293
+ - **JevBench:** 240 cases, 1,938 gold questions. Coherence is scored by the official `jevbench` harness, ×100.
294
+ - **Jev suite:** 22 public-dataset tasks, 880 questions.
295
+ - **Pooled:** the mean of BH, JevBench and suite accuracy.
296
+ - **JevExtra:** our own skills benchmark: chess, browser, tools, agent action, prompt injection, safety, judging and
297
+ grounding. The headline is the mean skill accuracy.
298
+ - qev's indirect prompt-injection items share text with training rows (16-gram audit). The table includes them,
299
+ like every other item.
300
+ - **JevSpatial:** our games and spatial benchmark, 2,040 items: game moves with the facts in the question and in the
301
+ state, rules in the question, coordinates and small grids. Breakout, Whac-A-Mole, Knife Hit, 2048, Tetris and Pac-Man
302
+ are never used in training.
303
+ - **JevosTasks:** seven policy and rule tasks built like the ones other System One models report (admission, rental,
304
+ agent rules, authority, fraud points; ShARC dev; patent phrase similarity test), 3,440 items. The five generated
305
+ domains are never used in training.
306
+
307
+ Where two values are given, they are **joint / per_question**. The two are equal on BH and the suite, which have one
308
+ question per request.
309
+
310
+ | benchmark | metric | **qev-0.8b** (joint / per_question) | Qwen3.5-0.8B-Base (zero-shot) | decider-0.8b |
311
+ |---|---|---|---|---|
312
+ | Benchmark Heaven | accuracy | **0.771** | 0.528 | 0.658 |
313
+ | Benchmark Heaven | Brier (lower is better) | **0.337** | 0.569 | 0.430 |
314
+ | Benchmark Heaven | ECE (lower is better) | 0.063 | 0.089 | **0.061** |
315
+ | JevBench | accuracy | **0.851** / 0.849 | 0.439 | 0.822 |
316
+ | JevBench | coherence | 64.5 / **71.2** | 51.6 | 69.6 |
317
+ | Jev suite | accuracy | 0.617 | 0.392 | **0.682** |
318
+ | pooled (BH, JevBench, suite) | accuracy | **0.746** / 0.745 | 0.453 | 0.721 |
319
+ | JevExtra | mean skill accuracy | **0.720** | 0.442 | 0.581 |
320
+ | JevSpatial | accuracy | **0.742** | | |
321
+ | JevosTasks | accuracy | **0.637** | | |
322
+
323
+ **Contamination audit.** None of the training data shares any 16-token sequence with a BH or JevBench item. In the
324
+ Jev suite, 2 of 22,773 test-pool items reach 50% 16-gram coverage. The training data was also screened by meaning
325
+ against every test item, using embeddings with a threshold set from near copies.
326
+
327
+ **Where it is weakest:**
328
+
329
+ - JevBench `score` questions (4-level scales): accuracy 0.39. Most errors are off by one level, and predictions lean
330
+ toward the middle levels.
331
+ - Long and hard BH items: accuracy 0.51 on long items and 0.57 on the hard tier.
332
+ - The suite's wide-label and graded tasks: go_emotions, helpsteer2, stsb.
333
+ - Abstaining in agent decisions, when no tool fits or information is missing.
334
+
335
+ **Runtime fidelity.**
336
+
337
+ - On the 902-request dev set used for release checks (1,782 questions), `qev.py` on an NVIDIA L40S agrees with our
338
+ CUDA reference path on 99.7% of argmax answers, with mean absolute probability difference 0.003.
339
+ - On Apple MPS (bf16) and on CPU (fp32), over the first 50 requests (103 questions), the agreement is 99.0%: one
340
+ near-tie question flips. The mean difference is 0.003.
341
+ - The MLX and GGUF builds agree with the reference on 99.0–99.8% of argmax answers (see their repos).
342
+ - These agreement figures were measured on the first release's weights; the architecture, code and quantisation are
343
+ unchanged.
344
+
345
+ **This repo.** `qev_mlx.py` agrees with the CUDA reference on 99.3% (bf16) and 99.0% (8-bit) of argmax
346
+ answers. per_question with the shared state prefix agrees with running each row on its own on 99.7%.
347
+
348
+ ## Speed
349
+
350
+ ### qev_mlx.py (M4 Pro, 24 GB, bf16)
351
 
352
  Steady-state batch-1 latency over 200 dev requests (mean 746 tokens, median 417).
353
 
 
360
  Memory is 2.0 GB RSS and 2.6 GB Metal for bf16 (1.3 / 2.0 GB for 8-bit), plus about 25 MB per cached state. Prefill
361
  is compute-bound on the Mac GPU, so 8-bit saves memory, not time.
362
 
363
+ ### The CUDA engine in the main repo, for comparison
364
+
365
+ Measured on our dev requests: mean 746 tokens, median 417, about 2 typed questions each. These are longer
366
+ than the benchmark items behind the 3.5 ms p50 in the 27-model comparison above, hence the higher figure here. CUDA: one NVIDIA L40S with
367
+ our optimised engine (packed variable-length batches, the flash-linear-attention Gated DeltaNet kernels, CUDA graphs
368
+ for small batches). Apple: an M4 Pro (24 GB) with MLX, bf16, compiled.
369
+
370
+ | setting | hardware | p50 latency | throughput |
371
+ |---|---|---|---|
372
+ | single request, CUDA graphs | 1x L40S | 5.7 ms | ~100 req/s, ~200 decisions/s |
373
+ | packed batch (32k tokens, ~43 requests) | 1x L40S | ~370 ms | ~117 req/s, ~230 decisions/s |
374
+ | single request, MLX bf16 | Apple M4 Pro | 109 ms | ~6 req/s, ~11 decisions/s |
375
+
376
+ `per_question` (shared prefix): 7.9 ms p50 on the L40S. MLX prefix-cache hit: 18.5 ms.
377
+
378
+ The CUDA rows are what `qev.py` gives by default on an NVIDIA GPU: its fast backend (`qev_fast/`), after
379
+ `pip install -r requirements.txt -r requirements-cuda.txt`. In a fresh environment installed from those two files
380
+ alone (torch 2.14, flash-linear-attention 0.5.2, transformers 5.20; no vLLM), one L40S measured:
381
+
382
+ - 5.6 ms p50 and 19.5 ms p90 per request, 9.7 ms mean;
383
+ - about 105 requests/s one request at a time, and about 97 requests/s in packed batches (117 with vLLM's
384
+ FlashAttention-2 build installed);
385
+ - 99.8% argmax agreement with our evaluation path on 1,782 dev questions.
386
+
387
+ Start-up: about 25 s until ready when the kernel cache is warm, about 2 min on the very first start (kernel
388
+ compilation). The common CUDA graphs are captured at start; the rest are captured in the background after the
389
+ model is ready.
390
+
391
+ The model is 0.8B parameters: 1.7 GB in bf16, 0.8 GB in 8-bit. For Apple silicon, use
392
+ [qev-0.8b-MLX](https://huggingface.co/labqoat/qev-0.8b-MLX).
393
+
394
+ ## Training
395
+
396
+ qev-0.8b is a full fine-tune of Qwen3.5-0.8B-Base (all text layers and the final norm; the tied embeddings are the
397
+ base's). It was trained on our own decision data, in stages:
398
+
399
+ 1. **Base fine-tune:** two epochs, about 1.8B tokens, over a decision mix of 1.28M requests (learning rate 5e-6;
400
+ DiLoCo over 16 workers).
401
+ 2. **Anneal** on a newer data release (about 142M tokens).
402
+ 3. **Coherence anneal:** about 52M tokens, with a consistency loss on twin sets built from our own probability laws:
403
+ invariance to rephrasing and reordering, complements, partitions, bounds, monotonicity and conditioning. No
404
+ benchmark code or items were used.
405
+ 4. A further round on newer data with coherence twins (about 104M tokens).
406
+ 5. **Weight soups** of the trunk tensors to keep calibration while adding consistency.
407
+ 6. **A gap pass** (150M tokens) over data for weak skills, BH-style long hard decisions, hard yes / no and grounding
408
+ negatives, order-invariance twins, and replay.
409
+ 7. A 50/50 weight soup of the pre-gap and post-gap models (the first release).
410
+ 8. **A games, rules and spatial pass** (109M tokens with replay; 27M new): code-generated game-move decisions with the
411
+ facts in the question or in the state, rules written in the question, and coordinate / grid relations. The labels
412
+ are computed by code; the benchmark-only games and domains above are excluded, and every row was screened against
413
+ the benchmark items.
414
+ 9. **The release:** a 50/50 weight soup of the model from step 8 and the first release. The per-type temperatures
415
+ were then refit on the calibration split.
416
+
417
+ ## Training data and licenses
418
+
419
+ The decision data comes from public datasets and data generated for the project. Each training row records its
420
+ source, its license and how its label was made. Labels are of these kinds:
421
+
422
+ | label kind | share of rows |
423
+ |---|---|
424
+ | the source dataset's own label | about 49% |
425
+ | computed exactly by a program | 37% |
426
+ | a writer model's label confirmed by two independent solver models | 7% |
427
+ | a model's rating or soft distribution | about 7% |
428
+
429
+ By license, for the decision dataset release the training mix was drawn from (2.2M requests):
430
+
431
+ - **Permissive licenses**, about two thirds of the rows: CC-BY-4.0 (about 35% in total, including NVIDIA's Nemotron
432
+ agentic data, reasoning-core's procedural pile, FLD and ProofWriter), CC0-1.0 / public domain (about 17%, including
433
+ the CFPB complaint database and NVD), Apache-2.0 (about 8%), MIT (about 5%, including QA sets under mixed MIT /
434
+ CC-BY / Apache terms) and BSD-3-Clause (2.7%). A further 1.8% is under per-source upstream terms or ODC-BY.
435
+ - **Share-alike sources**, about 10.5%:
436
+ - CC-BY-SA-4.0 from MTOP (1.6%), the Schema-Guided Dialogue dataset (1.1%), SQuAD-derived QNLI (1.1%) and HotpotQA
437
+ (0.2%), plus other CC-BY-SA-4.0 sources (4.3%);
438
+ - DBpedia (CC-BY-SA-3.0, 1.1%);
439
+ - Bitext customer-support data (CDLA-Sharing-1.0, 1.0%).
440
+ - **Generated for the project**, about 17%:
441
+ - computed by programs (6.3%);
442
+ - written by an LLM and checked by two blind solver models (8.7%);
443
+ - other generated rows released under CC0 (2.0%).
444
+ - **Model-made labels:** a further 1.2% carries ratings made by a language model, under MIT.
445
+ - **Research-use releases:** about 0.7% comes from the LeWiDi research release, whose component datasets carry their
446
+ own terms, including research-use-only terms for one of them.
447
+
448
+ Later training stages added program-generated and writer/verifier-generated rows of the same kinds. The model is
449
+ released under Apache-2.0. Whether share-alike or research-use terms on a part of the training data affect a given
450
+ use of the weights is for the user to assess.
451
+
452
+ ## Limitations
453
+
454
+ - **Ordinal scores are the weakest type.** Treat `score` outputs as soft signals.
455
+ - **Long and hard inputs.** Accuracy drops on long states (several thousand tokens) and on deliberately hard items.
456
+ The model reads the request in one pass and does no reasoning.
457
+ - **Coherence is improved, not guaranteed.** Complements and marginalisation across related questions, and unit
458
+ restatements such as "90 min" vs "1.5 h", are still often violated. Use per_question for multi-question requests,
459
+ and do not assume that answers to related questions are mutually consistent.
460
+ - **Abstention.** The model rarely chooses "cannot answer" or "no tool call" options, even when it should. Give it an
461
+ explicit option and check its probability rather than relying on the argmax.
462
+ - **Calibration is fitted, not universal.** On data far from the calibration split, probabilities can be over- or
463
+ under-confident. Recalibrate on your own labelled data when probabilities drive decisions.
464
+ - **Language and domain.** The data is mostly English business, operations and agent-log decisions. Other languages
465
+ and domains have not been evaluated.
466
+ - **Options.** A question can have at most 255 options. Option names matter, because the model reads them.
467
+ - **Inherited behaviour.** The model inherits biases of the base model and of its data, part of which was labelled
468
+ by larger models. Do not use it as the only decision maker for consequential decisions about people (medical,
469
+ legal, credit, employment).
470
+
471
+ ## Files
472
+
473
+ | file | what it is |
474
+ |---|---|
475
+ | `model.safetensors`, `config.json` | the weights in bf16 and mlx-lm's Qwen3.5 config |
476
+ | `q8/` | the same model with 8-bit affine weights (group size 64) |
477
+ | `tokenizer.json`, `tokenizer_config.json`, `chat_template.jinja` | the tokenizer |
478
+ | `qev_config.json` | temperatures, the 255 option-letter tokens, the prompt and readout description |
479
+ | `qev_mlx.py` | the MLX runner |
480
+ | `SHA256SUMS` | checksums |
481
+
482
+ ## Provenance
483
+
484
+ The base is `Qwen/Qwen3.5-0.8B-Base` at revision `dc7cdfe2ee4154fa7e30f5b51ca41bfa40174e68`. qev-0.8b is the release
485
+ named "A1" (checkpoint SE50) in our internal reports.
486
+
487
+ The MLX weights are qev-0.8b's weights in mlx-lm's Qwen3.5 layout (`mlx_lm.utils.load_model` loads
488
+ them); `q8/` holds the same model with 8-bit affine weights, group size 64.
489
+
490
  ## License
491
 
492
+ Apache-2.0, as is the base model, Qwen3.5-0.8B-Base. See "Training data and licenses" for the data.
SHA256SUMS CHANGED
@@ -1,4 +1,4 @@
1
- 2df4901181a2a2bd9c0642d15c19d7d8ecfcb26941e5dfbc799a60456968278e README.md
2
  273d8e0e683b885071fb17e08d71e5f2a5ddfb5309756181681de4f5a1822d80 chat_template.jinja
3
  711035379ef578e900004acaf8de9130f12189438ee721dd935dcb3080b077af config.json
4
  aa600fc9b70a8830ef6c05da7b3228c1b8e73fa3c4810a707eb8df304817ebc1 model.safetensors
 
1
+ 7d655921ca9ef6e324c6c38d4915f71925c24307687e3dcd988478021be14b77 README.md
2
  273d8e0e683b885071fb17e08d71e5f2a5ddfb5309756181681de4f5a1822d80 chat_template.jinja
3
  711035379ef578e900004acaf8de9130f12189438ee721dd935dcb3080b077af config.json
4
  aa600fc9b70a8830ef6c05da7b3228c1b8e73fa3c4810a707eb8df304817ebc1 model.safetensors