Skip to main content
Use a Score when the answer is a position on a spectrum you can describe in steps. For example, how severe a bug is, how happy a customer is, or how much Python experience a candidate has. If the answer is one of a fixed set of options with no order between them, use a Choice. If it’s a yes or no, use a Noul. Choose a question type compares all three. A Score answer is a position along your levels in score, which can fall between two levels. The model also returns a probability for every level in probabilities, and a confidence value for the answer. Example Score questions:
The numbers in front of each step are positions, explained under Levels.

Request structure

The POST request body to the TypeSafe API has the same three top-level fields as any other question type: state, which is the content to evaluate; model; and questions. Each Score question has the following fields:
  • type: Always "score".
  • instructions: The question the model answers. What it’s rating.
  • criteria: An ordered array of level descriptions, from the low end of the scale to the high end. Needs at least two levels and takes up to 10.
Below is a request where the state is a bug report and the question is how severe the bug is: You choose the question id, bug_severity in this case. This id is not sent to the model. The answer is returned under the same id.

Levels

Each entry in criteria is a level: one point on the spectrum of possible answers, described in words. A level’s number is its position in the criteria array, starting at 0, so the three entries above are levels 0, 1 and 2. The order of the array is the numbering. The model gets the descriptions and nothing else, and each level is judged on its own against the state. The score in the response is a position on the levels spectrum. For a three-level scale it runs from 0 to 2, and it can land between two levels. Our client SDKs provide typed questions. In Python, the same question is a Score:
Use the system_one method or the https://api.typesafe.ai/v1/systemone endpoint to call a System One model. The model field selects which model handles the request. How to build with TypeSafe covers where in your code to call it. Use one of our client SDKs or call the TypeSafe API directly. If a coding agent is writing the integration for you, install the TypeSafe agent skill first so it knows the request and response shapes.
instructions and each level in criteria can be a string, an object, or an array. Start with strings. Use an object when a level needs a description plus a few example situations. See Structured level descriptions below and the API reference.

Response structure

The response has one entry in answers per question, under the ids from the request. This is the response to the example request above:
Each Score answer has five values:
  • type: The type of TypeSafe question.
  • probabilities: The probability of each level, keyed by level number as a string. The sum of all values is 1.
  • score: The position on the level number line, from 0 to the top level number, which is 2 here. It’s each level number multiplied by its probability, added up: 0 x 0.0 + 1 x 0.70 + 2 x 0.30 = 1.30.
  • legend: Each level number mapped back to its description.
  • confidence: A number from 0 to 1 computed from how probabilities is spread. A single peak on one level means high confidence. Probability spread over several levels means low confidence.
A score of 1.30 means mostly level 1 with some weight on level 2. That matches the report: the export is broken, and switching to Chrome is a workaround for most customers, but not for the ones who only use Safari. The model puts 0.70 on “workaround exists” and 0.30 on “no workaround”, and confidence is 0.54 because it’s split. Using the Python SDK, ScoreAnswer has score, confidence, probabilities, and legend as typed fields. The SDK keys probabilities and legend by integer level rather than by string.

Reading a Score

Let’s look at how the score changes with different inputs. For example, using the question and its levels from the request above:
We can see how different bug reports change the score:
probabilities
StatescoreconfidenceLevel 0Level 1Level 2
The export button is misaligned by a few pixels on the settings page.0.01.01.00.00.0
The PDF export button does nothing when clicked. I can still export to CSV and convert it myself, but that takes ages.1.01.00.01.00.0
Export to PDF fails with a spinner that never finishes. Some of our team say CSV export still works for them, others say it fails too.1.120.810.00.880.12
The export button crashes the settings page in Safari. It works in Chrome, but a few of our customers only use Safari.1.30.540.00.70.3
Nobody on our team can log in since this morning. We get a 500 error on every attempt.2.01.00.00.01.0
In these examples, confidence 1.0 means the returned distribution puts all its probability on one level. This describes the model’s answer, not a guarantee that the answer is correct. The score is a probability-weighted mean of the level numbers. In the third and fourth examples, probability is split between levels 1 and 2. More weight on level 2 raises the score. It does not measure the fraction of customers without a workaround. Different distributions can produce the same score. A score of 1.0 can mean all probability is on level 1, or half is on each of levels 0 and 2. Read probabilities and confidence alongside the score to distinguish these cases. A fractional score is a position. You can use it to rank reports by severity, or round it to the nearest level when your code needs one outcome. Our entity alignment cookbook shows an example of rounding to the nearest level to make a decision. Low confidence on a Score usually means one of three things. The levels overlap for this state, the question is measuring more than one thing, or the state doesn’t say enough to place it. Our Confidence docs cover how to use it in your code.

Writing good levels

Describe situations, not degrees. “Broken or degraded feature, but workaround exists” gives the model something to match the state against. “Moderately severe” doesn’t. Concrete descriptions can help the model distinguish levels. Check the answers against known examples; higher confidence alone does not show that a description is better. Every level is evaluated separately. The model doesn’t see a level’s number or its neighbours, so “worse than the previous level” means nothing to it, and numbers in the descriptions or the instructions don’t help. Here is what happens when the levels are only numbers, on the misaligned-button report from the table above:
The same report with the three descriptive levels scores 0.0 at confidence 1.0. With numbers only, the model has nothing to match against and splits the probability between 0 and 1. Use as many levels as you can describe distinctly, up to 10. Three is fine. Don’t add levels you can’t describe distinctly. Keep each Score to one dimension. If a description says “punctual and smart and experienced”, the question is measuring three things, and an input that is high on one and low on another can’t be placed. Confidence drops and the score means less. Split it into one Score per thing and combine them in code, as the next section shows. If the top of your scale has a rare extreme case you need to act on differently, give it its own level. A sentiment scale that ends at “very angry” can add “abusive or threatening”. Without that level, both messages may receive a score near the top. The score alone may not distinguish them. If there is no in-between at all, and the answer is one of a few discrete categories, use a Choice instead, or split the question into several Noul questions. It’s important to test your levels against your own data. Two wordings of the same scale can behave differently on your data.

Splitting a complex judgment into several Scores

A complex judgment, one that depends on several things, is best split into one Score per thing. You can then combine the Scores returned from TypeSafe in your code to make the judgment. Some Scores may matter more than others, so give each Score a weight for its relative importance. The weights are yours. When the combined result doesn’t match what your team would decide, change them in code and run again. Send the Scores in one request. They are evaluated in parallel. Adding questions barely changes the response time and costs a few extra question tokens; see Ask multiple questions together. The request below is the spinner ticket from the table above with some more context. It asks three Scores: how severe the bug is, how frustrated the customer is, and how much the report gives an engineer to work with. TypeSafe’s response:
Each question is answered on its own against the ticket and given a score:
  • severity is 1.24 at confidence 0.63. Same reading as the opening example: the export is broken and some have a workaround.
  • frustration is 1.45 at confidence 0.33. The wording is civil, but “third time” and “I’m done” shift the score toward the top level, so the model splits 0.55 and 0.45 between “frustrated but civil” and “very angry”. For this ticket the two levels overlap, which explains the low confidence.
  • report_quality is 3.0 at confidence 1.0. The steps and browser version are both stated.
The three scales have different lengths, so before combining them, normalize each score. A four-level scale returns 0 to 3 and a three-level scale returns 0 to 2, so a top score on one is bigger than a top score on the other. Divide each score by its top level number, len(criteria) - 1, to put every score on 0 to 1. Then the weights mean what they say: 0.6 on severity and 0.3 on frustration makes severity count twice as much. The TypeSafe Python SDK code below asks the three questions, normalizes each score, and combines them using an example priority calculation:
For the example response above, the normalized scores are 0.62 for severity, 0.725 for frustration, and 1.0 for report quality. The priority is 0.6 × 0.62 + 0.3 × 0.725 + 0.1 × 1.0 = 0.6895, which rounds to 0.69. The weights live in your code, so you can see exactly how the number is made and change it when the ranking doesn’t match what your team would do. If you later need more Scores, add them to TRIAGE_QUESTIONS. The request count stays at one. This technique of breaking a complex judgment into separate Scores and then combining them with weights in your code is called the Composite scoring pattern.

Structured level descriptions

Start with a basic text description for each level. When the model keeps scoring between two neighbouring levels on inputs you think are clear, give each level an object instead of a string, with a field for what the level covers and a field with a few example situations. Use the same field names on every level so the model can compare like with like. The request below is the spinner ticket that we used earlier, but with examples on each level: The response:
With plain strings this ticket scored 1.12 with a confidence of 0.81. With examples it scores 1.06 at 0.91 confidence. Examples steer the model, and they only help when they look like your real inputs. The table below is the opening Safari report with three different sets of level objects: In this comparison, the matching example concentrates more probability on one level. The unrelated example changes the result only slightly compared with plain strings. Higher confidence does not establish which answer is correct. Choose examples with known expected levels, then test the revised descriptions on separate inputs before keeping them.