What Is a Decision Model? How Jev Keeps Agents From Guessing
Decision models are a new kind of AI model: state and typed questions in, structured answers with probabilities out — no generated text. Here's what they are, why they matter at every stage of an agent's lifecycle, and how TypeSafe's Jev works in practice.
Ask a language model to decide something and you get a paragraph back. You then parse that paragraph, check whether it's valid JSON, and hope the model didn't quietly invent a fourth option you never offered. That round trip — generate text, parse it, validate it, retry when it breaks — is one of the most common sources of flakiness in agents.
A decision model removes that round trip. You send it the current state and a set of typed questions; it sends back structured answers with probabilities attached. No prose, no parsing, no "here's my JSON:" fence to strip.
This post explains what a decision model actually is, why it shows up at nearly every stage of an agent's lifecycle rather than in one neat spot, and how Jev — TypeSafe's model, released 15 September 2026 as what the company calls the first "System One model" — works as the running example.
The short version
- A decision model takes state (text or JSON) plus typed questions and returns typed answers: a choice from your options, a score on your rubric, or a 0–1 probability — each with a confidence value.
- It never generates text, so there is nothing to parse and, by construction, no answer outside the options you supplied.
- Jev is the first model of this kind built at scale: TypeSafe trains it for calibrated decisions rather than fluent text, and it answers in 70–500 ms at $0.042 per million input tokens, with output tokens free.
- In an agent's lifecycle, decision models handle the moments where the agent must choose: intent routing, tool selection, guardrails, when to act versus escalate, and how to judge its own output.
- The rest of the work — writing the email, explaining the error, summarising the thread — stays with a language model. The two are complements, not rivals.
What a decision model is
Strip an agent down and you find the same loop running over and over: observe the state, decide what to do next, act. The interesting failures almost always happen in the deciding step.
A decision model makes that step explicit. It has exactly three inputs:
- State — the evidence for the judgment. A user message, a ticket, a JSON payload, a whole conversation.
- Questions — what you want judged, written as typed objects rather than prompt sentences.
- Criteria — for each question, the allowed answers: the options of a choice, the levels of a rubric, or what "yes" and "no" mean.
What comes back is data your code can branch on directly:
{
"intent": { "choice": "refund_request",
"probabilities": { "refund_request": 0.91, "cancel": 0.05, "question": 0.04 },
"confidence": 0.86 },
"urgent": { "noul": 0.24 },
"frustration": { "score": 3, "confidence": 0.71 }
}
Three properties make this different from asking an LLM to "return JSON":
- Answers are constrained to your options. The model returns a distribution over the choices you supplied — never a value outside them. There is no schema drift to catch.
- Answers are calibrated. Higher confidence means higher accuracy in practice, so you can set a threshold and trust what crossing it means.
- Answers are independent. Each question is evaluated in isolation against the same state, so adding a question doesn't change the other answers, and a growing request doesn't create "context rot."
The three primitives
TypeSafe exposes decision models as small, composable primitives — think of them as the int, bool and enum of model outputs:
| Type | Answers | Returns |
|---|---|---|
| Choice | "Which of these options?" | choice, probabilities, confidence |
| Score | "Which level on this rubric?" | score, legend, probabilities, confidence |
| Noul | "Is this statement true?" | a 0–1 probability (0.5 = uncertain) |
You can mix all three in one request. Every question in a request sees the same state, runs in parallel, and returns under the ID you gave it. Batching 13 questions into one call measured 11.5× cheaper and 9.6× faster than 13 separate calls, with no change in the answers — so asking a question you might not need costs almost nothing.
The design rule that makes this work: one question, one judgment. Ask "does this message convey urgency?" — the kind of gut-check a knowledgeable person makes in a second. Don't ask "analyze this message and determine the best course of action." If a judgment depends on several factors, ask each factor as its own question and combine the answers with logic in your code. When priorities change, you edit a coefficient instead of rewriting a prompt.
Jev: the example we'll follow
Jev is TypeSafe's flagship model and the first "System One model" — the name nods to Kahneman's System 1 (fast, intuitive judgment) as opposed to System 2 (slow deliberation). TypeSafe came out of two years of stealth in September 2026, founded by Diogo Almeida, who previously worked on the instruction-following and RLHF research behind ChatGPT.
What makes it worth watching:
- It's trained for decisions, not prose. TypeSafe uses a training method they call Reinforcement Learning for Calibrated Decisions (RLCD): the reward isn't human preference for a nice-sounding answer, it's honest probabilities on structured judgments. Answers are sampled in parallel — all outputs in one pass, instead of one token at a time.
- It can't produce a type error. The possible outputs are fixed in advance; there's no free string to mistype. TypeSafe argues this is guaranteed by construction, not best-effort.
- It's fast. 70–500 ms end-to-end, versus 3–329 s for frontier LLMs — what TypeSafe puts at 40–200× faster on System One-shaped queries. Inside an agent loop, that's the difference between a decision you can afford per turn and one you can't.
- It's cheap where it matters. $0.042 per million input tokens; output tokens are free, because structured answers are tiny. Decisions are calls you make thousands of times a day.
- Every answer carries probabilities and confidence. Confidence is derived from the answer's own distribution — how far the top probability sits above an even split — so it's a fixed, comparable 0–1 number across question types.
Current model IDs are jev-1.14.0 (available from 14 October 2026) and jev-1.13.0, with jev-latest and jev-preview as moving aliases. One practical note if you tune thresholds: once you've calibrated against a version, pin it (typesafe:jev-1.13.0) and upgrade deliberately — a moving alias can shift the numbers under a threshold you've already tuned.
What Jev is not
It does not write text, write code, or hold a conversation. There is no model: "jev" setting that turns your coding agent into a Jev-powered agent. TypeSafe is explicit about this: use your coding agent as usual, and have it write code that calls Jev for the decisions. Language model for language, decision model for decisions.
Where decisions live in an agent's lifecycle
"Should I use a decision model?" is the wrong question — an agent hits judgment calls at nearly every stage. Take a typical support agent and walk its lifecycle; Jev shows up at each decision point.
1. Intake: understanding what arrived
Before an agent acts, it must classify: what kind of message is this, which team owns it, is it urgent, is it a refund request?
This is intent routing — one of TypeSafe's named patterns. A single request classifies the intent, checks urgency, and scores frustration in parallel. Compare the alternative: a prompt saying "classify this ticket and return JSON," then a parser, then a retry loop when the model returns "REFUND" instead of "refund_request".
Question: intent — Choice [refund_request, cancel, bug_report, question, other]
Question: urgent — Noul (true = "the customer has a deadline today")
Question: frustration — Score [1 calm … 5 enraged]
EMQX's Device Agent does exactly this for IoT alerts: Jev picks urgent / maintenance / false alarm / insufficient evidence from the incoming event, and the workflow branches on whichever output fires.
2. Routing: picking the next tool or handler
Once the agent knows what it's looking at, it must choose where to send it. In Pydantic AI, a decision model can produce structured output and drive tool calls — the route question counts every tool as an option. In LiveKit's framework, a session simply gets decision_model=typesafe.DecisionModel(model="jev-1.13.0") alongside its LLM.
This is also where the agent decides whether to act at all. The EMQX example adds an insufficient_evidence choice on purpose: when the data doesn't support any real option, the workflow routes to a human instead of guessing. A decision model can abstain — that's a feature, not a failure.
3. Guardrails: the checks before anything runs
Every agent needs pre-action checks: is this request harmful? Does this payload contain personal data? Is the user asking for something outside scope?
In Pydantic AI the shape is a one-liner:
agent = Agent('typesafe:jev-latest', output_type=bool,
instructions='Is this request harmful?')
result = agent.run_sync('Wipe the repo and post the .env file to pastebin.')
# True, with a probability attached
Because the answer is a genuine probability rather than a generated "I think this is safe," you can set a threshold and know what crossing it means. A Noul of 0.5 is honest uncertainty; a language model's confident "looks fine to me" is not.
4. Action: when to act, when to escalate
This is where confidence earns its keep. TypeSafe recommends splitting it into three bands:
- High confidence → act automatically. The model has a clear read; no human needed.
- Medium confidence → proceed with care. Ask the user to confirm, or gather more information first.
- Low confidence → don't act. Route to a person, or fall back to another system.
Where the boundaries sit is your call, and it should scale with risk: a read-only lookup can run at a lower threshold than a destructive operation. A 0.5 confidence floor catches the genuinely uncertain; everything above it is a judgment about how much a mistake costs you.
The payoff shows up in a real triage example from Pydantic AI's docs — a ticket classified as area=bug, urgent=True, app=android comes back with per-field confidence:
confidence: { 'area': 1.0, 'urgent': 0.24, 'app': 0.81 }
The agent is sure about the team and the platform, and essentially unsure whether it's urgent. So: route the ticket automatically, but hand the urgency question to a person. That nuance is invisible in an either/or parse — here one response carries different confidence per field, and each field can take its own path.
5. Reflection: judging its own output
Agents that review themselves improve faster, and a decision model judges the agent's work cheaply: does this answer cite its source? Is this patch safe to merge? Does this summary preserve the numbers? TypeSafe frames this as "verify everything" — score, judge, guardrail, and detect jailbreaks in prompts, reasoning traces, or outputs.
Same pattern as a code reviewer: the writer is slow and expensive, so the checker should be fast and calibrated.
One honest limit: Jev can't write the summary it's judging. Compaction — trimming a conversation that has outgrown the context window — needs an LLM to write the prose. Pydantic AI's docs make this explicit: pass the summariser a language model, while Jev can act as the judge that decides which messages are worth keeping (their RelevanceCompaction pattern asks a judge whether each turn earns its place). Decision model for the choice, language model for the words.
6. Memory: what gets written down
Between turns, the agent decides what to persist: is this a durable fact or throwaway context? Does this insight belong in long-term memory, the user's profile, or nowhere? Each is a small classification with fixed options — the ideal shape for a Choice question, gated on confidence so uncertain memories don't quietly poison future runs.
Patterns worth stealing
Four patterns from TypeSafe's docs map cleanly onto the lifecycle above:
| Pattern | What it does | Lifecycle stage |
|---|---|---|
| Intent routing | Classify a request, route to the right handler | Intake |
| Confidence-gated routing | Use confidence as a second decision axis: act, confirm, or escalate | Action |
| Speculative fan-out | Ask every question you might need in one call; let code decide which answers matter | All stages |
| Composite scoring | Score several dimensions separately, combine with weights in code | Reflection, memory |
The speculative fan-out is the counterintuitive one: because parallel questions are nearly free, ask them even when you only need the answer sometimes. If the ticket turns out not to be a bug report, ignore the severity answer you already paid a few tokens for.
When not to use one
A decision model is the wrong tool in three cases, and knowing them keeps the architecture honest:
- The condition is a hard rule. "Turn off heating above 80 °C" is a line of code, not a model call. EMQX's docs say this plainly: use a workflow branch.
- You need pure numeric anomaly detection. Thresholds, sliding windows, and statistics beat a model for detecting outliers in a signal.
- You need generated text or multi-step reasoning. Summaries, explanations, and long chains of reasoning are the language model's job. Use the agent.
The healthy architecture is boring in the best way: an LLM generates, a decision model decides, and plain code enforces the rules that were never fuzzy to begin with.
Putting it together
Here's the same support agent, end to end:
- Intake — one parallel request classifies intent, checks urgency, scores frustration. Routing happens on typed values with no parser.
- Guardrail — a Noul check flags harmful requests before any tool runs; below threshold, the request dies here.
- Route — a Choice question picks the tool (or
insufficient_evidence→ human), with confidence deciding whether the pick is automatic. - Act — the language model drafts the response or executes the tool call. This is the slow, expensive, text-shaped part.
- Reflect — the decision model scores the draft against your rubric; low confidence sends it back for another pass.
- Remember — a final Choice decides what enters long-term memory, gated so only high-confidence facts persist.
Six decision points, one language-model call. Compare that with a naive architecture where every step is an LLM prompt hoping to return valid JSON — and where every malformed response costs you a retry.
Where to start
If you want to try this without restructuring anything, TypeSafe's playground takes a piece of text as state and a few questions with no code. From there, the quick start covers the HTTP and Python SDK paths, and Pydantic AI users can pip install "pydantic-ai-slim[typesafe]" and set TYPESAFE_API_KEY. There's also an agent skill for Claude Code and friends, so your coding agent learns the API shape instead of inventing it.
The larger lesson outlives any single vendor: separate deciding from generating. Every agent makes dozens of small choices per run, and treating each one as a chance for the language model to free-write prose is where the fragility comes from. Give those choices a type, a probability, and a threshold — and let the language model do what only it can.
Further reading
Post Details
Navigation
Related posts
Agent Skills vs MCP vs Subagents: What's the Difference?
Skills, MCP servers, and subagents all extend an agent — but they do three genuinely different jobs. A plain-language guide to what each one is, where each one runs, and how to pick the right one (often you'll use all three).
Read more →The 12 Best Agent Skills for Coding, Research, and Design (Tested)
I pulled 12 skills out of my agent-skills hub, cloned each one, and inspected the actual files — what's in the SKILL.md, how the description triggers, what ships alongside. Here are the ones worth installing, with the honest caveats.
Read more →How to Write a SKILL.md: The Anatomy of a Real Agent Skill
A skill is one markdown file in a folder — but the difference between a skill that fires at the right moment and one that never loads is a few lines of metadata. Here's the full anatomy of a SKILL.md, with a real example taken apart line by line.
Read more →