cd /news/ai-tools/ask-twice-jev-s-choice-splits-its-ow… · home topics ai-tools article
[ARTICLE · art-135072] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=· neutral

Ask twice: Jev's Choice splits its own vote

A developer built a two-stage emotion-naming system on TypeSafe's System One model, in which a first Choice selects an emotion family and a second selects the specific shade within that family, scoring 22 of 24 on a hand-labeled test set. The same developer found that a single 60-word Choice collapsed confidence because near-synonyms split probability, and that lexicon-based approaches using Warriner, Kuperman & Brysbaert valence/arousal/dominance norms scored only 3 of 24. The work concludes that deciding which parts of a task belong to code versus the model is a design decision with a measurable cost, and that a Choice's confidence reflects distribution concentration rather than correctness.

by read5 min views1 publishedSep 20, 2026

Jev is a System One model from TypeSafe. It reads natural language like any LLM,

and instead of writing a reply it returns a probability distribution over options

you define. No prose, no reasoning trace, no JSON to repair.

I spent a day building waif, which reads a piece of

text and names the feeling in it. That job has no right answer, which makes it an

unusually honest test rig: nothing can be graded against a key, so every design

decision has to be either argued or measured. I measured five.

The obvious build is one Choice over sixty emotion words. It does not work, and

the reason is structural rather than a prompting problem you can fix with better

wording.

Annoyed, irritated and frustrated are one feeling in three wordings. A

Choice divides probability between them, so a text the model read perfectly

clearly comes back at 0.19 / 0.17 / 0.15, and confidence collapses for a reason

that has nothing to do with the input. The distribution is telling you about your

option list, not about the text.

The rule that falls out of it: a Choice's options have to be alternatives to each other, not the same thing described from different angles. Anything a

Families do not have that problem. Anger, fear, sadness and shame are genuinely

different answers. And once the family is fixed, so are its shades — which shade of anger is a fair question, because the context has already ruled out the

So the naming became two Choices in sequence: the first picks the family, the

second picks the shade from that family alone. TypeSafe's docs say a second

request is warranted when an earlier answer determines the next question's

options. This is exactly that, and here is what it bought — scored on 24 texts

against a list of acceptable answers for each:

Design Score
Nearest word in the whole vocabulary, by published valence / arousal / dominance 3 / 24
Nearest word within a family, by rank on the axis that separates that family 9 / 24
Nearest word within a family, by distance in those published ratings 12 / 24
Family chosen by the model, then the shade chosen by the model 22 / 24

Both of the two failures were the wrong family. Given the family, the shade was

right every single time.

The alternative was speculative fan-out: ask all eleven within-family Choices in

the first request and consume only the relevant answer. That keeps a reading to

one request, at roughly 3–4k input tokens against 1,250 + 444 for two. Two

requests won on cost, and on a latency story I could explain.

The first three rows of that table are me trying to do the naming in code.

I had a good reason. Published human ratings exist for exactly the dimensions I

was measuring — Warriner, Kuperman & Brysbaert's norms give valence, arousal and

dominance for 13,915 English words, rated by people. Placing the text on those

axes and taking the nearest word looks like the principled version:

research-backed coordinates instead of somebody's guesses.

It scored 3/24, and the three reasons are worth knowing before anyone else

reaches for an emotion lexicon:

I also tried fixing the scale mismatch by z-scoring both sides against a probe

corpus. Worse, and instructively so: the corpus was negative-skewed, so the

mapping inherited the skew, and a plainly warm text landed below the mean and got

named from the sad half of the space.

The lesson is not don't use lexicons. It is that the boundary between what code owns and what the model owns is a design decision with a number attached,

A Score's confidence summarises how concentrated the distribution is. It is not

how sure the model is of being right, which is the trap everyone hits first.

Here is the second-order version, which cost me a bug. One reading came back at

confidence 0.72 on an axis — comfortably above any threshold I would set —

with the mass sitting 50% on one level and 49% on the one next to it. The

distribution genuinely is peaked: nearly all the weight is in two adjacent

buckets. It is also a coin toss, and my page reported the winner in exactly the

voice it uses at 0.98.

Confidence cannot catch that, because concentration across two neighbours is

still concentration. The margin between first and second is a separate test

and it is the one a reader actually cares about.

A question set grows by accretion. Each addition looks free, and none of them

announce that they have stopped saying anything.

So run them over a corpus and look at the spread. Mine had a Noul asking is more than one feeling present. It returned ≥0.6 on

Two more went for a different reason. They were informative, but nothing

downstream consumed them beyond appending a line to the output. A question whose

entire effect is an occasional footnote costs a reader more attention than it

returns.

The check is cheap: for every question, the min, max and spread of its answer

across a representative corpus. Anything that barely moves is either a gate or a

mistake.

A rubric level must not contain a word from another axis. My control rubric

had a level reading "Overwhelmed: struggling to keep any grip on it". That

primes the model with a feeling while asking it about agency, and it labels the

output with a word that is not a position on a control scale at all. Every level

of an axis has to be a point on that axis.

Input is dominated by your rubrics, not by your input. Criteria are sent on

every call, so a one-line text costs almost exactly what a paragraph does. At

this size requests are the scarce resource and tokens are not — which is the

whole argument for batching every independent question into one call, and the

reason a dependent second call is a real cost rather than a rounding error.

waif is live at dave8172-website.vercel.app/waif. Every number in this post is on the page, under

How it works, next to the vocabulary it was scored against.

── more in #ai-tools 4 stories · sorted by recency
── more on @typesafe 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/ask-twice-jev-s-choi…] indexed:0 read:5min 2026-09-20 ·