Question sets
A question set is one JSON file that holds every question annotate asks of each record.
{
"version": 1,
"questions": {
"steps": {
"decide": "Does the report give steps to reproduce?"
},
"area": {
"choose": "Which part of the app is this?",
"options": [
"export",
"login",
"billing"
]
},
"impact": {
"score": "How much does this block the user?",
"levels": [
"None.",
"Slows them.",
"Blocks work."
]
}
}
}A set may also carry a top-level threshold and say which part of each record a question reads.
{
"version": 1,
"threshold": "0.2:0.8",
"questions": {
"urgent": {
"decide": "Does the customer need an answer today?",
"on": ["/subject", "/body"]
},
"team": {
"choose": "Which team owns this?",
"options": {
"billing": "Invoices, fees, and refunds.",
"shipping": "Parcels and delivery."
},
"on": "/body"
}
}
}The top level
versionis the number 1.questionsholds the questions, each under its name.thresholdapplies to everydecidequestion that names none. Hereurgenttakes the band 0.2:0.8.profilenames the backend profile the thresholds were set for.batchis"max"or a whole number above 0. A flag orTHINKTHEN_BATCHoutranks it.
Those five keys are the only ones allowed. The set names no address, model or output format.
Each question
A name uses lowercase letters, digits and underscores, and is not empty. The name becomes the answer's field in the output. Each question has the shape of one question file: one of decide, choose, tag or score, with its own options, levels and threshold. A question may not carry batch or its own profile.
on
on picks the part of the record a question reads. It is one JSON Pointer or a list of them, read inside the record or inside what --field selected. Several pointers send one object, keyed by the last part of each pointer. Two pointers that end in the same name are an error in the file. A question with no on reads the whole record, or what --field selected.
cat <<'EOF' |
{"id": "T-1", "subject": "Refund", "body": "Please refund the fee."}
EOF
thinkthen annotate triage.json \
--jsonl \
--plan |
jq 'select(.on) | .on,
(.request.questions | map_values(.instructions))'{
"urgent": [
"/subject",
"/body"
],
"team": [
"/body"
]
}
{
"q1": "The text is {\"subject\":\"Refund\",\"body\":\"Please refund the fee.\"}. Does the customer need an answer today?",
"q2": "The text is \"Please refund the fee.\". Which team owns this?"
}A text record has no parts. A question with on refuses it at exit 2. Under --lines, such a set is refused before any input is read.
What the file refuses
An unknown key anywhere in the file, a missing questions object, a wrong version and a bad name each stop the run at exit 5, before any request. The message names the key at fault.
annotate's edge cases · Question files · The question-set specification
On GitHub: github.com/botassembly/thinkthen