Ask every question at once
Every habit you built calling LLMs says to ask for one thing at a time and chain the calls. With Jev that habit is the expensive path. The state is ingested once, every question is evaluated against it in parallel, and you pay for the state — not the curiosity.
By the end you'll be able to
- Collapse a decision tree into a single request
- Ask speculative questions whose answers you may discard
- Know the three legitimate reasons to make a second request
On this page
The economics that drive the pattern#
- The state is billed once. Input tokens are the only charged unit, at $0.042 per million. Sending the same ticket to five separate calls bills it five times.
- Questions are evaluated in parallel. TypeSafe's docs say adding questions "barely changes the response time".
- Output tokens are free. The answers themselves cost nothing.
- Questions are independent. One answer is never hidden context for another, so adding or removing a question does not move the others.
Put together: a question you might not need costs you the tokens of the question text and nothing else. TypeSafe's parallel-questions cookbook runs a 13-question regulatory briefing over the GDPR Wikipedia article and reports that batching it into one call rather than thirteen is 11.5× cheaper and 9.6× faster with no change in the answers.
A triage tree in one request#
Take a support ticket. You need a category. If it is a bug report you also need severity and whether there are reproduction steps. If it is billing you need to know whether a refund was asked for. Classically that is two or three round trips. Here it is one.
from typesafe_sdk import Choice, Noul, Score, TypeSafeClient
client = TypeSafeClient()
response = client.system_one(
state=ticket,
questions={
"category": Choice(
instructions="Determine the broad category of this support ticket",
criteria={
"bug_report": "The user is reporting something that is broken or producing errors",
"billing": "Charges, invoices, refunds, subscriptions",
"feature_request": "The user is requesting new functionality",
"account": "Login, permissions, profile, security",
},
),
# Speculative: only meaningful if this turns out to be a bug report.
"bug_severity": Score(
instructions="How severe is the reported issue",
criteria=[
"Cosmetic; no impact to functionality",
"Broken or degraded feature; workaround exists",
"Blocking issue; no workaround exists",
],
),
"has_reproducible_steps": Noul(
instructions="The user describes specific steps to reproduce the issue",
),
# Speculative: only meaningful if this turns out to be billing.
"refund_requested": Noul(
instructions="The user is explicitly asking for a refund or credit",
),
# Useful no matter what the category is.
"frustration": Score(
instructions="How frustrated the user appears",
criteria=["Calm, matter-of-fact", "Frustrated but civil", "Very angry"],
),
},
)Then the branching happens where branching belongs — in code you can read, test and diff.
category = response.answers["category"]
bug_severity = response.answers["bug_severity"]
bug_repro = response.answers["has_reproducible_steps"]
refund = response.answers["refund_requested"]
frustration = response.answers["frustration"]
if category.choice == "bug_report":
if bug_severity.score > 1.5 and bug_repro.noul > 0.6:
escalate_to_engineering(ticket_id, severity="high")
else:
add_to_bug_backlog(ticket_id)
elif category.choice == "billing":
if refund.noul > 0.7:
route_to_billing_with_flag(ticket_id, refund_likely=True)
else:
route_to_billing(ticket_id)
elif category.choice == "feature_request":
log_feature_request(ticket_id)
# Frustration is useful regardless of category.
if frustration.score > 1.5:
flag_for_priority_response(ticket_id)Speculative questions are the point#
bug_severity and has_reproducible_steps are meaningless for a feature request. Ask them anyway. If the category comes back feature_request, your code ignores them and you have saved a round trip in the cases where it did not.
The rule of thumb: if a question could have been asked against the state you already sent, ask it in that request. Two requests should be the exception.
When a second request is genuinely necessary#
Questions in a request cannot see each other's answers. A real dependency exists only when your code cannot build the second request until the first has answered. TypeSafe names three cookbooks that qualify, and they are a good test of whether yours does:
| Cookbook | Why a second call is unavoidable |
|---|---|
| Skill suggestion | Ranks 182 skills in one request, then fetches the full text of the top three and judges them again against that better evidence. The second request's state did not exist yet. |
| Structure recovery | Asks whether each line break split a sentence, merges lines into blocks from those answers, then classifies the blocks. The blocks did not exist yet. |
| Hierarchical classification | Each Choice answer decides which options the next request offers. The second request's criteria did not exist yet. |
Notice the shape they share: the answer changes what you can ask, not merely what you do with it. If the second batch of questions could have been asked against the original state, it should have been.
Counting, and other things that look like one question#
Fan-out is also the workaround for one of Jev's documented weaknesses. It cannot count reliably — TypeSafe says the model "recognizes the shape of an answer rather than tallying", and error grows with the size of the thing counted. So do not ask for a count. Ask one Noul per item and add them up yourself.
from typesafe_sdk import Noul, TypeSafeClient
client = TypeSafeClient(model="jev-1.13")
YES = 0.5 # up to you on what you want the threshold to be, depends on your usecase.
items = ["typesafe", "apple", "california", "banana", "likes", "calibration", "orange", "vertex"]
result = client.system_one(
{"items": items},
{
f"item_{i}": Noul(instructions=f"Is `items[{i}]` the name of a fruit?")
for i in range(len(items))
},
)
count = sum(result.nouls[f"item_{i}"].noul > YES for i in range(len(items)))This is the whole philosophy in eight lines: the model makes one narrow semantic judgment per item, and the arithmetic — the part a computer has always been good at — stays in Python.
One caution worth carrying#
Next#
You now get a dozen answers per call. The next question is which of them you are allowed to act on without a human. Continue to Confidence-gated routing.
Sources for this page
- TypeSafe — Speculative fan-out
- TypeSafe — Primitives
- TypeSafe — Parallel questions cookbook
- TypeSafe — jev-1.13 jaggedness
- Anthony Maio — Jev: The Language Model That Won't Talk
Last reviewed 2026-09-18. Jev is days old and moving — where a claim is TypeSafe's own rather than independently verified, this page says so in the sentence that carries it.