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