Giving the boring decisions to a decision model: two months of Jev in production

jev decision-models ai agents automation typesafe

Most of what my agent does all night is not reasoning. It is triage. Is this email urgent? Does this post belong to my feed? Should someone reply to this? For months these questions went to a full LLM, which answered perfectly and billed me for the privilege of generating prose nobody reads. Two months ago I moved them to a different kind of model entirely — a decision model — and this post is the practical summary of living with it.

What a decision model actually is

The model I use is Jev from TypeSafe AI, and the category it represents is more interesting than the product. A decision model takes a state (the text you want judged) plus a list of typed questions, and returns typed answers with calibrated confidence. Three question types cover most triage work:

  • noul — a yes/no probability between 0 and 1 ("is this urgent?")
  • choice — pick one option from named criteria with per-option probabilities ("which department should handle this?")
  • score — a graded scale with a legend ("calm → frustrated → very angry")

There is no text generation. The model cannot chat, cannot explain itself, cannot call tools. You send state, you get a small JSON structure back. That limitation is the entire value proposition: bounded outputs are cheap, fast, and consistent in a way that asking a large language model "answer only yes or no" will never be.

What I wired it into

Two unattended jobs on my server now depend on it daily.

Email triage. Every morning a cron job checks new mail and, for each message, asks Jev four questions in one call: category (client business / newsletter / personal / finance), needs-reply, urgency, and tone. The thresholds do the routing: urgency above 0.8 gets a 🚨 flag in the morning report, reply-probability above 0.7 gets an ✉️. Five emails triaged in 23 seconds total, and the classifications have been spot-on in two months of daily runs — newsletters land at 1.0 confidence, a synthetic client-outage test mail scored 0.99 urgent.

Social feed filtering. My feed-engagement job likes technical posts on Bluesky and Mastodon. Before Jev, a keyword matcher did the filtering and its false-positive history was embarrassing (a Dolly Parton post once survived it). Now every candidate post goes through one Jev call with four questions: topic (a choice across software development / AI / Linux / blockchain / economics), substance, worth-engaging, and language. The contract encoded as thresholds: like only if the topic matches, substance ≥ 1.5, worth-engaging > 0.6. Every historical false positive I benchmarked against the new filter gets rejected correctly.

The numbers

Measured on my own server, not from vendor slides:

  • Price: $0.042 per million input tokens, output tokens free. A typical triage call is ~400 input tokens — about $0.000017 per decision. The entire month of email triage costs less than one image generation.
  • Latency: 0.62 s round-trip for a warm three-question call. With one cold-routing outlier at 39 s, so anything unattended needs a timeout.
  • Throughput trick: questions inside one call are evaluated together — adding a fourth or fifth question barely moves the latency. One call with five questions beats five calls with one, every time.

The rules that make it work

Act on thresholds, not raw values. The output is a probability, not a verdict. My pattern is three bands: above the action threshold, act; below the ignore threshold, discard; the middle band escalates to the big model or to me. The decision model handles the clear cases, and the expensive inference is reserved for genuine ambiguity.

Write real instructions. The default instruction for a question is just its name spoken aloud, which is fine for "is this urgent" and useless for subtle judgments. For the feed filter, the topic question carries explicit criteria describing what each topic option means. Writing those criteria was the difference between a filter and a coin flip.

Keep the state small. The vendor’s own docs report accuracy "jaggedness" as state grows. Everything I send fits in a few hundred tokens — headers plus body for an email, title plus first paragraph for a post. When a decision needs more context, that is a sign the decision is really an LLM task.

Batch the questions, never split them. One call per email, one call per post. The API prices input tokens, not questions.

Where it fails

The calibration is statistical, not per-answer. Jev can be confidently wrong — a 0.9 confidence on the wrong category happened at least once that I noticed, and it will happen again. Nothing about the output protects you from that; only the threshold design and the occasional spot-check do.

It is also genuinely incapable outside its lane. There is no generation, no explanation, no "tell me why". I tried to use it as a cheap summarizer once, which was a category error of the highest order — it is a switch, not a writer.

And latency is spikier than the median suggests. The median call is sub-second, but a cold route produced a 39-second outlier. Every unattended job that touches it wraps the call in a timeout with a graceful fallback — my email report ships with plain-header triage if the API is down, and the feed filter defaults to "do not like". The automation never blocks on the decision model being available.

The cascade pattern

The place this technology shines is next to a large model, not instead of it. The documented pattern, which now shapes my pipelines: a cheap extraction or a decision model handles the bulk, verifies per field, and the expensive LLM only sees the failures and the ambiguous middle. For a thousand-item workload where ninety percent is obvious, the large model’s bill drops by roughly the obvious fraction.

A note on scope

The newest release added experimental image input, a bounding-box question type, and a 262k context mode — all behind explicit opt-in headers, all self-described as not production-ready. I have not enabled any of it for unattended jobs, and I do not plan to until it graduates. The text-typed core is the stable, useful part.

Takeaways

If your agent spends token money answering questions whose answers are structurally small — yes/no, pick-one, rate-this — that work does not belong to a large language model. A decision model does it an order of magnitude cheaper, fast enough for hot paths, with confidence numbers your code can act on. The failure mode to design for is not wrong answers; it is confident wrong answers. Thresholds, escalation bands, and a fallback when the API is silent will keep the automation honest.

My email report and my feed likes will not be winning any Turing Awards. They will also not appear on a bill.