System One Models: A New Model Class
You already know how to get structured data out of an LLM. You write a prompt, you attach a JSON schema, you set temperature=0, you parse the result, you validate it with Pydantic or Zod, and you write a retry loop for the times it comes back wrong anyway. It works. Most production AI code in 2026 looks like this.
This level is about a different idea: a model class where that entire pipeline — prompt, generate, parse, validate, retry — collapses into a single call that returns a typed value with a probability attached. Not because someone wrapped an LLM in better tooling, but because the model never produces a string in the first place.
These are called System One models. Jev, from TypeSafe AI, is the one we'll build on through this course. Today we're only doing one thing: making sure you can define the category precisely and recognise the workloads that belong to it.
Start with the code you've already written
Here's a ticket router, the way you'd build it today:
# The familiar shape: ask for prose, hope it's valid prose.
resp = llm.chat.completions.create(
model="some-frontier-model",
messages=[{"role": "user", "content": f"""
Route this ticket. Reply with JSON only:
{{"department": "billing"|"technical"|"sales", "urgent": true|false}}
Ticket: {ticket_text}
"""}],
response_format={"type": "json_object"},
temperature=0,
)
raw = resp.choices[0].message.content # a STRING. Always a string.
data = json.loads(raw) # may raise
routing = Routing(**data) # may raise: "Billing", "billing-team", null
# ...and now the retry loop, the fallback department, the alerting
Think about what's actually happening in the machine. The model emitted {, then ", then dep, then artment... one token at a time, each conditioned on the last. The word billing is not a decision the model handed you — it's a sequence of tokens that spells a decision, which your code then has to turn back into a value. You asked a writer for an answer and got an essay containing the answer.
That round trip — value → prose → value — is where the bugs live. Schema-constrained decoding and JSON mode narrow the failure surface, but the architecture underneath is unchanged: a text generator, steered.
The definition: three invariants
A System One model takes unstructured state plus a schema you fix in advance, and returns typed values with calibrated confidence. No prose, ever.
Three properties define the class. If a system misses any one of them, it isn't a System One model:
1. Pre-declared answer space. You declare every possible answer before the call. Not as a hint in a prompt — as the actual space the model is allowed to land in. If the options are billing, technical and sales, there is no path through the model that produces escalations or Billing Dept. or an empty string.
2. Typed output. What comes back is a value of the type you asked for: an enum member, a number on a scale you defined, a probability. There is nothing to parse, so there is no parse error. TypeSafe's phrasing for Jev is that the model "never makes type errors" — structural validity is a property of the architecture, not of a validation layer you bolted on.
3. Attached probability. Every answer arrives with a calibrated probability distribution over the declared options, plus a confidence value. The model tells you not just what it chose but how sure it is, in a number your code can branch on.
A useful mnemonic: fixed space, real type, honest number.
The same router, decision-shaped
Here's the Jev-flavoured version. The SDK is pip install typesafe-sdk (Python 3.10+) or npm install @typesafe-ai/sdk (Node 20+); both read TYPESAFE_API_KEY from the environment.
from typesafe import TypeSafe, Choice
client = TypeSafe() # reads TYPESAFE_API_KEY
response = client.system_one(
# 1. STATE: the unstructured context. Text, JSON, arrays — program state,
# not a carefully engineered prompt.
state=ticket_text,
# 2. QUESTIONS: the pre-declared answer space. This is the schema, and it
# is the whole of what the model may return.
questions={
"department": Choice(
instructions="Which team owns this ticket?",
criteria={
"billing": "Invoices, refunds, payment failures, plan changes",
"technical": "Bugs, outages, integration or API problems",
"sales": "Pricing enquiries, upgrades, new contracts",
"other": "Anything that fits none of the above",
},
),
},
)
answer = response.answers["department"]
answer.choice # 'billing' <- an enum member, not a string to clean up
answer.confidence # 0.93 <- how sure the model is
answer.probabilities # the full distribution over the four options
Notice what is missing: no json.loads, no Pydantic model, no retry loop, no "the model returned Billing with a capital B again" incident. answer.choice cannot be a value you didn't declare, because the four keys of criteria are the answer space.
Notice also the other option. Because the space is closed, anything genuinely outside it has to land somewhere — so you give it a home on purpose. That's a habit worth forming from day one.
Gotcha — a closed answer space is not a correctness guarantee. Jev cannot invent a department, and it cannot return a type error. It can absolutely pick the wrong department from the three you declared. "Cannot hallucinate" means "cannot fabricate outside the declared space" — it does not mean "is always right". Anyone who tells you otherwise is selling something.
Jev exposes a small family of these typed questions — a Choice over up to 255 options, a Score over an ordered scale of 2–10 levels, and a yes/no Noul that returns a bare 0–1 probability. We'll take the full anatomy of a response apart in a later level; for now the shape above is enough.
What "calibrated" actually buys you
LLMs expose token log-probabilities, and engineers have been trying to use them as confidence signals for years. It mostly doesn't work: a log-prob tells you how likely that token was, which is a statement about text, not about the world. A model can be fluently, confidently wrong.
Calibration is a stronger and much more specific claim: when the model says 0.9, it should be right about 90% of the time. TypeSafe trains for this directly with a method they call Reinforcement Learning for Calibrated Decisions (RLCD), optimising for honest probabilities — higher confidence really meaning higher accuracy, and similar inputs producing similar answers.
That turns confidence into a control-flow primitive:
answer = response.answers["department"]
if answer.confidence >= 0.85:
route_to(answer.choice) # act automatically
elif answer.confidence >= 0.60:
route_to(answer.choice, flag="low-confidence") # act, but mark it
else:
route_to_human_queue(ticket) # don't guess
The threshold isn't a constant you copy from a blog post — it's a product decision, scaled to blast radius. A read-only "which tab should we open first" decision can run wide open. A "move money" decision should demand a high bar and fall back to explicit confirmation. Confidence gating is the single most useful pattern in this whole model class, and it only exists because the probability is trustworthy enough to branch on.
A model that decides vs a model that writes
Here is the cleanest way to hold the distinction:
| A model that writes (LLM) | A model that decides (System One) | |
|---|---|---|
| Output | A string, generated token by token | A typed value, produced in a single parallel pass |
| Answer space | Open — anything expressible in language | Closed — declared by you before the call |
| Consumer | A human, or code that parses first | Code, directly |
| Uncertainty | Token log-probs, weakly meaningful | Calibrated probability per option |
| Failure mode | Invalid JSON, invented values, drift | Wrong choice inside the declared set |
The last row is the important one. Both model classes are fallible, but a System One model's failures are of a kind your type system already understands: the answer is always well-formed and always in-range, so the only question left is whether it's correct — which is a question you can measure, threshold and monitor. Generation-shaped failures are open-ended and therefore need open-ended defences.
This is also why software consumes decisions directly. answer.choice is already a value. You don't have to defend against the model having written something creative.
Exercise: triage the workloads
Run down this list and ask one question of each: does a human read the output, or does an if statement?
| Workload | Shape | Why |
|---|---|---|
| Route a support ticket to a queue | Decision | Finite, known set of queues |
| Draft the reply the customer receives | Generation | A human reads every word |
| Score how frustrated a reviewer sounds, 1–5 | Decision | Ordered scale, fixed in advance |
| Summarise a 40-page contract for a partner | Generation | The output is prose |
| Flag whether a message breaches a content policy | Decision | Yes/no with a probability, feeds a gate |
| Decide if retrieved chunks are relevant enough to answer | Decision | A gate inside a pipeline, not a deliverable |
| Write the migration to add a column | Generation | Free-form code |
| Pick which of six tools an agent should call next | Decision | Closed set, chosen every turn |
The pattern that falls out: decision-shaped work is high-volume, repeated, low-variety judgement over state, where the answers are knowable up front. Generation-shaped work produces something new that a person will read.
Most real systems need both, and the natural architecture is a sandwich. A System One model decides whether to act, which branch to take, and how sure it is; an LLM then writes the customer-facing text on the branch you chose. The decision layer is where correctness and latency live; the generation layer is where language lives.
Best practice — look for the decisions hiding inside your prompts. In any mature LLM pipeline there are usually three or four sub-questions being smuggled into one big prompt: is this relevant? is this urgent? which tool? is this safe to auto-send? Each of those is decision-shaped and is currently costing you a generation round trip. Pulling them out is usually the first thing you do when you adopt a System One model.
Trade-off — you have to know the answers in advance. That's the price of the closed answer space. If you genuinely cannot enumerate the outcomes, this model class doesn't fit, and forcing it with a 255-option enum is a design smell rather than a win.
Not a one-vendor category
Jev, released by TypeSafe AI in San Francisco on 15 September 2026, was the first model shipped under this banner, and it's the one we'll use hands-on. But the category already has other entrants — Together AI's Tev1, the open-source Kev family, Laya from Convai Innovations, and small self-hostable decision models like Fastino Labs' GLiNER2.5-Decide. Treat "System One model" as a model class you're learning, in the way you learned "embedding model", not as one company's product name. We'll look properly at what TypeSafe actually shipped, and how Jev works under the hood, in the levels ahead.
Key takeaways
- A System One model takes unstructured state plus a schema fixed in advance and returns typed values with calibrated confidence — never prose.
- Three invariants define the class: pre-declared answer space, typed output, attached probability. Miss one and it isn't a System One model.
- LLMs give you a string that spells an answer; System One models give you the answer as a value, so the parse-validate-retry layer disappears.
- Calibrated means 0.9 confidence ≈ 90% accuracy — strong enough to branch on, unlike token log-probs.
- A closed answer space prevents invented and malformed outputs; it does not prevent the model choosing wrongly from within the set.
- Triage test: does a human read the output, or does an
ifstatement? Decision-shaped work is repeated judgement over state with knowable answers. - Most systems want both: a decision layer that gates and routes, and a generation layer that writes what people read.
- Jev (TypeSafe AI, 15 September 2026) was first, but the category now includes several commercial and open-source models.