← Writing 19 September 2026
Machine learning · Reproduction

The Technique Behind Jev

TypeSafe shipped a model that answers questions about text without generating any text. The first half of this is what Jev is and what its makers do and do not tell you. The second half is a small one I built to find out whether the parts that sound clever actually earn their place.

Contents

What Jev does

TypeSafe AI released Jev last week. You give it a document and a list of questions whose possible answers are fixed in advance. It returns one answer per question, each with a probability.

It never writes a sentence. There is no token-by-token generation, so a call takes one pass through the model instead of dozens. That is why they can charge for input and give output away free. There is no output to charge for.

Three stages. You send a document plus questions whose answer options you declare. The model sees one flat sequence of the document words followed by the option words. You get back typed values with probabilities.
The options are part of the input, not baked into the model. That is what lets you change them on every call. Every probability set sums to 1 over the list you supplied, so there is nothing to parse.

No paper came with it. No parameter count, no training details, and no calibration numbers, which is odd, because calibrated probabilities are the whole pitch.

Three kinds of question

You do not describe what you want in a sentence and hope. You declare questions, and every question is one of exactly three shapes.

TypeYou give itYou get back
yes / noa questiona single probability
choiceup to 255 optionsthe pick, plus how the probability is spread over the rest
scorean ordered scale, 2 to 10 stepsa weighted average across the scale

The yes/no type is the one that gives the design away. It returns a probability rather than a boolean, so the threshold lives in your code rather than in the model. You decide what counts as confident enough.

The score type is quietly clever too. It is not a number the model writes down. It is the average across an ordered scale, weighted by how likely each step is, which is why it moves smoothly instead of clustering on round numbers the way a model asked to "rate this 1 to 10" tends to.

What actually changed inside the model

The body of the model is an ordinary transformer: embeddings, then a stack of layers. Nothing there is new.

The change is the last layer. A language model ends with a layer that scores every word in the vocabulary, picks one, appends it, and runs the whole model again for the next word. That final layer is enormous: for Qwen3-0.6B it scores against a 1,024 × 151,936 matrix, about 156 million numbers. In this model that matrix does double duty as the input embedding table, so it stays. The work it does at the output does not.

Stop doing that step and put a layer that produces one score per option in its place. That layer is 1,025 numbers, and it replaces a matrix multiply against the entire vocabulary at every position. The generation loop goes with it, because there is no next word to guess.

Two model stacks side by side. Both share word embeddings and four transformer layers. The language model's last layer scores all 151,936 vocabulary words and feeds a generation loop, both marked removed. This model's last layer produces one score per option, with no loop.
Everything below the arrow is identical. The removed step is drawn faded because it is the only structural change, along with the loop that depended on it.

This is also where the type guarantee comes from, and it isn’t learned. The scores are turned into probabilities that sum to 1 across the options you listed. There is no leftover probability to assign to anything else, so an answer outside your list isn’t unlikely. There is nowhere for it to come from.

What they claim

70 to 500 milliseconds a call, four cents per million input tokens, nothing for output. Against frontier models on four workflows: security triage, agent-trace review, invoices and customer service.

ModelAccuracyCost per decisionLatency
Jev67.8%$0.00040.4 s
GPT-5.6 Terra67.9%$0.030410.1 s
GPT-5.6 Sol74.1%$0.083623.3 s
Claude Opus 573.1%$0.176137.8 s

That is a price argument, not a capability one. It matches the cheaper frontier model at about a seventy-sixth of the cost per decision, and gives up six points to the bigger ones.

Two things to hold onto. The "right answers" in that table were taken to be the average of what two other frontier models predicted, so accuracy there measures agreement with an ensemble rather than with reality. And there is no paper: no parameter count, no word on the training data, and, for a product sold on the honesty of its probabilities, no calibration numbers at all.

Where it is weak

TypeSafe are refreshingly direct about this, and the list is what you would expect of a single pass with no room to work.

  • Arithmetic and counting. No intermediate steps, so nowhere to do the arithmetic.
  • Dates. Treated as ordinary text. Work out "older than thirty days" yourself and hand over the answer.
  • Chains of reasoning. Accuracy falls away as the hops between document and answer pile up.
  • Irrelevant detail. A long, noisy document costs accuracy, and you cannot instruct your way around it.
  • Hostile input. Not hardened against it, which matters given that jailbreak screening is a pitched use case.

The toy I built

That is everything I could learn about Jev from the outside. The rest of this is what happened when I built a small version to check whether the parts that sounded clever actually do anything.

206,081 parameters. Four transformer layers. It trains on a laptop CPU in forty seconds. All of it is here: about 600 lines, no framework beyond PyTorch.

Nothing here is pretrained. The weights start random and only ever see the made-up data below. Where I quote Qwen3-0.6B figures, that is a size comparison to show what the same change costs on a real model. I never trained it, and none of the results come from it.

The training data is made up, and that is the point. I generate each document from a recipe I choose, so I can compute the correct answer’s exact probability with Bayes’ rule.

That gives me something real data never does: a ceiling. On this task the best possible score is 0.687. When my model scores 0.687 it isn’t mediocre, it’s perfect, and the other 31% is the task being genuinely undecidable.

One limitation, stated up front

My documents are bags of words, so shuffling them does not change the right answer. Real text is not like that, and anything below should be read as a result about this task rather than about language.

How it was trained

Four steps, and the second one is the unusual part.

Four-stage pipeline: generate a document from a controlled recipe; compute the exact answer probabilities with Bayes' rule and use that distribution as the training target; score the options and compare against the truth with a cross-entropy plus Brier loss; test on 5,000 held-out documents against a known ceiling.
The target is a probability, not a label. Most training tells a model which answer was right. This tells it how likely each answer was, which is the only way it can learn to be honest about uncertainty.

The whole run is twenty thousand documents, ten epochs, forty seconds on a laptop CPU. Testing is five thousand held-out documents, and because the recipe is known I can also compute the best score anyone could possibly get, which makes “is this good?” a question with a number rather than a feeling.

What I found

Two things. One that held up, and one that did not.

Why honest probabilities need honest targets

Two identical models. One trained on the right answer. One trained on the right answer’s probability.

Line chart of confidence error across five difficulty settings. The model trained on probabilities is below the one trained on labels at every point.
Five separately trained pairs. Training on probabilities wins everywhere, and wins by more as the task gets harder.

Training on a plain label throws away the fact that the label was uncertain. When two annotators disagree and you record only the majority vote, you have deleted the most useful thing you had.

One honest caveat: plain labels aren’t broken in principle. With unlimited data both models end up in the same place. The problem shows up with limited data, where a model with room to spare drives its own confidence to 100% and stays there.

New answer lists: right answer, wrong confidence

I trained the model on a four-way question only, then asked it two-way and three-way questions it had never seen.

Question typePicks the right answerConfidence off by
4 options, trained on this0.6820.091
3 options, never seen0.6810.340
2 options, never seen0.7760.276
2 options, worst case0.7270.451

The label survives. The probability does not, and the probability is the reason you’d build this instead of an ordinary classifier.

Should you build one?

Before answering that I ran the obvious control, because the post kept telling you to use a plain classifier instead without ever measuring one. Same backbone, same loss, same seed. The only changes are that the options leave the input and the head becomes a fixed four-way one.

 Flexible optionsPlain classifier
accuracy0.68680.6868
confidence error0.03890.0380
distance from truth0.09080.0941
per call0.677 ms0.656 ms
answer list can changeyesno

They are the same model to within noise. I had expected the plain one to be meaningfully quicker, since its input is 14 tokens rather than 18, but at this size the fixed overhead of a forward pass swamps four tokens and the gap is 3%.

So the flexible design buys precisely one thing, and it is the thing measured at 36% order sensitivity with confidence that does not survive an unfamiliar answer list. That is the whole decision:

Is your list of possible answers fixed? yes → use an ordinary classifier. Smaller, faster, simpler. no → keep reading. Do many questions share one long document? yes → the reuse trick is worth real money. Do the answer options change between calls? yes → test calibration on the option sets you will actually send, not just the ones you trained on.

What this doesn’t show

This is a 206,000-parameter model on invented words. It does not tell you how Jev is actually built, because TypeSafe has confirmed nothing. What I measured is how this shape of model behaves on a task I made up, and a bigger one on real text may well behave differently.

What it does establish is narrower and still useful. Training on probabilities rather than labels reaches the best score the task allows and stays honest about its confidence, across every difficulty I tried. Answer lists that change between calls give you the right answer, and a confidence that does not survive an answer list the model has never seen.

Finding that out cost twenty cents.

The obvious next step is the one I cannot run yet. All of this is a tiny model on invented words, and the interesting version is a real one on real text: a few hundred million parameters, actual support tickets and moderation data, and the same four questions asked again. That needs a GPU, and my cloud quota request for one is still sitting in a queue. When it clears I will write up what changes.

The model, the four ablations and every number in this post are at github.com/sahasrarjn/system-one. python -m tiny.run reproduces the calibration result; python -m tiny.ablate runs all four. Both are CPU-only.