strands-decider
5 TagsUpdated Apache-2.0by Strands Agents
Strands Agents' decision model: a LoRA and a pointer head on Qwen3.5-2B base or on Gemma 4 E2B / E4B. Each question is one row with a line per option, every option scored at the end of its own line, and the probabilities are calibrated with the authors' temperature for each question type. Up to 255 options.
ollaya run strands-decider --preset triage "I was charged twice for my subscription this month and want a refund."curl http://localhost:11435/api/decide \
-H "Content-Type: application/json" \
-d '{
"model": "strands-decider",
"state": "I was charged twice for my subscription this month and want a refund.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoices and refunds",
"technical": "Bugs, errors and outages",
"account": "Login, profile and settings"
}
},
"refund": {
"type": "noul",
"instructions": "Is the customer asking for a refund?"
}
}
}'# Already using a TypeSafe SDK? Set TYPESAFE_BASE_URL=http://localhost:11435 instead.
import requests
response = requests.post(
"http://localhost:11435/api/decide",
json={
"model": "strands-decider",
"state": "I was charged twice for my subscription this month and want a refund.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoices and refunds",
"technical": "Bugs, errors and outages",
"account": "Login, profile and settings"
}
},
"refund": {
"type": "noul",
"instructions": "Is the customer asking for a refund?"
}
}
},
)
answers = response.json()["answers"]
print(answers["department"]["choice"], answers["refund"]["noul"])const response = await fetch("http://localhost:11435/api/decide", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "strands-decider",
state: "I was charged twice for my subscription this month and want a refund.",
questions: {
department: {
type: "choice",
instructions: "Which team should handle this?",
criteria: {
billing: "Payments, invoices and refunds",
technical: "Bugs, errors and outages",
account: "Login, profile and settings"
}
},
refund: {
type: "noul",
instructions: "Is the customer asking for a refund?"
}
}
}),
});
const { answers } = await response.json();
console.log(answers.department.choice, answers.refund.noul);Models
Every tag| Name | Accuracy | Latency | |||
|---|---|---|---|---|---|
| Most accurateHighest typed-decisions accuracy of the family. | |||||
| strands-decider:e4bstrands-decider-E4B-gemma4-v1-2610 (LoRA and pointer head on Gemma 4 E4B, instruction-tuned), the largest Strands Decider here: 187 / 231 JevBench tasks upstream; about 20 GB in memory. | 0.699 | 213 ms | |||
| FasterFastest first; accuracy as measured on the bench. | |||||
| strands-decider:2b-fp16Strands Decider v1 (2610): a LoRA and a pointer head on Qwen3.5-2B base. Each question is one row, every option scored at the end of its own line, calibrated per question type. Up to 255 options and 10 score levels, 4,096 tokens per row. Needs about 8 GB of memory (weights widened to fp32 at load). This tag computes in fp16 on NVIDIA GPUs: faster than strands-decider:2b, with at least 99.5 % of its decisions the same. On a CPU it runs, slowly; use strands-decider:2b there. | 0.615 | 72 ms | |||
| strands-decider:2bstrands-decider-2B-qwen3.5-v1-2610 (LoRA and pointer head on Qwen3.5-2B base), about 8 GB in memory. | 0.616 | 98 ms | |||
| strands-deciderlatestSame as strands-decider:2b. | 0.616 | 98 ms | |||
| strands-decider:e2bstrands-decider-E2B-gemma4-v1-2610 (LoRA and pointer head on Gemma 4 E2B, instruction-tuned): 180 / 231 JevBench tasks upstream; about 11 GB in memory. | 0.646 | 116 ms | |||
Accuracy: typed-decisions, argmax against the majority label on 400 states (left out for a tag trained on that data). Latency: the median five-question request on an RTX 5090, Ollaya Bench.
Readme
Strands Decider is a decision model by Strands Agents: a LoRA adapter on a base decoder (Qwen3.5-2B base, or Gemma 4 E2B and E4B) plus a small pointer head, released under Apache-2.0 with its code at strands-labs/strands-decider. For each question it lays out the state, the question and one line per option, then scores every option at the end of its own line against the question, in one forward pass. It never generates text.
Models
| Tag | Base | Checkpoint | Params | JevBench public (upstream) |
|---|---|---|---|---|
strands-decider:latest, strands-decider:2b | Qwen3.5-2B base | v1, 2026-10 (strands-decider-2B-qwen3.5-v1-2610) | 2B | 180 / 231 tasks, ECE 0.072 |
strands-decider:e2b | Gemma 4 E2B (instruction-tuned) | v1, 2026-10-09 (strands-decider-E2B-gemma4-v1-2610) | 4.6B weights read (effective 2B) | 180 / 231 tasks, ECE 0.053 |
strands-decider:e4b | Gemma 4 E4B (instruction-tuned) | v1, 2026-10-09 (strands-decider-E4B-gemma4-v1-2610) | 8.9B weights read (effective 4B) | 187 / 231 tasks, ECE 0.037 |
The JevBench numbers are from the model cards, served at a 4,096-token window. Half of a Gemma 4 model's weights are its two embedding tables, which the model only looks up, hence "effective" sizes. Measured by Ollaya on an RTX 5090: 0.745 macro accuracy on Bespoke Labs' public benchmark (3,880 questions, pooled ECE 0.039, 133 ms per question) and 0.616 on the 400 typed-decisions states.
Usage
ollaya run strands-decider --preset triage "My order never arrived and support ignores me. Refund me today or I'm switching to your competitor."Point any TypeSafe client at http://localhost:11435 and set the model to strands-decider.
Speed
- RTX 5090, through the HTTP API (Ollaya Bench): five-question triage requests take 98 ms at the median.
- State length: every question reads the whole state again, so the cost grows with the number of questions times the state length.
How it works
- One row per question. The state comes first, then the question with a header for its type and one numbered line per option, then
<answer>. The pointer head compares the last token of each option's line with the last token of the row. - Calibrated. The scores are divided by the temperature the authors fitted for each question type: 0.86 / 1.83 / 1.07 (choice / score / noul) for
2b, 0.56 / 1.48 / 1.07 fore2b, 0.77 / 0.86 / 0.59 fore4b. - Weights. From Hugging Face, pinned to a commit and verified by sha256: the base from Qwen or Google, the adapter and
head.safetensorsfrom Strands Agents. The adapter is applied at run time, not merged; Ollaya hosts only the ONNX graph (10 MB). The Gemma 4 tags read only the text decoder of the checkpoint (its vision and audio towers stay untouched) and keep the weights in memory as BF16, about 11 GB fore2band 20 GB fore4b. - Parity. Ollaya's Rust runtime matches upstream
strands_deciderin fp32: identical token rows and option positions, the same rejections and the same decision on all 650 test questions, probabilities within 2.8e-6 for2b; for the Gemma tags the same 650 decisions, probabilities within 1.2e-5 fore2band 5.2e-6 fore4b. - fp16 tag.
strands-decider:2b-fp16runs the trunk in fp16 on NVIDIA GPUs (how): 72 ms for five questions on the RTX 5090 instead of 98 ms, 649 of 650 decisions the same asstrands-decider:2bon the parity set, and 0.615 on typed decisions. On a CPU usestrands-decider:2b.
Limits
- Choice criteria. Give
choicecriteria as an object of 2 to 255 options ({"refund": null, ...}). A plain list is rejected, as upstream does. Scores take 2 to 10 levels. - Context. A row is fitted to 4,096 tokens: a long question keeps its end, a long state its beginning. A request whose cut would remove an option is rejected.
- Not ported. Images (upstream's
--visionpath); the 12B and 26B-A4B Gemma 4 variants; fp16 tags of the Gemma 4 models.
Badge
For the model card or a README. Day-0 support for a new model
[](https://ollaya.dev/library/strands-decider)