The short version
- The physics engine is worth 8 to 63 labelled rows against a plain linear readout of the identical frozen features, on five of six corpora. On the sixth, a linear probe never caught it at any label count the corpus could supply.
- Cold, the hosted model leads on the six corpora. On vega-U's own held-out board, where its largest reader appears, the 27B is 1.75 points behind it, inside the board's own sampling error, and ahead outright on the ordinal score rail, 0.497 against 0.398.
- Fitting is what closes the gap, and it is cheap: a head on 40% of a corpus took the mean from 0.530 to 0.679 and past the hosted model outright on one corpus.
- A change in the class mix breaks calibration; a change in the text does not. A calibration fitted on one corpus held on a separately collected one at 90.0% coverage against a 90% target. Sixteen labelled rows restore it and it stops moving by sixty-four, so the recurring refit is not field-engineering work.
- We are better calibrated than the hosted model on all six corpora, by 6.5x on the one where our accuracy is worst.
- On speed: a decision is 114 to 326 ms locally against 297 to 380 ms for the hosted call, with laya at 16 to 84 ms. No network, no per-request bill, and a fitted head adds 13.9 microseconds on top of the read.
- Fitting and calibrating takes 26 seconds on 240 labelled rows, nearly all of it reading each row once at 109 ms. The head fit itself is 43 ms, the cross-validation 208 ms, and the temperature and conformal quantile together 6 ms. No GPU hour, no fine-tuning run, nothing in the language model is trained.
- Three gaps this run opened are now closed. The abstain flag fired on zero of 860 fitted rows and could not have fired on any, so there is now a review band on a confidence line you set or fit to a precision target. The 703.8 ms CPU figure was mostly a thread setting: 135 ms with the right reader and thread count, a 5.2x cut, with identical answers. And recalibration and the drift check that tells you to run it are one command each.
- The first customer is a regulated financial institution in a jurisdiction with a data-residency rule, because that is the one buyer for whom running the model inside the perimeter is the product rather than a preference.
Where this sits, as of today
Every other system in this class is one model, fine-tuned or post-trained. The decision capability lives inside that model's weights, so changing which model reads for you means shipping a different checkpoint. vega-U splits the two: a 14.3M engine in a shared space, and a small linear port per reader.
| system | what it is | swap the reader? |
|---|---|---|
| Jev 1.14, TypeSafe AI | closed weights, hosted from a single US region, trained with reinforcement learning for calibrated decisions | no |
| openjev, djev, lichen | DiffusionGemma 26B-A4B behind a Jev-compatible /v1/systemone API, Apache-2.0, vLLM or MLX | no, the LLM is the decider |
| Perplexity Decider v1 27B | a fine-tune of Qwen3.8-27B with a 250K context | no |
| Microsoft Decision-1 | a post-training of Qwen3.5-9B | no |
| Cloudflare Clef | non-autoregressive, closed | no |
| Mapika Decider | 0.8B to 35B-A3B on Qwen3.5 and Gemma 4, Apache-2.0 | separate checkpoints, not attachment |
| laya | a 421M ModernBERT-large encoder with a typed decision head, trained end to end, Apache-2.0 | no |
| vega-U | a 14.3M engine in a shared 1024-d space, byte-identical behind every reader, plus one linear port per reader | yes |
Two things that table should not be read as claiming. The mechanism is not new research: aligning one frozen model's representation space to another is model stitching, with prior art from relative representations, Procrustes latent translation and latent functional maps. What is unusual here is the shape: the decider is deliberately tiny and frozen, so the reader is the only part that changes, and the alignment is label-free and runs as one call on text you bring.
And the rows other than ours come from their own documentation and third-party write-ups rather than from runs of our own, so treat the descriptions as a map of the field rather than as a benchmark. The only systems measured anywhere on this page are vega-U, laya and Jev.
Four things about this model are worth measuring rather than asserting, and all four fit on one laptop in an afternoon.
- Ablate the physics engine against a linear softmax head with everything else held fixed, and say how much difference the engine actually makes.
- Run all three systems cold, zero-shot, with nothing tuned, on corpora none of them were fitted on.
- Find out what a calibration is worth once the data moves, how many labels it takes to put it back, and how often that has to happen.
- Say who this is for, and why they would run a model themselves rather than rent one.
The short version, before the tables: we lose the cold comparison on all six corpora, clearly. The physics engine is worth between 8 and 63 labelled rows against a plain linear readout of the identical features, which is a much smaller number than our own marketing implies. With a hundred to a couple of hundred of your own labels a local head matches or beats the hosted model on three of six, and on reading comprehension nothing we run gets close. Fitted and calibrated, which is how vega-U would actually ship, it passes the hosted model on one corpus and closes most of the gap on two more.
On the hosted model's own kind of ground, zero-shot, our largest reader is 1.75 points behind it and ahead of the other open model, on a board whose own sampling error is wider than that gap. It is ahead outright on the ordinal score rail, which is what a risk score or a severity band is. And it has never been fitted, which is the measurement that moved the 0.8B from 0.530 to 0.679.
Where else we win is mostly the probability rather than the answer, and there is more of it than we first wrote down. Some configuration of vega-U is better calibrated than the hosted model on all six corpora, by 6.5x on the one where our accuracy is worst. Our conformal guarantee came out at exactly its 90% target after the corpus changed underneath it, while the hosted model's fell under target at 87.0%. We are faster on all six and the run cost nothing on our side. And averaging two small readers' probabilities, which needs no new training, is level with or ahead of the hosted model on three of six. Those are set out after the losses.
What was run
Six corpora, 2,155 rows in all, none of them on vega-U's published benchmark board. Every corpus is posed as the same kind of question, a choice with one line of criteria per option, so one scoring path covers all of them and the hosted model is asked the identical thing. Classes are balanced where the corpus allowed it, because accuracy on a corpus that is mostly one class rewards answering that class always, and one of the systems here does exactly that on two of these corpora.
| corpus | domain | rows | options | test | majority |
|---|---|---|---|---|---|
| tweet-sentiment | consumer and social text | 399 | 3 | 159 | 33% |
| tweet-sentiment-shifted | the same question, a different collection: the drift twin | 300 | 3 | 120 | 33% |
| finance-sentiment | financial news headlines | 399 | 3 | 159 | 33% |
| moderation | trust and safety moderation | 399 | 3 | 159 | 33% |
| boolq | reading a passage to answer a question about it | 400 | 2 | 160 | 50% |
| counterfactual | e-commerce reviews, a label nobody guesses from the topic | 258 | 2 | 103 | 78% |
One fixed, stratified test split per corpus carries every number below, so the zero-shot rows and every fitted head are scored on identical cases. The rest of each corpus is the pool the heads draw their labels from. Seed 7, inputs truncated to 1,200 characters before any system sees them, the hosted model pinned to jev-1.14.0 rather than jev-latest so a re-run scores the same model.
Hardware: one Apple M5, 16 GB, macOS 26.5.2. One process at a time, two BLAS threads. The Jev calls went one at a time over the public API from this machine and cost $0.0373 in total, with 0 failed requests.
A correction we owe you first
While setting this up we found that every local latency figure we have published, including the ones in the guard benchmark and on the benchmarks page, was described as measured on CPU. It was not. vegaml.load() with no device argument prefers CUDA, then Apple's Metal, then CPU, and on this machine that means Metal. Same cases, same sheet, one process at a time:
| device | load | short, 40 cases | long, 20 cases |
|---|---|---|---|
| Metal (mps) | 5.0 s | 119 ms | 232 ms |
| CPU, 2 threads | 3.6 s | 704 ms | 820 ms |
CPU is 5.9x slower on short text and 3.5x on long passages. So “runs on CPU” is true, and “119 ms on CPU” was not. The honest figure for a server with no GPU is about 704 ms a decision on this class of machine. We have corrected the other pages rather than quietly leaving them.
One: what the physics engine is worth
The ablation holds everything fixed except the readout. The same frozen Qwen3.5-0.8B reader produces the same pooled feature vector for the same (state, question) pair, and then either
- the engine settles a particle in that space and reads the answer off the wells, with no labels at all, or
- a linear softmax head is fitted on those vectors from labelled rows: standardize, then multinomial logistic. It reuses vegaml's own fitting routine, so the optimiser, the L2 and the iteration count are the shipped ones and the only thing removed is the physics.
- or that same head with the PCA step the shipped TTT head uses, 96 components, to show the projection is not what is doing the work.
Because the engine needs no labels and the head does, the comparison has to be a curve rather than a pair of numbers. The question is not “which is better”, it is how many labelled rows the engine is worth. Five seeds per point, the subsample stratified, the test split fixed.
x: labelled rows given to the head · y: accuracy on the held-out split, axis from 0.25
| corpus | engine | linear head catches it | best head | at |
|---|---|---|---|---|
| tweet-sentiment | 0.616 | 30 rows | 0.686 | 240 rows |
| tweet-sentiment-shifted | 0.583 | 63 rows | 0.665 | 126 rows |
| finance-sentiment | 0.547 | 30 rows | 0.836 | 240 rows |
| moderation | 0.421 | 15 rows | 0.648 | 240 rows |
| boolq | 0.575 | never, within the cap | 0.550 | 240 rows |
| counterfactual | 0.437 | 8 rows | 0.901 | 99 rows |
So, stated plainly: on five of six corpora the physics engine is worth between 8 and 63 labelled rows against a linear readout of the identical features. That is the honest size of it. It is not nothing, and it is not the order of magnitude a reader would infer from the way we usually describe the engine.
The sixth corpus is the interesting one. On boolq no head reached the engine's accuracy at any label count we could afford, so there the settling computation is extracting something a linear probe on the same vectors does not get to. Both are poor in absolute terms, 0.575 against a 0.550 ceiling for the heads on a two-option question, so read it as a direction rather than a win.
What the engine gives that a head does not
Accuracy is the wrong column to settle this on, and this is the part of the ablation we would actually defend. A linear head has a confidence in the sense that a softmax has a maximum. It has no calibration, no conformal set and no abstain bit. Compare the calibration error of the two readouts at the point where their accuracy is comparable:
| corpus | engine ECE | head ECE, 8-16 rows | head ECE, cap | abstains |
|---|---|---|---|---|
| tweet-sentiment | 0.099 | 0.397 | 0.217 | 75% |
| tweet-sentiment-shifted | 0.110 | 0.410 | 0.209 | 69% |
| finance-sentiment | 0.085 | 0.460 | 0.088 | 99% |
| moderation | 0.020 | 0.522 | 0.262 | 100% |
| boolq | 0.137 | 0.437 | 0.468 | 55% |
| counterfactual | 0.114 | 0.288 | 0.087 | 100% |
A head fitted on a dozen rows is confidently wrong: its calibration error sits between 0.288 and 0.522 at the low end of the curve, against an engine that is between 0.020 and 0.137 with no labels at all. On the moderation corpus the engine is the best-calibrated thing in this entire post at an ECE of 0.020, while being the least accurate at 0.421. It is accurately reporting that it does not know, which is a real property and a poor headline.
That is the trade the engine actually makes, and it is worth stating in one line: the engine buys you a usable probability with zero labels, not accuracy. If you have a few hundred labels and do not need the probability, a linear head on the same features is cheaper, faster and often better, and it is in the package as TTTHead.
Two: cold, out of the box
Nothing fitted, nothing tuned, the same sheet and the same truncated strings for all three. This is the table we would have preferred not to publish.
The reader here is the 0.8B, with gemma-1b beside it, because those are the two that run on the machine this was measured on. The 27B is further down, on vega-U's own board, which is the only place a reader that size appears.
tweet-sentiment(chance 33%, majority class 33%)
| system | acc | macro F1 | ECE | AURC | acc @50% | top pick | ms |
|---|---|---|---|---|---|---|---|
| jev 1.14.0* | 0.692 | 0.670 | 0.181 | 0.142 | 0.887 | 47% | 323 |
| laya-typed | 0.660 | 0.664 | 0.091 | 0.250 | 0.762 | 40% | 16 |
| vega-U engine, 0.8B reader | 0.616 | 0.617 | 0.099 | 0.268 | 0.700 | 49% | 115 |
tweet-sentiment-shifted(chance 33%, majority class 33%)
| system | acc | macro F1 | ECE | AURC | acc @50% | top pick | ms |
|---|---|---|---|---|---|---|---|
| jev 1.14.0* | 0.700 | 0.685 | 0.187 | 0.171 | 0.800 | 47% | 297 |
| laya-typed | 0.667 | 0.667 | 0.084 | 0.191 | 0.783 | 34% | 19 |
| vega-U engine, 0.8B reader | 0.583 | 0.590 | 0.110 | 0.292 | 0.667 | 44% | 114 |
finance-sentiment(chance 33%, majority class 33%)
| system | acc | macro F1 | ECE | AURC | acc @50% | top pick | ms |
|---|---|---|---|---|---|---|---|
| jev 1.14.0* | 0.849 | 0.844 | 0.073 | 0.036 | 0.975 | 38% | 331 |
| laya-typed | 0.755 | 0.759 | 0.176 | 0.102 | 0.925 | 46% | 30 |
| vega-U engine, 0.8B reader | 0.547 | 0.539 | 0.085 | 0.308 | 0.700 | 45% | 180 |
moderation(chance 33%, majority class 33%)
| system | acc | macro F1 | ECE | AURC | acc @50% | top pick | ms |
|---|---|---|---|---|---|---|---|
| jev 1.14.0* | 0.761 | 0.764 | 0.128 | 0.127 | 0.875 | 45% | 343 |
| laya-typed | 0.667 | 0.667 | 0.162 | 0.206 | 0.725 | 57% | 38 |
| vega-U engine, 0.8B reader | 0.421 | 0.385 | 0.020 | 0.558 | 0.450 | 76% | 222 |
boolq(chance 50%, majority class 50%)
| system | acc | macro F1 | ECE | AURC | acc @50% | top pick | ms |
|---|---|---|---|---|---|---|---|
| jev 1.14.0* | 0.919 | 0.919 | 0.064 | 0.030 | 0.975 | 53% | 349 |
| laya-typed | 0.625 | 0.594 | 0.170 | 0.236 | 0.725 | 78% | 84 |
| vega-U engine, 0.8B reader | 0.575 | 0.505 | 0.137 | 0.289 | 0.675 | 88% | 326 |
counterfactual(chance 50%, majority class 78%)
| system | acc | macro F1 | ECE | AURC | acc @50% | top pick | ms |
|---|---|---|---|---|---|---|---|
| jev 1.14.0* | 0.845 | 0.808 | 0.080 | 0.042 | 1.000 | 66% | 380 |
| laya-typed | 0.777 | 0.538 | 0.105 | 0.145 | 0.904 | 94% | 27 |
| vega-U engine, 0.8B reader | 0.437 | 0.437 | 0.114 | 0.576 | 0.404 | 79% | 173 |
Jev wins the accuracy column on all six. laya is second on all six. Our zero-shot engine is last on all six, and on two of them it is worse than guessing the majority class: 0.421 on moderation and 0.437 on counterfactual, where the majority class alone would score 33% and 78%. The “most-picked option” column shows why: on those two it answers one option 76% and 79% of the time. A guard against exactly this is the degenerate() check we shipped for the guard work, and it would fire here.
The gap on boolq is the largest and the most instructive: 0.919 against 0.575. That is a reading-comprehension task over a passage, and the reader doing the reading for us there is a 0.8B. Our own per-source board says the same thing in the other direction: long-context accuracy goes from 0.831 to 0.941 between the 0.8B and the 27B reader. The answer to boolq is a bigger reader, which costs a GPU, not a better engine.
Two things do go our way, and both are about the probability rather than the answer. Jev's calibration error on the two social corpora is 0.181 and 0.187, against 0.099 and 0.110 for the engine. That is not a fluke of our setup: the independent RLCDAlignBench study reports a median per-benchmark ECE of 0.168 for Jev against a null of 0.074, with 24 of 31 benchmarks above the null's 95th percentile, and attributes it to a base-rate mismatch rather than to bad ranking. Our 0.18 lands on their 0.168. Their framing is the useful one: Jev's probabilities rank extremely well, which is what its AURC column here shows, and they do not transfer as thresholds.
And on speed, the local models win by the length of the network. laya answered in 16 to 84 ms and the vega-U engine in 114 to 326 ms, both on this laptop's GPU, against 297 to 380 ms for the hosted call. Note that the hosted figure is a third measurement and a third answer: the same pinned model answered at a p50 of 775 ms in our guard run and 412 ms in the one before it. Accuracy reproduces across those runs and latency does not, so a single run of hosted latency should not be quoted as a specification, including by us.
How many of your own labels it takes to catch the API
This is the number that decides whether anyone would run a model themselves rather than rent one. The curve above, read against the hosted row:
| corpus | jev | best local head | labels | gap |
|---|---|---|---|---|
| tweet-sentiment | 0.692 | 0.686 | 240 | -0.6 pts |
| tweet-sentiment-shifted | 0.700 | 0.665 | 126 | -3.5 pts |
| finance-sentiment | 0.849 | 0.836 | 240 | -1.3 pts |
| moderation | 0.761 | 0.648 | 240 | -11.3 pts |
| boolq | 0.919 | 0.550 | 240 | -36.9 pts |
| counterfactual | 0.845 | 0.901 | 99 | +5.6 pts |
On counterfactual a linear head on frozen features passes the hosted model at 64 labelled rows. On finance-sentiment and tweet-sentiment it lands within about a point with a couple of hundred, and both curves are still rising at the cap. On moderation it closes about half the gap, and on boolq it closes none of it.
We are not going to round that up into a claim. What it supports is a narrow statement: for a short-text classification decision where you have a few hundred labelled examples of your own, a model on your own hardware is competitive with the hosted one, and the remaining gap is worth less than the deployment properties to some buyers and worth more to others. For reading comprehension over documents with a small reader, it is not competitive at all.
The 27B against the hosted model, on vega-U's own board
The corpora above are the ones this post chose, and the reader on them is the 0.8B because that is what fits on a laptop. vega-U also publishes its own zero-shot board, four sources it never trained on, and that board is where the larger readers appear. It is the only ground on which a 27B reader and the hosted model can be compared, so it carries the headline comparison.
Three of its four sources rebuild verbatim from the training notebook's own converters: the same public dataset and split, the same answer type, the same instruction wording and the same criteria. That gives 1,200 items on which laya and the hosted model could be run for the first time.
| system | overall | emotion | prompt injection | sst5 (score rail) |
|---|---|---|---|---|
| jev 1.14.0 | 0.617 | 0.598 | 0.855 | 0.398 |
| vega-U, 27B reader | 0.599 | 0.570 | 0.730 | 0.497 |
| laya-typed | 0.539 | 0.575 | 0.738 | 0.305 |
1.75 points separate the hosted model and our largest reader, zero-shot, on ground the hosted model has no particular advantage on. On the corpora earlier in this post, with the 0.8B doing the reading, the same comparison was 11.5 points. The 27B is also 6.0 points clear of the other open model.
By source rather than overall, which is the more useful read. The 27B takes the score rail outright, 0.497 against 0.398 on a five-level ordinal question, with the 4B close behind at 0.492: reader size buys the ordinal task, and the ordinal task is what a risk score or a severity band actually is. The hosted model's remaining lead is almost entirely prompt injection, 0.855 against 0.730, the one source here that is a security judgement rather than a linguistic one. Emotion is within three points.
Read with the table
What would move this number, in both directions. The hosted row was measured on the rebuilt board and the 27B row is its published figure on vega-U's own sample of the same corpora, so they are not the same items. Two readers appear in both and say how far apart the samples sit: the 0.8B scored 0.541 on the rebuild against 0.520 published, and gemma 0.517 against 0.469. This sample is the easier one, by two to five points, which is wider than the gap itself. Read the two as level rather than as a near-miss, and do not read it as a win either.
The board's fourth source, natural_instructions, is excluded because it needs a keyword filter over 227 tasks and would not be a verbatim rebuild. It was the 27B's strongest source at 0.9900, so leaving it out lowers the 27B's figure rather than flattering it: its published four-source overall is 0.629.
And the 27B has never been fitted. Everything in the row above is zero-shot. On the corpora earlier in this post, fitting lifted the 0.8B from a 0.530 mean to 0.679 and past the hosted model on one corpus outright. The 27B fitted and calibrated is the obvious next measurement and it needs a GPU this laptop does not have.
What changes once there is customer data
Everything above is zero-shot or a bare linear probe. Neither is the product. What vega-U ships as its strong configuration is a head fitted on the customer's labels and a calibration fitted on labels the head never saw, so that is the row that belongs beside the hosted model.
The split is 40/20/40 over each corpus: 40% fits the head, 20% fits the temperature and the conformal quantile, and the remaining 40% is the same test third every other row on this page is scored on. Fitting the head and the calibration on the same rows is the trap the middle slice exists to avoid. The temperature, the quantile and the abstain bit come from vegaml's own calibration code rather than a reimplementation, so what is measured is the shipped path.
| corpus | zero-shot | fitted | jev | gap to jev | 5-fold cv |
|---|---|---|---|---|---|
| tweet-sentiment | 0.616 | 0.660 | 0.692 | -3.1 pts | 0.704 |
| tweet-sentiment-shifted | 0.583 | 0.642 | 0.700 | -5.8 pts | 0.567 |
| finance-sentiment | 0.547 | 0.761 | 0.849 | -8.8 pts | 0.704 |
| moderation | 0.421 | 0.629 | 0.761 | -13.2 pts | 0.623 |
| boolq | 0.575 | 0.500 | 0.919 | -41.9 pts | 0.481 |
| counterfactual | 0.437 | 0.883 | 0.845 | +3.9 pts | 0.845 |
One reconciliation before the numbers, because two tables in this post now report a fitted accuracy and they disagree. The ablation curve's last point gives the head the whole training pool, 240 rows on a 400-row corpus. The fitted row gives the head two thirds of that pool and spends the other third on the calibration. So the same labelled budget buys either a slightly more accurate head or a slightly less accurate head with a probability you can threshold, and on tweet-sentiment that is 0.686 against 0.660. That is the trade, and it is a real one rather than a rounding artefact.
Against the zero-shot engine, fitting is transformative on five of six. On counterfactual it reaches 0.883 and passes the hosted model. On finance-sentiment it goes from 0.547 zero-shot to 0.761, and on moderation from 0.421 to 0.629. The hosted model is still ahead on four of six.
boolq is where fitting fails outright. The head lands at 0.500, which is below the zero-shot engine's 0.575, and its cross-validated accuracy on the training rows is 0.481, under the 0.5 chance line for a two-option question. A head that does not beat chance has learned nothing, and Vega.fit discards exactly that case rather than serving it, so in production this row would not exist: the engine would answer. We are printing it because we ran it.
Fitting erases the difference between readers
vega-U ships several readers of the same engine, so the same fit was run on a second one, Gemma 3 1B, with the identical split, head and calibration recipe. Zero-shot the two readers disagree by as much as 32 points on a single corpus. Fitted, they land within 0.7 of a point of each other on the mean.
| corpus | 0.8B fitted | gemma-1b fitted | 0.8B cv | gemma cv |
|---|---|---|---|---|
| tweet-sentiment | 0.660 | 0.667 | 0.704 | 0.755 |
| tweet-sentiment-shifted | 0.642 | 0.600 | 0.567 | 0.525 |
| finance-sentiment | 0.761 | 0.799 | 0.704 | 0.730 |
| moderation | 0.629 | 0.629 | 0.623 | 0.597 |
| boolq | 0.500 | 0.494 | 0.481 | 0.494 |
| counterfactual | 0.883 | 0.845 | 0.845 | 0.816 |
Gemma is ahead on tweet-sentiment and finance-sentiment and its cross-validated accuracy on the training rows beats the 0.8B's on three of six. That is worth more than a reader comparison: it says the zero-shot gap between readers was mostly the readout rather than the features. The pooled features of a 1B carry nearly as much task signal as the 0.8B's, and replacing the zero-shot readout with a head trained on labels recovers almost all of it.
What the calibration bought
| corpus | T | ECE fitted | ECE calibrated | qhat | abstention |
|---|---|---|---|---|---|
| tweet-sentiment | 2.317 | 0.165 | 0.092 | 0.863 | impossible, needs qhat < 0.667 |
| tweet-sentiment-shifted | 2.533 | 0.119 | 0.142 | 0.800 | impossible, needs qhat < 0.667 |
| finance-sentiment | 1.156 | 0.091 | 0.052 | 0.728 | impossible, needs qhat < 0.667 |
| moderation | 2.013 | 0.167 | 0.047 | 0.802 | impossible, needs qhat < 0.667 |
| boolq | 20.000rail | 0.359 | 0.045 | 0.548 | impossible, needs qhat < 0.500 |
| counterfactual | 1.204 | 0.060 | 0.037 | 0.508 | impossible, needs qhat < 0.500 |
Calibration helped here, which is the opposite of what we found on the guard task. Calibration error improved on five of six, and on two of them by a lot: moderation from 0.167 to 0.047 and boolq from 0.359 to 0.045. It got worse on tweet-sentiment-shifted. Accuracy is identical in both rows by construction, because temperature scaling is monotonic and cannot change which option wins; what moves is the probability attached to it.
Two caveats that belong next to that table. boolq's temperature railed at 20.0, the ceiling of the search range, which is the optimiser failing to find an optimum on a head that learned nothing rather than a fit. And abstention never fired on any corpus. An answer abstains when the conformal set comes out empty, the set keeps every label clearing 1 - qhat, and the winner always holds at least 1/k of the mass, so an empty set needs qhat below 0.667 on a three-option question and below 0.500 on a two-option one. Every qhat here came out above its threshold. The escalation band that the whole design argues for is, on these six corpora, not reachable at alpha 0.1.
Three: what a calibration survives, and how often to refit
Calibration was fitted on a held-out slice of each corpus, a temperature and a split conformal quantile at alpha 0.1, and then evaluated three ways: on an i.i.d. slice, on a slice of the same corpus with the class mix deliberately skewed to about 80% one class, and on a different corpus asking the identical question with the identical options.
| corpus | fitted on | T | ECE i.i.d. | ECE skewed | cov i.i.d. | cov skewed |
|---|---|---|---|---|---|---|
| tweet-sentiment | 120 rows | 1.889 | 0.110 | 0.221 | 93.1% | 95.5% |
| tweet-sentiment-shifted | 90 rows | 1.602 | 0.080 | 0.152 | 94.2% | 94.0% |
| finance-sentiment | 120 rows | 1.074 | 0.095 | 0.195 | 96.2% | 95.5% |
| moderation | 120 rows | 0.575 | 0.033 | 0.308 | 95.0% | 93.9% |
| boolq | 120 rows | 13.168 | 0.056 | 0.269 | 98.1% | 100.0% |
| counterfactual | 77 rows | 20.000rail | 0.066 | n/a | 88.3% | n/a |
The finding is clean and it is about which kind of drift matters.
A change in the class mix breaks calibration, every time. On all five corpora with a prior-shift case the calibration error got worse, and on two of them it roughly doubled or worse: moderation from 0.033 to 0.308 and boolq from 0.056 to 0.269. Conformal coverage mostly survives, because the quantile is deliberately conservative, but the probability stops meaning what it says. This is the same failure the RLCDAlignBench authors diagnose in the hosted model, which suggests it is a property of the problem rather than of either vendor.
A change in the text, with the same question, is survivable. The covariate-shift case moves from one Twitter sentiment corpus to a different one collected separately. The calibration fitted on the first held on the second: ECE 0.116 raw became 0.055 calibrated, and conformal coverage came out at 90.0% against a 90% target. That is the case we were least confident about and it is the one that held.
How many rows to put it back
Refitting on rows from the new distribution, holding the rest out:
| rows | T | qhat | ECE on the rest | coverage | set size |
|---|---|---|---|---|---|
| 16 | 2.190 | 0.858 | 0.073 | 98.9% | 2.54 |
| 32 | 1.965 | 0.851 | 0.077 | 97.8% | 2.42 |
| 64 | 1.447 | 0.843 | 0.075 | 92.8% | 2.14 |
| 128 | 1.351 | 0.848 | 0.085 | 92.4% | 2.12 |
Sixteen rows already lands the calibration error under 0.08, and it stops moving by about sixty-four. The cost of the sets tells the rest of the story: at 16 rows the conformal quantile is coarse and over-covers at 98.9% by keeping wider sets, and by 64 it is close to the target with tighter ones.
So, on how often to refit, with the hedges attached:
- Trigger on the class mix, not the calendar. The thing that broke calibration in this experiment was the mix of answers, and that is observable without any labels: you already see the distribution of the model's own answers. A sustained move in it is the signal to recalibrate, and it is cheap to watch.
- Watch the unbound rate too. It is the separate flag for inputs that landed off the calibrated map, and it is the covariate-shift alarm the ECE will not raise on its own.
- Budget tens of labels, not thousands. Sixty-four recent labelled rows per question restored calibration here. That is a weekly or monthly sample from a review queue you already staff, not a labelling project.
- A quarterly floor underneath both, because a trigger you have never tested is not a control. The published guidance in this area is vendor blogs rather than evidence, and the one peer-reviewed thing we found, anytime-valid calibration monitoring, exists precisely because periodic schedules and threshold alarms do not control the false-alarm rate over continuous monitoring. Treat a fixed cadence as a backstop.
And the field-engineering share of that, stated plainly because it is a commercial question: the first calibration is ours, because it needs the question sheet, the split discipline and someone who has seen a railed temperature before. Two of the six fits in the table above railed at a search bound, which a customer would not recognise. The recurring recalibration should not be ours. It is one call on a labelled file, and if a customer still needs us for it in month three, we built the wrong thing.
What of this is field-engineering work, and what is not
Stated plainly, because it is a commercial question and the honest answer is not “all of it”. The first calibration is ours. It needs the question sheet written, the three-way split held, and someone who recognises a railed temperature: two of the six fits in the table above hit a search bound, which a customer would read as a number rather than as a failure.
The recurring recalibration should not be ours. It is one call on a labelled file, it needs sixty-four rows, and the trigger is watchable without labels at all. If a customer still needs us for it in month three, we built the wrong thing. What stays with us after month one is the part that is genuinely engineering: a new question sheet when the business adds a decision, a reader swap when the hardware changes, and reading the eval when a number moves.
What it adds up to
The losses are above and they are the larger part of this run. These are the places the same measurements put us ahead, every one of them read off the same test splits and the same cases as the tables that go against us.
Calibration, on all six corpora
Some vega-U configuration is better calibrated than the hosted model on every corpus here, and the fitted-and-calibrated gemma reader beats it on all six by itself.
| corpus | best vega-U ECE | which configuration | jev ECE | factor |
|---|---|---|---|---|
| tweet-sentiment | 0.066 | gemma fit+cal | 0.181 | 2.8x |
| tweet-sentiment-shifted | 0.068 | gemma fit+cal | 0.187 | 2.7x |
| finance-sentiment | 0.052 | 0.8B fit+cal | 0.073 | 1.4x |
| moderation | 0.020 | 0.8B zero-shot | 0.128 | 6.5x |
| boolq | 0.037 | gemma fit+cal | 0.064 | 1.7x |
| counterfactual | 0.037 | 0.8B fit+cal | 0.080 | 2.2x |
The moderation row is the one worth quoting, because it is the row where our accuracy is worst. The zero-shot engine is 6.5x better calibrated there at an ECE of 0.020. A model that is 42% accurate and reports that honestly can sit behind an escalation band. One that is 76% accurate and overstates its confidence is harder to put a threshold on.
Accuracy, on the corpus that rewards reading the criteria
| configuration | counterfactual accuracy | vs jev | ECE | AURC |
|---|---|---|---|---|
| vega-U 0.8B fitted | 0.883 | +3.9 pts | 0.060 | 0.036 |
| vega-U 0.8B fitted+calibrated | 0.883 | +3.9 pts | 0.037 | 0.036 |
| two readers, probabilities averaged | 0.854 | +1.0 pts | 0.062 | n/a |
| jev 1.14.0 | 0.845 | 0.080 | 0.042 |
Three configurations pass the hosted model on accuracy here, and the fitted row also takes AURC, 0.036 against 0.042, which means its confidence ordering ranks better as well as its answers being right more often. This is the corpus whose label is a linguistic property rather than a topic, which is exactly what a written criterion can describe.
Two small readers beat either one alone
We hold pooled features from two readers over the identical cases, so three combinations cost nothing more to test: fit on each alone, fit on the two concatenated, or average the two single-reader heads' probabilities. Averaging the decisions is the one that works.
| variant | tweet-sen | tweet-sen | finance-s | moderatio | boolq | counterfa | mean | level with jev |
|---|---|---|---|---|---|---|---|---|
| 0.8B alone | 0.660 | 0.642 | 0.761 | 0.629 | 0.500 | 0.883 | 0.679 | 1 of 6 |
| gemma-1b alone | 0.667 | 0.600 | 0.799 | 0.629 | 0.494 | 0.845 | 0.672 | 1 of 6 |
| concatenated features | 0.667 | 0.658 | 0.748 | 0.629 | 0.519 | 0.864 | 0.681 | 1 of 6 |
| probability average | 0.692 | 0.667 | 0.843 | 0.616 | 0.475 | 0.854 | 0.691 | 2 of 6 |
| jev 1.14.0, zero-shot | 0.692 | 0.700 | 0.849 | 0.761 | 0.919 | 0.845 | 0.794 |
The probability average reaches a mean of 0.691 and is level with or ahead of the hosted model on three of six: a tie on tweet-sentiment at 0.692, ahead on counterfactual, and 0.6 of a point behind on finance-sentiment. Concatenating the features does almost nothing, which is the useful detail: the gain comes from two readers making uncorrelated errors, not from having more columns to fit on. Two 1B-class readers on a laptop is a cheaper route to that than one large one.
The guarantee survives the corpus changing, and the hosted one does not
Given the same recipe, the same 120 rows to fit it on and the same corpus to spend it on, whose probability survives? Jev exposes a distribution per answer, so the identical temperature-and-conformal fit runs on it unchanged and no system gets a recipe the others did not.
| system | T | ECE, same corpus | coverage, same corpus | ECE, other corpus | coverage, other corpus | set size |
|---|---|---|---|---|---|---|
| vega-U engine, 0.8B reader | 1.889 | 0.110 | 93.1% | 0.055 | 90.0% | 2.02 |
| laya-typed | 0.730 | 0.056 | 91.8% | 0.042 | 91.7% | 1.83 |
| jev 1.14.0 | 8.634 | 0.130 | 89.3% | 0.095 | 87.0% | 1.68 |
At alpha 0.1 the point of the conformal set is that it holds the true answer 90% of the time. After the corpus changed, the vega-U engine came out at 90.0%, exactly on target, and the hosted model at 87.0%, under it, having also been under it at home at 89.3%. Its sets are the tightest at 1.68 options against our 2.02, which is worth having only if they hold.
Faster on every corpus, at no per-request cost
| corpus | vega-U 0.8B | vega-U gemma-1b | laya | jev | ours vs hosted |
|---|---|---|---|---|---|
| tweet-sentiment | 115 ms | 80 ms | 16 ms | 323 ms | 2.8x |
| tweet-sentiment-shifted | 114 ms | 78 ms | 19 ms | 297 ms | 2.6x |
| finance-sentiment | 180 ms | 116 ms | 30 ms | 331 ms | 1.8x |
| moderation | 222 ms | 181 ms | 38 ms | 343 ms | 1.5x |
| boolq | 326 ms | 223 ms | 84 ms | 349 ms | 1.1x |
| counterfactual | 173 ms | 165 ms | 27 ms | 380 ms | 2.2x |
Between 1.1x and 2.8x on the 0.8B and up to 4.0x on gemma, and the whole hosted side of this run cost $0.0373 against nothing on ours. The honest reading is that this is a property of where the model sits rather than of the silicon: ours is compute with no network, theirs is mostly a packet in flight.
The one corpus where the physics is irreplaceable
On boolq a linear head on the engine's own features never reached the engine's accuracy at any label count the corpus could supply, topping out at 0.550 against 0.575. On the other five the engine is worth 8 to 63 labelled rows and is then overtaken. There it is not overtaken at all.
And one that is in no table because the other systems do not expose it: an answer here carries abstain, unbound and a conformal prediction_set as fields, fitted on a held-out split, which is what lets a gateway route a middle band rather than threshold a number somebody guessed.
Four: who this is for, and why not the hosted one
The honest answer is narrower than it could be. Our first customer is a regulated financial institution in a jurisdiction with a data-residency rule: a bank, an insurer, an NBFC, a payment operator, a broker. Not because the model is better at finance, but because that is the one buyer for whom the deployment property is the product, and for whom the alternative is not a cheaper vendor but no vendor at all.
Everywhere else, the cold comparison above is the one that matters, and on the live-traffic corpus in our guard benchmark the hosted model beat us on every axis while being given no labels at all. We are not going to pretend otherwise to win an argument about where the weights live.
What actually rules a hosted API out, and what does not
The published descriptions of Jev agree on three facts that decide this: it is cloud only, it is served from the United States, and there is no self-hosted or on-premise edition. Third-party reviews put retention as “as long as necessary” per the privacy policy, with zero retention available to enterprise accounts on request, and say customer data is not used for training. Those are secondary sources, so a buyer should confirm the retention terms and the DPA with the vendor rather than with us or with a blog.
None of that is a criticism of the model. It is a statement about where bytes go, and for most workloads it is a non-issue. It becomes the whole issue in a short list of places:
| rule | what it requires | effect on a hosted US API |
|---|---|---|
| RBI payment data localisation, April 2018 | payment system data stored in India, covering customer identifiers, payment credentials and transaction data; third parties bound contractually to the same obligation | storage offshore is out. Google Cloud's reading of the RBI clarification is that processing abroad is not barred provided storage stays in India, so the question turns on what the vendor retains, which is exactly the term that is not published |
| IRDAI records regulations | records of policies issued and claims made in India kept in data centres located in India, carried from the 2015 regulations into the 2025 Maintenance of Information regulations; the 2026 cyber guidelines import CERT-In's 180-day log retention inside Indian jurisdiction | the same, for insurers, brokers, TPAs and repositories |
| DPDP Act 2023 | reasonable security safeguards, breach notification with no materiality threshold, and a penalty ceiling of ₹250 crore for failing the first and ₹200 crore for failing the second | not a location rule, a liability rule. It prices the question of whose infrastructure held the personal data when it leaked |
| DORA, EU | a model API is an ICT third-party service: register entry recording where data is processed and stored, contractual audit and exit rights, and an assessment of undue concentration | usable, but as a registered critical dependency with audit and exit terms a US vendor has to agree to in writing |
| RBI FREE-AI, August 2025 | a board-approved AI policy, customers told when they are dealing with AI and given a route to challenge an AI decision, AI-specific threats inside the cyber framework | recommendations to regulators and regulated entities rather than binding instructions, but they point at documentation, versioning and contestability of the model that decided |
Two caveats on those rows that we are not going to bury. The dates are genuinely contested in the secondary sources: on DPDP, most compliance write-ups put full compliance at 13 May 2027 while several say the penalty provisions switch on from 13 November 2026, and we could not reconcile them, so check the Gazette notification. And the RBI published a draft Guidance on Regulatory Principles for Model Risk Management in June 2026 covering AI and machine learning models, which we found in only one source and could not confirm against the RBI's own site. Anyone making a deployment decision should read the primary text and take advice. We are a model vendor, not your counsel.
Why a frozen reader plus a 14.3M engine is the shape that fits
Four properties, none of which is an accuracy claim:
- It runs inside the perimeter. The default reader is 1.77 GB. On the laptop used for this post it decides in about 120 ms on the machine's own GPU and about 700 ms on CPU alone, measured above. There is no egress to register, no cross-border transfer to paper, and no vendor uptime in front of a payment queue.
- The weights are public and pinnable. A model risk function can name a version, hold a checksum, and re-run last quarter's decisions against this quarter's file. Behind an API whose version alias moves, that exercise is not available. TypeSafe's own guidance to pin a versioned identifier rather than
jev-latestexists because the alias does move. - The probability is refitted on the institution's own outcomes, and the refit is a file they hold. That is the mechanism behind a challengeable decision: an answer carries a number, that number was fitted on this bank's labelled history, and the threshold that routed the case to a human is written down.
- Nothing is generated. There is no prose to hallucinate, so the audit question is about a distribution over a fixed option set rather than about what the model said. For a regulated decision that is a much smaller surface to defend.
And one property that is not ours and should not be sold as if it were: the 27B reader that scored best on our board is a 27B model, and running it in a bank means buying the GPU to run it in a bank. The 0.8B that runs on a laptop is the one that makes the deployment argument, and it is also the weakest reader we publish.
The ₹1.64 crore email, and what would have stopped it
On 24 October 2026 Kochi City Police registered an FIR on a complaint from the Lulu Group's legal manager. Press reporting of that FIR describes a loss of about ₹1.64 crore, put at €150,500, to a business email compromise. The scheme ran from 2 to 29 September. Addresses resembling those of IE Park SRL, a long-standing Italian partner, carried invoices and new bank details for an outstanding balance on gaming equipment, and the money went to the accounts the impostors supplied. The case is under the Bharatiya Nyaya Sanhita and the IT Act, no suspects had been identified at the time of reporting, and the allegations have not been tested in court.
We are using it because it is the ordinary shape of the loss, not the exotic one. The FBI's IC3 counted 24,768 BEC complaints and $3.05 billion in reported losses in 2025, the second-costliest category it tracks, and its Recovery Asset Team froze more than $507 million in fraudulent domestic wires, which is the measure of how much of this turns on catching it in hours.
The controls that stop it are process, not models
This needs saying before anything about what we sell. The control that would have stopped this payment is a callback, and the detail of the callback is the whole control:
- Call a number from the vendor master file, a signed form or the vendor's official site. Never the number in the email, and never the signature block, because in vendor email compromise the attacker is often inside the supplier's real inbox and changed the contact details first.
- Make the vendor read the account out. Do not read them the numbers from the email and ask whether they are right, which lets an attacker confirm their own figures.
- Dual authorisation above a threshold, where the second approver reads the callback evidence rather than the same email the first approver read.
- Hold the first payment after any change of bank details for manual review.
A decision model does not verify a bank account and cannot replace any line of that list. What it can do is cheap and specific: read every inbound message and answer “does this ask to change where money goes?” so that the callback workflow is triggered by the message rather than by whoever happened to open it. That is a two-way typed decision with a probability on it, which is the one thing that makes it routable: high confidence holds the payment, the middle band goes to the payments team, low confidence does nothing.
PAYMENT_CHANGE = {
"type": "choice",
"instructions": (
"This message arrived in an accounts-payable inbox. Decide whether acting "
"on it would change where money is sent. Judge the request, not the tone: "
"a polite, well-formatted message from a known supplier is the usual shape "
"of this request."
),
"criteria": {
"changes-payment": (
"asks for payment to a different account, bank, country or intermediary "
"than the one on file, supplies new banking details, attaches an invoice "
"whose remittance details differ from the supplier's existing ones, or "
"asks for an existing payment to be redirected, held or reissued"
),
"ordinary": (
"anything else: a quote, a statement, a query, a dispute, a reminder "
"about an unpaid invoice that does not change the destination"
),
},
}We have not benchmarked that sheet, and we are not going to quote a number for it. There is no public corpus of real business email compromise that postdates these models; the spam corpora that exist are on our own training board, so a figure from them would be meaningless; and a corpus of BEC emails we wrote ourselves would measure our imagination rather than the model. The sheet above is the shape of the thing, offered as a starting point for someone who has their own mail archive and their own labels, which is the only data that can answer it. That is also the honest version of what a field engagement does: the labels are the customer's, and so is the measurement.
The rest of the BFSI surface
Every row below is the same primitive: a state, a typed question, a calibrated probability, and an explicit band that goes to a human. None of them needs generated text, which is why none of them needs a frontier model, and all of them involve records that a residency rule or a liability rule touches.
| where | the question | answer type | why typed and calibrated | the band |
|---|---|---|---|---|
| AML alert triage | Does this alert show a pattern worth a case file? | score, 0 to 4 | the queue is ranked by expected level, not by a rule's binary output, and the ranking is what cuts the review hours | the middle levels go to an analyst, the top escalates, the bottom auto-closes with a logged probability |
| KYC adverse media | Does this article concern this applicant, and does it describe a reportable matter? | two booleans on one read | two decisions from one pass over the article, each with its own probability, instead of a summary a human still has to read | anything under the threshold goes to the analyst with the article attached |
| Claims first notice of loss | Which handler, how severe, and are there fraud signals? | choice, score and boolean on one read | one read of the report answers all three, and the severity's expected value sorts the queue better than its argmax | a flagged fraud signal never auto-settles |
| Complaints and mis-selling | Is this a regulated complaint, and about what? | boolean plus choice | misclassifying a complaint is itself the reportable event, so the decision needs a confidence you can audit, not a keyword rule | near-threshold cases are reviewed before the clock starts |
| Disputes and chargebacks | Which reason code, and is the documentation sufficient? | choice plus boolean | codes are a fixed set with real definitions, which is exactly what criteria are for | insufficient documentation routes to the agent, never to an automatic refusal |
| Credit file notes and collections | What did this interaction establish, and how likely is contact to succeed? | choice plus score | prioritising the next call is an ordering problem, and an ordering is only as good as the confidence behind it | unbound answers mean the traffic drifted, which is the signal to recalibrate |
| Trade finance documents | Does this document satisfy the stated condition? | boolean per condition | a long document read once answers many conditions, which is where the 73k context and prefix caching pay for themselves | every negative goes to a checker with the clause attached |
| Vendor payment changes | Does acting on this change where money goes? | choice | the sheet above; the model triggers the callback, the callback is the control | high confidence holds the payment, the middle band goes to payments |
What is measured on this page and what is not matters here. The six corpora above include one BFSI-domain task, financial headline sentiment, and nothing else in that table has been benchmarked by us. The rows are a map of where the primitive fits, not a claim about accuracy on any of them. The number that should decide a deployment is the one from the institution's own labels, which is what vegaml-eval exists to produce.
What this run told us to fix, and what the fix measured
Three gaps in everything above are ours rather than the benchmark's, and all three fall out of these numbers rather than out of anybody's opinion. They are closed in vegaml 0.14.0. What follows is what the fixes measured, on the same laptop, because a fix announced is not a fix.
The escalation band had no way to fire
The whole case for putting this inside a bank is that the uncertain middle goes to a human. So it matters a great deal that in the fitted runs above, the abstain flag fired on zero rows out of 860, and could not have fired on any of them.
The reason is a definition. Once you recalibrate on your own labels, which is the step this post spends a third of its length recommending, abstaining means the conformal prediction set came out empty. For that to be possible at all, the conformal threshold has to sit below 1 minus one over the number of options. It did not, on any corpus:
| corpus | options | fitted qhat | needs below | abstain rate | could fire? |
|---|---|---|---|---|---|
| tweet-sentiment | 3 | 0.863 | 0.667 | 0.0% | no |
| tweet-sentiment-shifted | 3 | 0.800 | 0.667 | 0.0% | no |
| finance-sentiment | 3 | 0.728 | 0.667 | 0.0% | no |
| moderation | 3 | 0.802 | 0.667 | 0.0% | no |
| boolq | 2 | 0.548 | 0.500 | 0.0% | no |
| counterfactual | 2 | 0.508 | 0.500 | 0.0% | no |
Read with the table
The checkpoint's own published abstain flag is a different thing and does fire: it is a plain confidence cut, at a risk level we picked. The trouble is that it is ours and not the customer's, it is tied to no precision target, and it disappears the moment the probabilities are refitted. A fitted head is worse again: a head answer carries no conformal set and no abstain flag at all, so a deployment that fits heads has no escalation signal from the model whatsoever.
What ships instead reads confidence and never looks at the prediction set. You can set the line yourself, per question, with no labels involved. Or, with labels, you can ask for the thing you actually want, which is not a threshold but a standard: give it the precision the automatic band has to hold and it returns the widest band that holds it, along with what that costs in review load.
v.set_band({"payment_change": 0.8}) # a line you choose
out["answers"]["payment_change"]["route"] # "auto" or "review"
cal = calibrate(v, examples, questions, target_precision=0.97)
cal.report["payment_change"]["band"]
# {"threshold": 0.83, "review_rate": 0.31, "precision": 0.983,
# "precision_lb": 0.971, "held_out": {"precision": 0.975, ...}}Two details decide whether that number survives contact with a pilot. The target is held on a one-sided 95% lower bound rather than on the precision the band happened to score, because the threshold is chosen on the same rows that then grade it, and picking the best-looking cut on a few hundred rows is exactly how a 97% target ships as a 91% deployment: twelve correct out of twelve certifies 0.816, not 1.0. And a target that nothing can hold is refused rather than approximated. Every answer routes to review, and the report names the target this much data could actually support. The failure mode we were careful to rule out is the one where the band quietly gets weaker than the number written on the contract.
The CPU number was mostly configuration
703.8 ms per decision on CPU against 118.7 ms on Metal is the figure published above, and a bank's on-prem box often has no GPU at all. Most of that gap turned out to be a setting. The right thread count belongs to the reader, not to the engine, and torch's default is wrong for the one we ship:
| torch threads | Qwen3.5-0.8B, the default reader | gemma-3-1b-it |
|---|---|---|
| 1 | 258.2 ms | 167.6 ms |
| 2 | 763.8 ms | 138.9 ms |
| 4 (torch's default here) | 1268.1 ms | 135.2 ms |
| 6 | 1852.0 ms | 142.7 ms |
| 10 | 3612.1 ms | 146.3 ms |
The shipped reader gets 4.9x slower going from one thread to four and 14.0x slower going to ten. It is not the engine and not the prompt length: Qwen3.5's linear-attention blocks fall back to a reference PyTorch implementation whenever the CUDA kernels are missing, which on a CPU they always are, and that fallback does not thread. A plain-transformer reader has no such fallback and behaves the ordinary way. It holds at length too, 547 against 1639 ms at 855 characters, and 1019 against 2853 ms at 3426 characters.
The thread count is free in the only sense that matters: one thread and four agreed on 12 of 12 choices with a confidence difference of 0.0000. The second lever is the reader, and it is free in a different sense: the engine behind every reader is the same 14.3M parameters, checksum-identical, so this is a configuration change rather than a different model. Fitted and calibrated across the six corpora, the gemma reader means 0.672 accuracy against the 0.8B's 0.679, at a calibration error of 0.059 against 0.069. Seven tenths of a point behind on accuracy, a point ahead on calibration, and 135.2 ms instead of 703.8 ms. That is 5.2x, with no quantization and no GPU in the box.
Read with the table
We built the quantized path as well, and it is not the speed answer. Dynamic int8 over every linear layer takes the reader's weights from 3.75 GB to 0.95 GB, which is the difference between fitting on a small box and not. It also ran 4.1x slower here and moved 2 of 12 answers, with confidence shifting 0.102 on average. The latency is a backend fact rather than an int8 fact: this laptop has qnnpack, while float32 here runs through Apple's matrix path at a measured 1,357 GFLOP/s, so there is nothing for int8 to beat. A Linux x86 box has fbgemm instead and the comparison there is different, which is precisely why it ships reporting which backend it chose rather than claiming a speedup we have not measured on your hardware.
Recalibration is one command, and it tells you when to run it
If a customer still needs us in month three we built the wrong thing, which is easy to write and worth checking. So recalibration is one command, and so is the question of whether it is due:
vegaml-calibrate fit --labels recent.jsonl --questions @questions.json \
--target-precision 0.97 --out calibration.json
vegaml-calibrate check --answers last-week.jsonl --calibration calibration.json
# exits 1 when the traffic has moved off the calibrationThe check needs no labels at all, which is the point: labels are what the refit costs, and this is the thing that tells you to go and collect them. It reads answers the deployment has already produced and compares the mix of them against the mix the calibration was fitted on, which the fit records automatically. It exits non-zero at the point of action, so a cron entry and a pager are the entire alerting setup.
What it watches is decided by the drift section above rather than by taste. A covariate shift, the same decisions written differently, left calibration intact and in fact improved it. A change in the mix of answers broke it on every corpus we tried. So the statistic is the population stability index over the answer mix, which is one counter per option and is already what model-risk teams in banking read, at its conventional bands: under 0.1 stable, 0.1 to 0.25 watch, 0.25 and over refit. Beside it sit mean confidence, because a reader that has started hedging on everything moves no mix at all, and review load, because a band whose review rate has doubled is costing a pilot money whatever the statistics say. An option the calibration never produced is not drift in degree but a different question, and always a refit. Under fifty answers for a question it reports the numbers and refuses a verdict, because a mix drawn from a handful of answers moves on its own, and paging on that is how a drift alert gets switched off for good.
The bill for acting on it is the one already in the short version: 26 seconds on 240 rows, almost all of it reading each row once, with the temperature and the conformal quantile together costing 6 ms. Sixteen labelled rows restored calibration after the prior shift and it stopped moving by sixty-four. That is a morning of labelling, not an engagement.
What this does not measure
- One reader. Everything local here is the Qwen3.5-0.8B reader, the smallest we publish, because it is the one that runs on a laptop. The 4B and 27B readers score materially higher on our own board and were not run here.
- Heads, not the fitted product. The curve fits a head on frozen features. It is not the same thing as vega-U's own fitted-and-calibrated path, and the numbers should not be read as that path's ceiling.
- Contamination is not controlled. boolq, amazon_counterfactual and tweet_eval are widely published and predate all three systems, so all three may have seen them. That inflates every zero-shot row roughly equally and cannot explain a degenerate answer distribution, so the findings against us are conservative under it.
- 240 labels is a cap, not a plateau. Several curves are still rising where they stop, because a 400-row corpus cannot supply more while keeping a 160-row test split.
- Latency is one laptop. Treat it as a ratio between rows. The hosted figures carry the network from one machine in India to a US region on one afternoon.
- No BEC benchmark. As set out above, there is no public corpus that would make one meaningful.
Run it yourself
The harness is one file. It caches every stage, so each can be re-run without paying for the ones before it, and the corpora download to the ordinary HuggingFace cache rather than being copied into a repository.
pip install vegaml pandas pyarrow huggingface_hub
export JEV_API_KEY=... # only needed for the hosted rows
python -I scripts/ablation_eval.py \
--n 400 --n-twin 300 \
--curve 8,16,32,64,128,240 --curve-seeds 5 \
--stages features,engine,laya,jev,ablate,driftSwap the task table at the top of the file for your own corpora and the same three measurements run against your data, which is the only version of this that should decide anything.
Sources
- Corpora: cardiffnlp/tweet_eval, mteb/tweet_sentiment_extraction, zeroshot/twitter-financial-news-sentiment, tdavidson/hate_speech_offensive, google/boolq, mteb/amazon_counterfactual.
- Just Ask Jev: Reinforcement Learning for Calibrated Decisions as a Zero-Shot Detector of AI Alignment Failures, for the independent zero-shot and calibration results on the hosted model.
- Red Hat Developers, benchmarking AI decision models against traditional guardrails, an independent guardrail comparison putting Jev 1.13 at 86.35% and laya at 85.44% on prompt injection.
- O'Reilly Radar on Jev, for the spam comparison: 98.3% zero-shot against 98.4% for a TF-IDF model trained on about 14,800 labelled emails.
- FBI IC3 2025 annual report, for the business email compromise totals.
- Reporting of the Kochi FIR: DT Next, Metro Vaartha, The420.
- Google Cloud on RBI payment data localisation, and the IRDAI Maintenance of Information regulations, 2025.
- RBI's FREE-AI committee report, August 2025.
- Our own harness and this run's raw output are in the vegaml ecosystem repositories, and the guard study it follows is here.
If one of these numbers is the one that matters to you, the harness takes your corpus in about ten lines of diff. We would rather you ran it than believed us.