Questions and answers
A Curva request has a state (the data to judge) and one or more typed questions (up to 64). Every question gets a typed answer with probabilities. Answers are only ever mapped onto the labels you declared, never parsed from free text, so there is nothing to validate on your side. Extraction questions (Text, Number, Integer) answer with a value instead, checked against its type and bounds, plus the probability that it is correct.
The four label question types
Section titled “The four label question types”| Type | Asks | Answer |
|---|---|---|
| Choice | Pick exactly one option | choice (the option key), probabilities per option, confidence |
| Score | Place on an ordered scale | score (the expected level, 0 = lowest), probabilities per level, confidence |
| Noul | Yes or no | noul = P(yes) |
| Multi | Pick any subset | selected (options at or above the threshold), probabilities per option |
The Choice, Score and Noul values in the picture are the ones in the response below. The Multi bars are an illustration.
Choice
Section titled “Choice”Choice("Which team should handle this?", {"billing": "payments, refunds", "technical": "bugs", "sales": "pricing"})Choice("Which team?", ["billing", "technical"]) # a list of keys also worksOptions map a key to a description (the description may be empty). A Choice takes 2 to 255
options. By default Curva also adds a none_of_these option, so the model is never forced into a
wrong pick (see escape option).
Score("How frustrated is the customer?", ["calm", "annoyed", "angry"])Levels go from lowest to highest (2 to 20 levels). The score is the expected level: with
probabilities [0.36, 0.62, 0.02] the score is 0.65, between “calm” and “annoyed”. This keeps
the uncertainty visible instead of rounding it away.
Noul("The customer explicitly asks for a refund")A yes/no question. The answer is a single number, noul, the probability of yes. Internally a
Noul is asked as a normalised two-option choice, so P(x) and P(not x) are consistent.
Multi("Which apply?", ["refund", "bug", "complaint"], threshold=0.5)Each option is scored as its own yes/no, all in the same model call. The probabilities are
independent, so they don’t sum to 1. Options with a probability at or above threshold
(default 0.5) are in selected. A Multi takes 1 to 20 options.
Extraction: Text, Number and Integer
Section titled “Extraction: Text, Number and Integer”| Type | Asks | Answer |
|---|---|---|
| Text | Copy or write a short text | value (a string, at most max_length characters, default 200), confidence |
| Number | Read a number, optionally within min..max |
value (a number), confidence |
| Integer | Read a whole number, optionally within min..max |
value (an integer), confidence |
Text("Who issued the invoice?", max_length=100)Number("Total amount charged", min=0)Integer("How many line items?", min=1, nullable=True) # value may be Noneconfidence is the model’s probability that the value is correct, and it is calibrated from your
feedback like any other answer (the feedback label is the true value). nullable=True lets the
answer be null when the state has no such value. A reply of the wrong type, out of range, or
too long counts as no answer: value: null, confidence: 0.
Extraction questions are answered in verbal mode (there is no label token to read), and they
share one call with the label questions of the same request. See the
extraction guide.
How probabilities are read
Section titled “How probabilities are read”Curva reads probabilities from the model in one of two modes:
logprobs: the model answers with one label token per question, and Curva reads each label’s probability from the token log-probabilities. One request covers every question. If the declared labels hold less than half of the probability at a position, that question is retried on its own.verbal: the model returns a JSON object constrained to the declared labels, with a probability for each one.
The default, auto, uses logprobs when the model returns them and switches to verbal when it
doesn’t. The response’s mode field says which one actually answered. Choices with more than 20
options are always answered in verbal mode, because providers return at most 20 logprobs, and so
are requests with a Text, Number or Integer question.
The response
Section titled “The response”{ "id": "dec_19294a3c1f2000000", "model": "inclusionai/ling-3.0-flash-fin:free", "mode": "logprobs", "latency_ms": 1144, "cost_usd": 0.0, "cached": false, "answers": { "department": { "choice": "billing", "probabilities": { "billing": 0.9999, "technical": 0.0, "sales": 0.0001 }, "confidence": 0.9999 }, "frustration": { "score": 0.65, "probabilities": [0.36, 0.62, 0.02], "confidence": 0.62 }, "refund_requested": { "noul": 0.999 } }}Every answer also carries calibrated, which becomes true once your feedback has fitted a
calibrator for that exact question (Probabilities and calibration).
Identical requests are served from a decision cache: cached: true, no model call, and zero
latency and cost.

