Skip to content
Questions

Patterns

A few shapes that come up again and again when decisions are made from probabilities. Each one is a handful of lines around a single API call.

Routing by confidence

Let the model handle the cases it is sure about and send the rest to a person. One choice question picks the queue; the confidence decides whether the pick is used.

from typesafe_sdk import Choice, TypeSafeClient # Reads TYPESAFE_BASE_URL (https://api.vakyn.com) and TYPESAFE_API_KEY from the environment.client = TypeSafeClient()QUEUES = {    "billing": "Charges, invoices and refunds",    "shipping": "Delivery, tracking and damaged parcels",    "account": "Sign-in, password and profile",}  def route(message: str) -> str:    result = client.system_one(        state=message,        questions={"queue": Choice(instructions="Which queue should handle this?", criteria=QUEUES)},    )    answer = result.choices["queue"]    if answer.confidence >= 0.8:        return answer.choice  # sure enough: route automatically    return "triage"  # unsure: a person decides  print(route("I changed my email address and now the password reset link never arrives."))
  • Measure how many messages fall under the threshold. If it is too many, the options are probably unclear or incomplete; fix the criteria before lowering the bar.
  • Log the distribution, not only the pick. When a person corrects a route, you can see whether the model had the right answer in second place.

Example: moderating product reviews

Routing by confidence with several questions at once. Each review gets a noul (fake?), a choice (topic) and a score (how negative). The code acts only when every answer is sure enough; anything else waits for a moderator. The How VAKYN works page walks through it.

Questionsfakenoultopicchoicenegativescore
  • Clear case

    Acts automatically
    Stopped working after five weeks. 1 star. I bought the Tallow & Pine CafeDuo 2 in March and used it every morning for two coffees. In week five the steam wand stopped heating, then the pump started rattling and the machine shut itself off mid-shot. I descaled it twice with the brand's own tablets and followed the reset steps in the manual exactly. Nothing helped. For a machine at this price I expected years, not weeks. Do not buy. Marta K., verified purchase
    QuestionAnswerConfidence
    fakeyes 0.040.96
    topicquality0.97
    negative3.94 of 40.95

    Every answer is at least 0.70 sure (lowest: negative 0.95), so your code acts on it.

  • Borderline case

    Goes to a person
    Decent kettle for what I paid. It took ages to show up and the box was a bit crushed, but it boils fine. Would maybe buy again if it goes on sale. J.
    QuestionAnswerConfidence
    fakeyes 0.030.97
    topicquality0.23
    negative1.93 of 40.94

    topic is only 0.23 sure, under 0.70, so a person decides.

Real answers from VAKYN-4B Q8_0. The rule: act when every answer is at least 0.70 sure; a noul's confidence is how far it leans either way.
curl -s "https://api.vakyn.com/v1/systemone" \  -H "Authorization: Bearer $TYPESAFE_API_KEY" \  -H "Content-Type: application/json" \  -o answer.json \  -d @- <<'JSON'{  "model": "jev-latest",  "state": "Stopped working after five weeks. 1 star.\n\nI bought the Tallow & Pine CafeDuo 2 in March and used it every morning for two coffees. In week five the steam wand stopped heating, then the pump started rattling and the machine shut itself off mid-shot. I descaled it twice with the brand's own tablets and followed the reset steps in the manual exactly. Nothing helped. For a machine at this price I expected years, not weeks. Do not buy.\n\nMarta K., verified purchase",  "questions": {    "fake": {      "type": "noul",      "instructions": "Is this review likely fake: paid for, written by the seller, or not based on using the product?"    },    "topic": {      "type": "choice",      "instructions": "What is the review mainly about?",      "criteria": {        "quality": "How the product works, feels or holds up",        "delivery": "Shipping, packaging and arrival",        "price": "Price and value for money",        "service": "Support, returns and refunds",        "other": null      }    },    "negative": {      "type": "score",      "instructions": "How negative is the review?",      "criteria": [        "Positive",        "Mostly positive",        "Mixed",        "Mostly negative",        "Very negative"      ]    }  }}JSON # Act when every answer is at least 0.70 sure; otherwise a person decides.jq -r '.answers as $a  | {fake: ([$a.fake.noul, 1 - $a.fake.noul] | max), topic: $a.topic.confidence, negative: $a.negative.confidence}  | to_entries | min_by(.value) as $weakest  | if $weakest.value >= 0.70    then "act: \(if $a.fake.noul >= 0.5 then "remove" else "publish" end), topic \($a.topic.choice), negative \($a.negative.score) of 4"    else "human: \($weakest.key) is only \($weakest.value * 100 | round / 100) sure" end' answer.json

The borderline case runs the same code with its own state. topic is only 0.23 sure, under 0.70, so it prints a human line and a person decides. Change the threshold to match what a wrong answer costs you.

Run it in the playground, with a threshold slider

Example: approving expense reports

The same rule on structured data. The report goes in as JSON with the policy next to it; a choice says approve, flag or reject, a noul says whether any line breaks the policy, and a score rates the risk of paying it as filed. Reports the model is sure about are paid or returned by code; the rest go to finance. The How VAKYN works page walks through it.

Questionsdecisionchoiceover_policynoulriskscore
  • Clear case

    Acts automatically
    { "policy": { "rail": "Second class, booked through the travel desk", "hotel": "Up to EUR 180 a night", "meals": "Up to EUR 60 a day while travelling, no alcohol", "receipts": "A receipt for every line over EUR 25" }, "report": { "employee": "Ines Varga, field engineer", "trip": "Customer site visit in Lyon, 6 October 2026", "lines": [ { "date": "2026-10-06", "item": "Flight, Brussels to Lyon and back, business class, booked privately", "amount_eur": 1240, "receipt": false }, { "date": "2026-10-06", "item": "Hotel, three nights for a one-day visit", "amount_eur": 1260, "receipt": false }, { "date": "2026-10-06", "item": "Dinner, alone, two bottles of wine", "amount_eur": 310, "receipt": false } ], "total_eur": 2810 } }
    QuestionAnswerConfidence
    decisionreject0.82
    over_policyyes 1.001.00
    risk2.97 of 30.97

    Every answer is at least 0.70 sure (lowest: decision 0.82), so your code acts on it.

  • Borderline case

    Goes to a person
    { "policy": { "rail": "Second class, booked through the travel desk", "hotel": "Up to EUR 180 a night", "meals": "Up to EUR 60 a day while travelling, no alcohol", "receipts": "A receipt for every line over EUR 25" }, "report": { "employee": "Pieter Claes, account manager", "trip": "Trade fair in Milan, 13 to 14 October 2026", "lines": [ { "date": "2026-10-13", "item": "Rail, Brussels to Milan, second class, booked by the travel desk", "amount_eur": 164, "receipt": true }, { "date": "2026-10-13", "item": "Hotel, one night, city tax EUR 9 included", "amount_eur": 189, "receipt": true }, { "date": "2026-10-13", "item": "Dinner with two people from a prospect", "amount_eur": 118, "receipt": true, "note": "Talked about a pilot" }, { "date": "2026-10-14", "item": "Taxi to the fair", "amount_eur": 27, "receipt": false, "note": "Driver's card reader was broken" } ], "total_eur": 498 } }
    QuestionAnswerConfidence
    decisionreject0.65
    over_policyyes 0.940.94
    risk2.52 of 30.52

    risk is only 0.52 sure, under 0.70, so a person decides.

Real answers from VAKYN-4B Q8_0. The rule: act when every answer is at least 0.70 sure; a noul's confidence is how far it leans either way.
curl -s "https://api.vakyn.com/v1/systemone" \  -H "Authorization: Bearer $TYPESAFE_API_KEY" \  -H "Content-Type: application/json" \  -o answer.json \  -d @- <<'JSON'{  "model": "jev-latest",  "state": {    "policy": {      "rail": "Second class, booked through the travel desk",      "hotel": "Up to EUR 180 a night",      "meals": "Up to EUR 60 a day while travelling, no alcohol",      "receipts": "A receipt for every line over EUR 25"    },    "report": {      "employee": "Ines Varga, field engineer",      "trip": "Customer site visit in Lyon, 6 October 2026",      "lines": [        {          "date": "2026-10-06",          "item": "Flight, Brussels to Lyon and back, business class, booked privately",          "amount_eur": 1240,          "receipt": false        },        {          "date": "2026-10-06",          "item": "Hotel, three nights for a one-day visit",          "amount_eur": 1260,          "receipt": false        },        {          "date": "2026-10-06",          "item": "Dinner, alone, two bottles of wine",          "amount_eur": 310,          "receipt": false        }      ],      "total_eur": 2810    }  },  "questions": {    "decision": {      "type": "choice",      "instructions": "What should finance do with this expense report?",      "criteria": {        "approve": "Pay it as filed",        "flag": "Hold it until a person checks it",        "reject": "Send it back to the employee to fix"      }    },    "over_policy": {      "type": "noul",      "instructions": "Does any line in the report break the expense policy?"    },    "risk": {      "type": "score",      "instructions": "How risky is it to pay this report as filed?",      "criteria": [        "No risk",        "Low risk",        "Some risk",        "High risk"      ]    }  }}JSON # Act when every answer is at least 0.70 sure; otherwise a person decides.jq -r '.answers as $a  | $a | map_values(if .type == "noul" then [.noul, 1 - .noul] | max else .confidence end)  | to_entries | min_by(.value) as $weakest  | if $weakest.value >= 0.70    then "act: \($a.decision.choice), over policy \(if $a.over_policy.noul >= 0.5 then "yes" else "no" end), risk \($a.risk.score) of 3"    else "human: \($weakest.key) is only \($weakest.value * 100 | round / 100) sure" end' answer.json

The borderline case runs the same code with its own state. risk is only 0.52 sure, under 0.70, so it prints a human line and a person decides. Change the threshold to match what a wrong answer costs you.

Run it in the playground, with a threshold slider

Guardrails for agents

Put VAKYN between an agent and the APIs it calls. Before a call goes out, send the call, the agent's job and the context as the state, and ask whether to allow it, hold it for a person or block it, with the facts that decide it as their own questions: can it be undone, how much damage could a mistake do, is it the agent's job. The call runs only when every answer is sure; the rest wait for a person. The How VAKYN works page walks through it.

Questionsactionchoiceirreversiblenouldamagescorewithin_jobnoul
  • Clear case

    Acts automatically
    { "agent": { "name": "Returns assistant at Orla Cycles", "job": "Handles returns. May refund returned items to the original payment method, up to EUR 1,000 per order, once the warehouse has checked the return in." }, "call": { "api": "POST /payments/refunds", "body": { "order": "OC-58213", "amount": 900, "currency": "EUR", "to": "original payment method", "reason": "Returned, unused" } }, "context": { "order": "OC-58213, one e-bike battery, paid EUR 900.00 by card on 2026-09-28", "return": "Checked in by the warehouse on 2026-10-09: sealed, unused, serial number matches the order", "earlier_refunds_on_order": 0, "customer": "Customer since 2022, 11 orders, no earlier refunds, same card on file since 2022" } }
    QuestionAnswerConfidence
    actionallow1.00
    irreversibleyes 0.140.86
    damage0.21 of 30.79
    within_jobyes 0.990.99

    Every answer is at least 0.70 sure (lowest: damage 0.79), so your code acts on it.

  • Borderline case

    Goes to a person
    { "agent": { "name": "Returns assistant at Orla Cycles", "job": "Handles returns. May refund returned items to the original payment method, up to EUR 1,000 per order, once the warehouse has checked the return in." }, "call": { "api": "POST /payments/refunds", "body": { "order": "OC-60477", "amount": 900, "currency": "EUR", "to": "original payment method", "reason": "Parcel never arrived" } }, "context": { "order": "OC-60477, one e-bike battery, paid EUR 900.00 by card on 2026-10-02", "return": "None: the customer says the parcel never arrived", "carrier": "Tracking says delivered to a parcel locker on 2026-10-05", "earlier_refunds_on_order": 0, "customer": "First order, account opened 2026-10-01" } }
    QuestionAnswerConfidence
    actionblock0.93
    irreversibleyes 0.290.71
    damage0.77 of 30.23
    within_jobyes 0.420.58

    damage is only 0.23 sure, under 0.70, so a person decides.

Real answers from VAKYN-4B Q8_0. The rule: act when every answer is at least 0.70 sure; a noul's confidence is how far it leans either way.
curl -s "https://api.vakyn.com/v1/systemone" \  -H "Authorization: Bearer $TYPESAFE_API_KEY" \  -H "Content-Type: application/json" \  -o answer.json \  -d @- <<'JSON'{  "model": "jev-latest",  "state": {    "agent": {      "name": "Returns assistant at Orla Cycles",      "job": "Handles returns. May refund returned items to the original payment method, up to EUR 1,000 per order, once the warehouse has checked the return in."    },    "call": {      "api": "POST /payments/refunds",      "body": {        "order": "OC-58213",        "amount": 900,        "currency": "EUR",        "to": "original payment method",        "reason": "Returned, unused"      }    },    "context": {      "order": "OC-58213, one e-bike battery, paid EUR 900.00 by card on 2026-09-28",      "return": "Checked in by the warehouse on 2026-10-09: sealed, unused, serial number matches the order",      "earlier_refunds_on_order": 0,      "customer": "Customer since 2022, 11 orders, no earlier refunds, same card on file since 2022"    }  },  "questions": {    "action": {      "type": "choice",      "instructions": "What should the guardrail do with this API call?",      "criteria": {        "allow": "Let the agent make the call now",        "ask_human": "Hold the call until a person approves it",        "block": "Stop the call and tell the agent why"      }    },    "irreversible": {      "type": "noul",      "instructions": "Would this call be hard or impossible to undo once made?"    },    "damage": {      "type": "score",      "instructions": "If this call is a mistake, how hard is the damage to put right?",      "criteria": [        "Easy",        "Takes some work",        "Hard",        "Impossible"      ]    },    "within_job": {      "type": "noul",      "instructions": "Is this call part of the agent's job as described?"    }  }}JSON # Act when every answer is at least 0.70 sure; otherwise a person approves the call.jq -r '.answers as $a  | $a | map_values(if .type == "noul" then [.noul, 1 - .noul] | max else .confidence end)  | to_entries | min_by(.value) as $weakest  | if $weakest.value >= 0.70    then "act: \($a.action.choice) (damage \($a.damage.score) of 3)"    else "human: \($weakest.key) is only \($weakest.value * 100 | round / 100) sure" end' answer.json

The borderline case runs the same code with its own state. damage is only 0.23 sure, under 0.70, so it prints a human line and a person decides. Change the threshold to match what a wrong answer costs you.

Run it in the playground, with a threshold slider

Fan-out: many questions, one reading

A request reads the state once and answers every question from that reading, so a checklist of ten yes/no checks costs about the same as one. Build the questions from data and send them together.

from typesafe_sdk import Noul, TypeSafeClient # Reads TYPESAFE_BASE_URL (https://api.vakyn.com) and TYPESAFE_API_KEY from the environment.client = TypeSafeClient()CHECKS = {    "has_total": "Does the invoice state a total amount?",    "has_due_date": "Does the invoice state a payment due date?",    "has_vat_number": "Does the invoice show the supplier's VAT number?",    "mentions_late_fee": "Does the invoice mention a fee for late payment?",} invoice = {    "supplier": "Northwind Paper Co.",    "lines": [{"item": "A4 paper, 40 boxes", "amount": "1,180.00 EUR"}],    "text": "Total due: 1,180.00 EUR. Payment within 30 days of the invoice date.",} # One request, one reading of the invoice, every check answered.result = client.system_one(    state=invoice,    questions={name: Noul(instructions=q) for name, q in CHECKS.items()},)missing = [name for name, a in result.nouls.items() if a.noul < 0.5]print("missing:", missing)

The same idea works across types: a noul for "is this a complaint", a choice for the product and a score for severity, all in one call. Keep each question about one thing; the request may hold as many as fit in the token limits.

Example: eight job applications in one request

Eight applications for one role in one state, and ten questions built from the data: a priority score for each application, who to interview first and whether any application shows a red flag. The How VAKYN works page walks through it.

Questionspriority_1scorepriority_2scorepriority_3scorepriority_4scorepriority_5scorepriority_6scorepriority_7scorepriority_8scorebestchoicered_flagsnoul
  • Clear case

    Acts automatically
    { "role": { "title": "Night shift lead, distribution center in Rotterdam", "must_have": [ "Two years or more leading a warehouse team", "A valid forklift certificate", "Can work nights, Sunday to Thursday" ], "nice_to_have": [ "Dutch and English", "Has used a warehouse management system" ] }, "applications": [ { "application": 1, "name": "Joost Verhagen", "summary": "Four years as night shift lead at a grocery distribution center, team of 14. Forklift certificate valid to 2028. Wants to stay on nights. Dutch and English. Daily work in a warehouse management system." }, { "application": 2, "name": "Mila Petrova", "summary": "Recent graduate in logistics, no work experience in a warehouse yet. No forklift certificate. Prefers day shifts." }, { "application": 3, "name": "Sam O'Brien", "summary": "Barista for three years. No warehouse or forklift experience. Available weekends only." }, { "application": 4, "name": "Fatima Zahra El Idrissi", "summary": "Office manager for six years. No warehouse experience, no forklift certificate. Looking for a day job close to home." }, { "application": 5, "name": "Kees de Wit", "summary": "Retired in 2025 after thirty years as a truck driver. No team lead experience. Wants two days a week." }, { "application": 6, "name": "Lucas Moreau", "summary": "Software developer looking for a career change into tech sales. Never worked in a warehouse." }, { "application": 7, "name": "Hanna Kowalski", "summary": "Student, wants a summer job. No experience and no forklift certificate. Available in July and August only." }, { "application": 8, "name": "Ravi Menon", "summary": "Chef for ten years in restaurants. No warehouse experience. Cannot work nights." } ] }
    QuestionAnswerConfidence
    priority_11.98 of 20.97
    priority_20.00 of 21.00
    priority_30.00 of 21.00
    priority_40.00 of 21.00
    priority_50.01 of 20.99
    priority_60.00 of 21.00
    priority_70.00 of 21.00
    priority_80.00 of 21.00
    bestapplication_10.96
    red_flagsyes 0.090.91

    Every answer is at least 0.70 sure (lowest: red_flags 0.91), so your code acts on it.

  • Borderline case

    Goes to a person
    { "role": { "title": "Night shift lead, distribution center in Rotterdam", "must_have": [ "Two years or more leading a warehouse team", "A valid forklift certificate", "Can work nights, Sunday to Thursday" ], "nice_to_have": [ "Dutch and English", "Has used a warehouse management system" ] }, "applications": [ { "application": 1, "name": "Anouk Jansen", "summary": "Three years as shift lead in a parcel hub, team of 9, mostly evenings. Forklift certificate valid to 2027. Open to nights. Dutch and English." }, { "application": 2, "name": "Daniel Mensah", "summary": "Five years as warehouse team lead on day shifts, team of 20. Forklift certificate expired in 2025, says he will renew it. Can work nights. English only." }, { "application": 3, "name": "Eva Lindgren", "summary": "Two years leading a night team of 6 in a cold store. Forklift certificate valid. Uses a warehouse management system daily. English and some Dutch." }, { "application": 4, "name": "Tom Bakker", "summary": "Forklift driver for seven years, no team lead role. Certificate valid. Night shifts now. Dutch." }, { "application": 5, "name": "Yusuf Demir", "summary": "Says he led a team of 30 for eight years; his dates show four years in total at two companies. Forklift certificate attached." }, { "application": 6, "name": "Sofia Russo", "summary": "Two years as assistant shift lead at a fashion warehouse. Forklift certificate valid. Prefers nights. English and Italian." }, { "application": 7, "name": "Bram Visser", "summary": "Nine years as a warehouse supervisor, then three years out of work for family reasons. Certificate valid to 2026. Can work nights." }, { "application": 8, "name": "Lena Hofmann", "summary": "Store manager in retail for five years, team of 12. No forklift certificate. Can work nights." } ] }
    QuestionAnswerConfidence
    priority_11.85 of 20.77
    priority_20.29 of 20.56
    priority_31.81 of 20.71
    priority_40.05 of 20.93
    priority_50.77 of 20.27
    priority_60.12 of 20.82
    priority_71.00 of 20.49
    priority_80.01 of 20.99
    bestapplication_30.49
    red_flagsyes 0.930.93

    priority_5 is only 0.27 sure, under 0.70, so a person decides.

Real answers from VAKYN-4B Q8_0. The rule: act when every answer is at least 0.70 sure; a noul's confidence is how far it leans either way.
curl -s "https://api.vakyn.com/v1/systemone" \  -H "Authorization: Bearer $TYPESAFE_API_KEY" \  -H "Content-Type: application/json" \  -o answer.json \  -d @- <<'JSON'{  "model": "jev-latest",  "state": {    "role": {      "title": "Night shift lead, distribution center in Rotterdam",      "must_have": [        "Two years or more leading a warehouse team",        "A valid forklift certificate",        "Can work nights, Sunday to Thursday"      ],      "nice_to_have": [        "Dutch and English",        "Has used a warehouse management system"      ]    },    "applications": [      {        "application": 1,        "name": "Joost Verhagen",        "summary": "Four years as night shift lead at a grocery distribution center, team of 14. Forklift certificate valid to 2028. Wants to stay on nights. Dutch and English. Daily work in a warehouse management system."      },      {        "application": 2,        "name": "Mila Petrova",        "summary": "Recent graduate in logistics, no work experience in a warehouse yet. No forklift certificate. Prefers day shifts."      },      {        "application": 3,        "name": "Sam O'Brien",        "summary": "Barista for three years. No warehouse or forklift experience. Available weekends only."      },      {        "application": 4,        "name": "Fatima Zahra El Idrissi",        "summary": "Office manager for six years. No warehouse experience, no forklift certificate. Looking for a day job close to home."      },      {        "application": 5,        "name": "Kees de Wit",        "summary": "Retired in 2025 after thirty years as a truck driver. No team lead experience. Wants two days a week."      },      {        "application": 6,        "name": "Lucas Moreau",        "summary": "Software developer looking for a career change into tech sales. Never worked in a warehouse."      },      {        "application": 7,        "name": "Hanna Kowalski",        "summary": "Student, wants a summer job. No experience and no forklift certificate. Available in July and August only."      },      {        "application": 8,        "name": "Ravi Menon",        "summary": "Chef for ten years in restaurants. No warehouse experience. Cannot work nights."      }    ]  },  "questions": {    "priority_1": {      "type": "score",      "instructions": "How high should application 1 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "priority_2": {      "type": "score",      "instructions": "How high should application 2 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "priority_3": {      "type": "score",      "instructions": "How high should application 3 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "priority_4": {      "type": "score",      "instructions": "How high should application 4 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "priority_5": {      "type": "score",      "instructions": "How high should application 5 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "priority_6": {      "type": "score",      "instructions": "How high should application 6 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "priority_7": {      "type": "score",      "instructions": "How high should application 7 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "priority_8": {      "type": "score",      "instructions": "How high should application 8 go on the interview list?",      "criteria": [        "Do not interview",        "Interview if needed",        "Interview first"      ]    },    "best": {      "type": "choice",      "instructions": "Which application should we interview first?",      "criteria": {        "application_1": null,        "application_2": null,        "application_3": null,        "application_4": null,        "application_5": null,        "application_6": null,        "application_7": null,        "application_8": null      }    },    "red_flags": {      "type": "noul",      "instructions": "Does any application show a red flag, such as claims that contradict each other or a certificate that looks made up?"    }  }}JSON # Act when every answer is at least 0.70 sure; otherwise a person decides.jq -r '.answers as $a  | $a | map_values(if .type == "noul" then [.noul, 1 - .noul] | max else .confidence end)  | to_entries | min_by(.value) as $weakest  | if $weakest.value >= 0.70    then "act: interview \($a.best.choice) first, red flags \(if $a.red_flags.noul >= 0.5 then "yes" else "no" end), "      + "priorities \([range(1; 9) as $i | $a["priority_\($i)"].score | round] | map(tostring) | join(" "))"    else "human: \($weakest.key) is only \($weakest.value * 100 | round / 100) sure" end' answer.json

The borderline case runs the same code with its own state. priority_5 is only 0.27 sure, under 0.70, so it prints a human line and a person decides. Change the threshold to match what a wrong answer costs you.

Run it in the playground, with a threshold slider

Composite scores

When a decision depends on several criteria, ask one score per criterion and combine them in your code with weights you control. You can explain the result ("strong on budget, weak on timing") and change the weights without touching the questions.

from typesafe_sdk import Score, TypeSafeClient # Reads TYPESAFE_BASE_URL (https://api.vakyn.com) and TYPESAFE_API_KEY from the environment.client = TypeSafeClient()LEVELS = ["Not at all", "Partly", "Clearly"]WEIGHTS = {"budget": 0.4, "timeline": 0.3, "decision_maker": 0.3} lead = (    "Hi, I run operations at a 40-person logistics firm. We set aside budget for a scheduling "    "tool this quarter and want something live before the summer peak. I'll loop in our CFO.")result = client.system_one(    state=lead,    questions={        "budget": Score(instructions="Does the lead have budget for a purchase?", criteria=LEVELS),        "timeline": Score(instructions="Does the lead need a solution soon?", criteria=LEVELS),        "decision_maker": Score(instructions="Is the writer able to approve the purchase?", criteria=LEVELS),    },)# Each score runs from 0 to 2; normalise to 0-1 and weigh.fit = sum(WEIGHTS[k] * result.scores[k].score / 2 for k in WEIGHTS)print(f"fit: {fit:.2f}")

Use the expected score rather than the most likely level: it moves smoothly as the evidence changes, which makes the combined number easier to rank and to threshold.

Long documents

A request may hold up to 32,768 tokens of state plus its longest question, and 65,536 tokens overall. The server never cuts a document short: over the limit it answers 400 with max_tokens_exceeded. For longer documents, split them and combine the answers. For a question like "does any part say X?", the document says X unless every part says it does not:

from typesafe_sdk import Noul, TypeSafeBadRequestError, TypeSafeClient # Reads TYPESAFE_BASE_URL (https://api.vakyn.com) and TYPESAFE_API_KEY from the environment.client = TypeSafeClient()QUESTION = {"auto_renews": Noul(instructions="Does this text say the contract renews automatically?")}  def ask(text: str) -> float:    try:        return client.system_one(state=text, questions=QUESTION).nouls["auto_renews"].noul    except TypeSafeBadRequestError as e:        if "max_tokens_exceeded" not in str(e.body):            raise        # Over the limit: split on paragraphs and ask each half.        paragraphs = text.split("\n\n")        if len(paragraphs) < 2:            raise        mid = len(paragraphs) // 2        halves = ["\n\n".join(paragraphs[:mid]), "\n\n".join(paragraphs[mid:])]        # "Does any part say so?": yes unless every part says no.        p_no = 1.0        for half in halves:            p_no *= 1 - ask(half)        return 1 - p_no  contract = "\n\n".join(f"Clause {i}. The parties agree to clause {i}." for i in range(1, 6000))print(f"{ask(contract):.2f}")

Many requests at once

A server admits a fixed number of requests at once, counting those running and those waiting their turn; beyond that it answers 429 with a retry-after header, and both SDKs wait and retry on their own. On open-server the number is --max-queue (32 by default). Send work concurrently and let the queue absorb it:

import asyncio from typesafe_sdk import AsyncTypeSafeClient, Noul MESSAGES = [    "Where is my parcel?",    "Please cancel my subscription at the end of the month.",    "Your app crashes when I open settings.",]  async def main() -> None:    # Reads TYPESAFE_BASE_URL (https://api.vakyn.com) and TYPESAFE_API_KEY from the environment.    async with AsyncTypeSafeClient() as client:        # The server queues what it can take and answers 429 beyond that;        # the SDK retries 429 after the delay the server asks for.        results = await asyncio.gather(*(            client.system_one(state=m, questions={"cancel": Noul(instructions="Does the customer want to cancel?")})            for m in MESSAGES        ))    for m, r in zip(MESSAGES, results):        print(f"{r.nouls['cancel'].noul:.2f}  {m}")  asyncio.run(main())

Loading the docs…