Question types
Every question has an id (its key in questions) and comes back as one answer under that id. Decisions pick among fixed answers with a calibrated probability for each. Reads write a value constrained to its type.
| Type | Family | Ask it when | Main answer field |
|---|---|---|---|
Noul |
Decision | A statement is true or false | noul: P(true) |
Choice |
Decision | One of several options applies | choice, probabilities |
Score |
Decision | Something sits on an ordered scale | score: expected level |
Fill |
Read (preview) | A value has to be written or computed | value, parsed |
Extract |
Read (preview) | A value appears verbatim in the input | value or values |
Locate |
Read (preview) | You need where something is in an image | coordinates, center |
Noul: yes or no
A statement, answered with the probability that it is true. Phrase it as a statement, not a question.
from simoai import Noul
"done": Noul("The task is finished")
"risky": Noul("The next action is risky",
criteria={"true": "it deletes data or moves money", "false": "it only reads"})"done": {"type": "noul", "instructions": "The task is finished"},
"risky": {"type": "noul", "instructions": "The next action is risky",
"criteria": {"true": "it deletes data or moves money", "false": "it only reads"}}criteria is optional: descriptions of what true and false mean, when the statement alone is ambiguous. The answer is {"type": "noul", "noul": 0.97}; in the SDK, .result is noul >= 0.5.
Choice: one of several
Pick one option. Options are a map from the key you get back to a description that helps the model. 2 to 256 options.
from simoai import Choice
"team": Choice("Which team should handle this ticket?",
{"billing": "refunds and payments",
"shipping": "delivery and tracking",
"tech": "website and app problems"})"team": {"type": "choice", "instructions": "Which team should handle this ticket?",
"criteria": {"billing": "refunds and payments",
"shipping": "delivery and tracking",
"tech": "website and app problems"}}The answer has choice (the most likely key), probabilities (one per option, summing to 1) and confidence.
Arguments for a choice
A Choice can generate arguments for the option the model picked, in the same request. Only the chosen option's arguments are produced, which makes this the natural way to pick a tool call:
"next": Choice("What should the agent do next?",
{"click": "click an element", "type": "type text", "stop": "the task is done"},
args={"click": {"target": Extract("the element to click")},
"type": {"text": Fill("the text to type", max_tokens=32)}})
r["next"].choice # "click"
r["next"].arg_values # {"target": "Sign up"}"next": {"type": "choice", "instructions": "What should the agent do next?",
"criteria": {"click": "click an element", "type": "type text", "stop": "the task is done"},
"args": {"click": {"target": {"type": "extract", "instructions": "the element to click",
"kind": "text", "many": false, "max_tokens": 64}},
"type": {"text": {"type": "fill", "instructions": "the text to type",
"kind": "text", "max_tokens": 32}}}}Score: a level on a scale
A position on an ordered scale. Any number of levels, listed from lowest to highest.
from simoai import Score
"mood": Score("How upset is the customer?", ["calm", "annoyed", "angry", "furious"])"mood": {"type": "score", "instructions": "How upset is the customer?",
"criteria": ["calm", "annoyed", "angry", "furious"]}score is the expected level, 0-based: 2.4 on this scale sits between "angry" and "furious". probabilities has one entry per level, keyed by its index ("0", "1", …, with the names in legend), and confidence is the largest of them.
Fill: write a value
Generate a short value: a number, a count, a summary, a code. Output is constrained to its type, so an int is always an integer and a json_list always parses.
from simoai import Fill
"amount": Fill("the amount due, in dollars", kind="number"), # parsed -> float
"count": Fill("how many items are in the order", kind="int"), # parsed -> int
"colors": Fill("the colors mentioned", kind="json_list"), # parsed -> list of strings
"code": Fill("the discount code", pattern=r"[A-Z]{3}-\d{4}"), # must match the regex
"summary": Fill("the customer's request in a few words", max_tokens=24),"amount": {"type": "fill", "instructions": "the amount due, in dollars", "kind": "number", "max_tokens": 16},
"count": {"type": "fill", "instructions": "how many items are in the order", "kind": "int", "max_tokens": 16},
"colors": {"type": "fill", "instructions": "the colors mentioned", "kind": "json_list", "max_tokens": 16},
"code": {"type": "fill", "instructions": "the discount code", "kind": "text", "pattern": "[A-Z]{3}-\\d{4}", "max_tokens": 16},
"summary": {"type": "fill", "instructions": "the customer's request in a few words", "kind": "text", "max_tokens": 24}| Argument | Values | Default |
|---|---|---|
kind |
text, int, number, json_list |
text |
pattern |
A regex the value must match (only with kind="text") |
none |
max_tokens |
1 to 256 | 16 |
The answer has value (the text), parsed (the typed value), valid (whether it satisfied its type) and confidence. When max_tokens cuts a value short, valid can be false.
Extract: copy from the input
Pull a value out of the state, verbatim where possible. With many=True, get every match as a list.
from simoai import Extract
"order": Extract("the order number, exactly as written", max_tokens=8), # value -> "A-1042"
"orders": Extract("every order number mentioned", many=True), # values -> ["A-1042", "B-2210"]"order": {"type": "extract", "instructions": "the order number, exactly as written", "kind": "text", "many": false, "max_tokens": 8},
"orders": {"type": "extract", "instructions": "every order number mentioned", "kind": "text", "many": true, "max_tokens": 64}max_tokens is 1 to 512 (default 64). In the wire format, "list_format": "json" makes a many extract come back from a JSON list instead of one item per line; the result is the same values list.
Locate: find it in an image
Where something is in an image of the state. Needs an image in media.
from simoai import Locate
"pay_btn": Locate("the Pay button"), # in the first image
"change": Locate("what changed", image=2), # in the second image"pay_btn": {"type": "extract", "kind": "coordinates", "instructions": "the Pay button",
"image": 1, "scale": "rel1000", "max_tokens": 64}The answer has coordinates (a box [x1, y1, x2, y2]), center ([x, y]) and image_size ([width, height]), in pixels. When the thing is not visible, coordinates and center are null. The SDK maps answers back to the pixels of the image you passed in; over raw HTTP they are in the pixels of the image you sent.
| Argument | Meaning | Default |
|---|---|---|
image |
Which image, 1-based (the N of <image:N>) |
1 |
scale |
How coordinates are expressed to the model: rel1000 (a 0-1000 grid) or pixels. The answer is in pixels either way |
rel1000 |
max_tokens |
Output budget | 64 |
Plain dictionaries
Anywhere the SDK accepts a question object, a dict in the wire format works too, which is handy for questions stored in JSON files:
{"type": "noul", "instructions": "The task is finished"}
{"type": "extract", "instructions": "every SKU", "kind": "text", "many": True, "list_format": "json"}