← Use cases · Download & Install · Agent setup guide

Calibrated decision gates with vision

Use case #8 · every command below was run as shown on Apple M4 CPU (release build), strands-decider-2b f16 — 2026-10-05 — timings are from those runs.

calibrated probabilitiesnoul · choice · score~1.8 s text · ~8 s vision2B on CPU

Gate automated actions on probabilities you can actually trust: strands-decider-2b answers typed questions — true/false, pick-one, ordered score — over text, JSON records, and screenshots, with calibrated confidence numbers, entirely on-box via POST /v1/systemone.

Why run it locally: Every automation eventually hits the question "is the model sure enough to act?". Generic LLM probabilities (including our own /v1/score) are relative preferences — fine for picking, misleading for gating. strands-decider-2b is trained and shipped with per-type calibration: its published calibration error is 0.052 on text (0.014 on vision benchmarks), and on the public JevBench set served from THIS server it scored 72.29% — identical to the reference GPU stack — with a measured calibration error of 0.054. That makes a threshold policy meaningful: act above 0.9, confirm between 0.5 and 0.9, hand to a human below 0.5.

Model & hardware fit: strands-decider-2b (strands fetch group) — a dedicated decision model, not a chat model. 8 GB RAM for f16, 4 GB for the Q8 variant (text-only); vision uses the bundled mmproj. CPU is enough.

Setup (once, ~5 minutes)

1. The server — any install works; one line on Linux:

curl -fsSL https://inference-server.searchblox.com/install | sudo bash

2. The decision model — fetch the strands group (f16 + Q8 + vision mmproj, ~6.2 GB):

sudo ./fetch-models.sh strands

strands-decider-2b is not a chat model — its only job is POST /v1/systemone. Full background in the calibrated-decisions article.

The walkthrough

A support message comes in; ask a typed true/false ("noul") question (~1.8 s measured, deterministic across runs):

curl -s http://127.0.0.1:8081/v1/systemone \
  -H "Authorization: Bearer $SEARCHAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"strands-decider-2b",
       "state":"Help! My payouts have been failing for 3 days!",
       "questions":{"is_urgent":{"type":"noul",
                                 "instructions":"Does this convey urgency?"}}}'
# → {"answers":{"is_urgent":{"type":"noul","noul":0.835}}, ...}

0.835 lands in the 0.5–0.9 band → fast-track it, but have a human confirm. Ask several questions over one state in one request — the state is read once and the cache forked per question, so the extras are nearly free (three answers in ~4.5 s measured):

curl -s http://127.0.0.1:8081/v1/systemone \
  -H "Authorization: Bearer $SEARCHAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"model":"strands-decider-2b",
       "state":"Hi team, just checking in on the invoice from March — no rush at all, whenever you get a chance.",
       "questions":{
         "is_urgent":{"type":"noul","instructions":"Does this convey urgency?"},
         "team":{"type":"choice","instructions":"Which team should handle this?",
                 "criteria":{"billing":"payments, invoices, payouts",
                             "technical":"bugs, outages, API errors",
                             "sales":"pricing and upgrades"}},
         "mood":{"type":"score","instructions":"How frustrated is the writer?",
                 "criteria":["calm","frustrated","desperate"]}}}'
# → is_urgent 0.1785 · team "billing" at 0.9524 · mood 0.51 (calm-leaning)

"No rush" scores 0.18 urgency; the routing choice is 0.95 → auto-route to billing, no confirmation needed. And the same gate reads screenshots: attach an image and ask the question (~8 s measured; this run used a checkout screen showing a full-page error):

IMG=$(base64 -w0 checkout-result.png)   # macOS: base64 -i checkout-result.png
curl -s http://127.0.0.1:8081/v1/systemone \
  -H "Authorization: Bearer $SEARCHAI_API_KEY" -H "Content-Type: application/json" \
  -d "{\"model\":\"strands-decider-2b\",
       \"state\":\"The screenshot shows the final screen of a payment attempt.\",
       \"images\":[\"$IMG\"],
       \"questions\":{\"payment_ok\":{\"type\":\"noul\",
                       \"instructions\":\"Did the payment succeed?\"}}}"
# → {"answers":{"payment_ok":{"type":"noul","noul":0.2124}}}  — don't mark it paid

The threshold policy is then three lines of glue:

P=$(... | jq .answers.payment_ok.noul)
if   awk "BEGIN{exit !($P>=0.9)}";  then mark_paid
elif awk "BEGIN{exit !($P>=0.5)}";  then queue_for_confirmation
else                                      escalate_to_human; fi

Honest notes

strands-decider-2b is a decision model only — free-text generation from it is deliberately meaningless; pair it with a chat model for drafting (the decide-then-generate pattern). The published numbers are what they are: 72.29% on JevBench (167/231, a statistical tie with the reference GPU stack — not a win), calibration error 0.052 text / 0.014 vision published, 0.054 measured on our serving stack. Calibrated ≈ the confidence numbers are honest, not that every answer is right — hard multi-hop and long-policy questions still miss; that is exactly what the <0.5 → human band is for. Vision accepts ≤4 images, ≤4096×4096, via the f16 model + mmproj (the Q8 variant serves text-only).

Measured: ~1.8 s per text decision · ~4.5 s for three questions on one state · ~8 s with a screenshot attached (Apple M4 CPU, strands-decider-2b f16).

Next: all the use cases · give the agent memory across sessions · 3–4× the speed with one GPU flag