Sign-up and API keys are open. Paid top-ups open soon.What changed
Docs menu

Concepts

Questions

The question object, question ids, the pick, rate and check types, criteria, and how questions in one request relate.

View as Markdown

A question is a typed ask about the state. There are three types: pick chooses one of your options, rate (beta) places the state on your scale, and check gives the probability that a statement is true. One request carries 1 to 32 questions, keyed by ids you choose.

The question object

Field Types Required Rule
type all yes "pick", "rate" or "check".
instructions all yes What to decide, 1 to 4,000 characters, not only whitespace. May contain field references.
criteria all no Extra rules or definitions. See Criteria.
options pick yes, for pick Label to description. Rejected on other types.
levels rate yes, for rate Ordered level names. Rejected on other types.
min_confidence all no 0 to 1. Enables abstention.

A field that is not in this table is rejected with 422 unknown_field (or 422 field_not_allowed for a field of another question type), so a typo such as instruction fails loudly instead of being ignored. The same holds for the top level of the request, where an unknown field gives 422 unknown_field.

Question ids

The keys of questions are your question ids. An id matches ^[A-Za-z][A-Za-z0-9_-]{0,63}$: it starts with a letter, then has up to 63 more letters, digits, underscores or hyphens. Anything else gives 422 invalid_question_id.

Ids are for your code only. They never reach the model, so an id such as angry tells the model nothing. Put everything the model needs in instructions, criteria, options or levels.

Answers come back under the same ids, in the order you sent the questions.

pick

A pick chooses one option from a set you define.

{
  "type": "pick",
  "instructions": "Which team should handle {{ticket.body}}?",
  "options": {
    "billing": "Payments, refunds and invoices",
    "technical": "Bugs, outages and sign-in problems",
    "shipping": "Delivery and returns",
    "other": null
  },
  "min_confidence": 0.5
}

options maps each label to a description or null:

  • 1 to 255 options. More gives 422 too_many_options.
  • A label is 1 to 100 characters, with no leading or trailing whitespace.
  • Labels must differ by more than case or Unicode normalization. Billing and billing, or two labels that differ only in how an accented letter is encoded, give 422 duplicate_label: the model cannot tell them apart.
  • A description is null or 1 to 1,000 characters, and not only whitespace. Use null when the label says it all, as other does above. Add a description when a label could be read two ways.

Order matters. Options are shown to the model in the order you send them, and a tie between probabilities goes to the earlier option. Sending the same options in a different order is a different request and may give different probabilities. Python dicts keep the order of every key. JavaScript objects, and so JSON.parse, do not: they list integer-like keys such as "1", "2" and "10" first, in numeric order, whatever order your text used. A pick with labels "10", "2", "1" that goes through a plain JavaScript object reaches Dex as "1", "2", "10", which is a different request.

  • TypeScript SDK. Pass labels to pick() in the order you want. For an object you built another way, mark its order with withKeyOrder(obj, keys) before decide(). On the way back, read a pick answer's probabilities with orderedEntries() to get option order. A request you read from a file keeps its order with parseRequest(text).
  • CLI and VS Code extension. They read request files in file order and send them in that order.
  • Your own code. If your labels look like integers, build the JSON text yourself or use a parser that keeps order.

Include a way out. If the state might fit none of your options, add one such as other or none. Otherwise the model must choose among options that do not fit.

More than 255 options. Split the choice into stages: first pick a department, then send a second request that picks a queue within it.

A pick answer has:

Field Meaning
choice The most probable label. Ties go to the earlier option.
probabilities Label to probability, in option order, summing to exactly 1.
confidence The gap between the two largest probabilities. See Confidence.
abstained true when confidence is below min_confidence.

On the fallback path, a pick with more than 20 options gets exact probabilities only for the 20 most likely options. See Fallback and served_by.

rate

A rate places the state on an ordered scale.

Beta. rate works and is part of the API, but its measured quality misses our bars. On our test set, dex-1.0.1 picks the reference level on 63.7% of rate questions (the bar is 68%), and its calibration error is 0.138 (the bar is 0.05). Treat its probabilities as a rough guide, set thresholds from your own data, and set min_confidence so an unsure rating abstains. See Quality bars.

{
  "type": "rate",
  "instructions": "How fast must we reply to {{ticket.body}}?",
  "levels": ["Can wait", "This week", "Within two working days", "Today"]
}

levels is a list of 2 to 10 unique strings, each 1 to 200 characters, ordered from lowest (index 0) to highest. Anything else gives 422 invalid_levels, and so do two levels that differ only in case or Unicode normalization, such as Low and low. A level follows the label rule: no whitespace at either end and no line breaks, or 422 invalid_value with the level in param, such as questions.spoed.levels.2. Write levels that form a real scale, because the rating treats their order as meaningful.

A rate answer has:

Field Meaning
rating The probability-weighted level index, from 0 to the number of levels minus 1.
levels The levels you sent, in order.
probabilities One probability per level, aligned with levels, summing to exactly 1.
confidence 1 when all probability sits on one level, 0 when it splits evenly between the two extreme levels.
abstained true when confidence is below min_confidence.

rating is not rounded to a level. It is an average: it tells you where the probability centers, and confidence tells you how spread out it is. Compare the rating with thresholds of your own, round it when you need one level, or read probabilities when you need the whole distribution.

check

A check gives the probability that a statement about the state is true.

{
  "type": "check",
  "instructions": "Is the writer of {{ticket.body}} angry?",
  "min_confidence": 0.6
}

A check has no options or levels. Its answer has probability (the probability of yes), confidence and abstained. Phrase the statement so that yes is the case you act on.

Criteria

criteria adds rules and definitions the model must apply to one question. It takes:

  • a string of 1 to 4,000 characters, or
  • a list of 1 to 20 strings, each 1 to 500 characters, or
  • null, which is the same as leaving it out.

A criteria string of only whitespace gives 422 invalid_value, as does such an item in the list.

Criteria may contain field references. Use them for edge cases and house rules. The English moderation example in the contract uses two:

{
  "type": "pick",
  "instructions": "Which community rule does {{post.text}} break, if any?",
  "criteria": [
    "A threat to expose where someone lives counts as harassment even when it is conditional.",
    "Sharing your own phone number in a sale listing is allowed."
  ],
  "options": {
    "none": "Breaks no rule",
    "harassment": "Threats, intimidation or exposing someone's personal details",
    "spam": "Repeated or unsolicited promotion",
    "self_harm": "Encourages or describes self-harm",
    "hate": "Attacks people for a protected characteristic"
  },
  "min_confidence": 0.6
}

Criteria are billed as question tokens, once per question that carries them.

Questions in one request

All questions share the state, which is read and billed once. Each question sees the fixed prompt prefix, the state and itself, not the text of the other questions.

v1 does not promise question isolation. Adding, removing or reordering another question in the same request can move an answer slightly, because the questions are computed together. The same request bytes always give the same answers. If an answer must not depend on the other questions, send that question in a request of its own. See Determinism.

This also means Dex does not make answers consistent with each other. Two differently phrased questions about the same fact can disagree.

When exactly one of several outcomes must hold, ask one pick instead of several checks. Two checks such as "Is this about billing?" and "Is this about shipping?" can both come back above 0.5. A single pick with billing, shipping and other always returns exactly one choice, with probabilities that sum to 1.

Some questions are weaker today: sarcasm read literally, date and amount arithmetic, and criteria written in Dutch. See Known limits for the numbers and how to work around each.