Jev Prompt Engineering — Questions, Criteria & State

How to write Jev questions and criteria that hold up: one judgment per question, situation-based criteria, clean state, parallelism, and the model's known failures.

September 20, 2026
jevtypesafeprompt-engineeringcriterianoulchoicescore

Prompt Engineering for Jev

Jev has no system prompt to tune and no persona to design. You are not writing a prompt; you are defining a judgment. The state is the evidence, the questions are the judgments, and the code around the call owns every threshold and every side effect.

This is the highest-leverage page in the section. The difference between a Jev integration that works and one that quietly misfires is almost entirely in how the questions and criteria are written.

The Shape of a Request

Every call has the same anatomy:

  • State — a string, object, or array. Prefer an object with named fields over one blob. This is the evidence the model judges.
  • Questions — an object keyed by your question ID. Each one has a type (Noul, Choice, or Score), instructions, and optional criteria.
  • Answers — every question returns independently, in parallel, against the same state.

Question IDs are invisible:

The model never sees the key you use for a question. Naming a field safe_to_publish adds no instruction. The full requirement must live in instructions and criteria — write them as if the key did not exist, because to the model it doesn't.

Rule 1 — One Judgment Per Question

"Analyze this message and decide the best course of action" hides several judgments behind one answer. Jev cannot reason across steps inside a single question, so that prompt degrades into a guess.

Split it into atomic questions and combine the answers in code:

const { answers } = await client.systemOne({
  state,
  questions: {
    is_refund_request: noul('Does `ticket_message` request a refund?'),
    urgency: score('How time-sensitive is this request?', [
      'No deadline mentioned',
      'Wants a response within a day',
      'Blocked and escalating now',
    ]),
    department: choice('Which team owns this?', {
      billing: 'Charges, invoices, refunds, subscriptions',
      technical: 'Bugs, outages, integration problems',
      other: 'None of the above',
    }),
  },
})

const escalate = answers.urgency.score >= 2 && answers.is_refund_request.noul > 0.5

Atomic does not mean trivial. A bounded action selection or a contextual interpretation is a valid single judgment. The test is whether a careful human could answer it in seconds from the state alone.

Rule 2 — It Reads Literally

Jev takes the instructions as written. Scoping words, negations, and implied conditions are not inferred. If a question is misbehaving, the fix is almost always to write the exact condition rather than to add encouragement.

If a condition needs two literal checks, split it into two Nouls. If a term is ambiguous, define it in the criteria. The classic failure is a requirement that lives only in your head — when you correct a wrong answer by explaining what you meant, that explanation is the missing half of the instruction.

Rule 3 — Criteria Describe Situations, Not Degrees

For a Choice, say what belongs in an option and what belongs in its neighbor, so the boundary is unambiguous:

choice('Which team should handle this?', {
  billing: 'Charges, invoices, refunds, subscriptions',
  technical: 'Bugs, outages, integration problems',
  other: 'None of the above',
})

For a Score, each level must be a concrete state of the world, never a vague intensifier:

  • Good: "Broken feature, a workaround exists"
  • Bad: "Moderately severe"

Levels are evaluated independently, so "worse than the previous level" conveys nothing — the model does not see the other levels.

Per-level examples measurably help

Attach an example object to each level instead of a bare string. In the vendor documentation, a Safari export bug scored through plain string levels returned 1.30 at 0.54 confidence; the same levels written as { what, examples } objects returned 1.07 at 0.90 confidence. Unrelated examples changed almost nothing — examples help when they look like your real inputs.

score('How severe is the reported issue?', [
  {
    what: 'Cosmetic problem, no functional impact',
    examples: ['Typo in a label', 'Misaligned icon'],
  },
  {
    what: 'Degraded, a workaround exists',
    examples: ['Export fails for Safari only', 'Search returns partial results'],
  },
  {
    what: 'Blocking, the task cannot be completed',
    examples: ['Login is down', 'Checkout throws an error'],
  },
])

Rule 4 — Give an Escape Hatch

Any Choice list that might not cover every input should include an other or none_of_the_above option. When you extract a possibly-missing value, add not stated. Without an escape option, the model is forced to pick the least-wrong option, and a forced wrong answer looks identical to a confident right one.

Rule 5 — Keep instructions and criteria consistent

Contradictions between the instruction and the criteria degrade answers. A Noul whose criteria imply that "true" means "no" is a real bug, not a style issue. Read both as a single specification.

Rule 6 — Numbers, Dates, and Counting Stay in Code

Do not ask Jev to do arithmetic, count items, compare dates, or convert units. Pass results or named buckets in the state instead, and let code do the comparison.

  • Counting: one Noul per item ("is items[i] a fruit?") plus a sum in code.
  • Dates: extract with a Choice over months, days, or years (plus not stated), build the real date in code, and compare there.
  • Composite scoring: several independent Scores in one call, then a weighted sum in code. If the ranking is wrong, change a coefficient and rerun — don't rewrite the prompt.

State Discipline

How you assemble the state is half the result. A useful structure has three parts:

  1. The object being judged — the ticket, lead, document, commit, or output.
  2. The context needed to interpret it — the product, customer, policy, or goal.
  3. The facts that materially change the decision — the constraints, deadlines, and exceptions.

"Keep facts in, guesses out" is the rule of thumb, and relevant state beats maximum state. Accuracy drops as irrelevant material accumulates, so filter in code before the call rather than sending everything and hoping. Reference nested fields by backticked path so the model can point at exactly what it judged:

{
  "ticket_message": "...",
  "order": { "id": "A-104", "charges": [49, 49] },
  "refund_policy": "Duplicate charges are eligible for a full refund."
}

In the instructions, refer to those fields directly: Does `refund_policy` support the situation described in `ticket_message`, given `order.charges`?

If you have a long document and only some of it matters, prefilter with a relevance Noul per chunk before asking the real questions.

Parallelism and Speculative Fan-Out

Every question in a request is evaluated in parallel against the same state. Adding questions costs only their input tokens and barely changes latency — in the vendor's cookbook, a 13-question batch against a 53k-character document came out 12.2× cheaper and 10× faster than the same questions sent sequentially, largely because the long state is sent once.

So ask everything you might need, including speculative questions that only matter on some branches, and consume the applicable answers in code. Because questions cannot read one another's answers, a second request is only warranted when:

  • the next question needs evidence that must be fetched first, or
  • the first answer determines the next question's options.

That is the exception, not the default. Reaching for a second call usually means a question was not atomic enough.

Where Jev Breaks

TypeSafe publishes a per-version "jaggedness" list, which is worth taking at face value. The reliable failure modes:

FailureWhat happensFix
Math and countingCharacter counts, term occurrences, list lengths are unreliableDo the arithmetic in code; pass results or named buckets
Dates and timesOrdering, intervals, and windows fail, especially with mixed formatsExtract components, build and compare dates in code
Literal readingImplied scoping and double negatives are read as writtenState the exact condition or split it
IndirectionProperties of properties and multi-hop references cost accuracyPoint directly at the relevant state field
Irrelevant stateExtra material lowers accuracyFilter in code; Noul-per-chunk prefilter for long docs
Score as measurementScores threshold and rank, but do not measure magnitudeNever interpolate "40% more angry" from a Score
Typed is not correctA schema-valid answer can be wrongVersion model, questions, criteria, and thresholds together; replay a labeled set
Adversarial stateState is data, and hostile text can steer itPrecise criteria and hostile-input testing before exposure
GenerationIt cannot write text, code, or summariesFind candidates in code, let Jev pick; never ask it to produce a value

The summary rule for extraction: pick a card from the deck, don't ask it to name a card. Generate or locate candidate values with code or an LLM, then let Jev select the intended one.

Thresholds, Rollout, and Maintenance

Confidence tells you how concentrated the distribution is, not how often the model is right. Two practices follow:

  • Thresholds come from your own labels. Log the probability alongside the eventual outcome, then move thresholds from data — never from an article, including this one.
  • Threshold with bands and margins. Never cut at a value the model actually returns, like 0.975. Scores drift by a hundredth between identical runs while the decision stays stable.

Roll out the same way every time: keep existing behavior, shadow Jev beside it and log full answers, label right and wrong, adjust questions and thresholds from that data, automate only the low-risk path, and keep a human or a stronger model for the uncertain band.

Then prune. After the first real run, read the results and cut every question whose answer never changed an action. Batch-oriented integrations accumulate dead questions the same way test suites accumulate dead tests. Finally, keep every question and threshold in one reviewable, versioned file per project — that is what gets diffed and reviewed, and a threshold change should be a one-line review.

Next