DEV Community

Cover image for Strands Decider 2B: AWS's open source model that doesn't generate text, it decides
Matias Martinez
Matias Martinez

Posted on Originally published at blog.cloudacademy.ar

Strands Decider 2B: AWS's open source model that doesn't generate text, it decides

A lot of the LLM calls inside an agent aren't about generating text. They're tiny decisions: which team owns this ticket? Did the user actually say that tool argument, or did the model make it up? Is this message urgent? Cheap model or expensive model? For each one, we ship the full context to a frontier model, wait a second or two, and parse free text that comes with no measure of how confident the model is.

Strands Labs, the experimental arm of the Strands Agents team at AWS, released Strands Decider 2B, an Apache 2.0 model built for exactly that gap. It doesn't generate text. It takes a state plus a set of questions and, for each one, picks from closed options and returns a calibrated probability. It runs locally, answers in about 115 ms median on a desktop GPU, and plugs into Strands Agents' intervention system to review tool calls before they execute.

TL;DR

  • A decision model (the authors call it a "system one" model): picks options, answers yes/no, or scores on a scale, always with a probability. No text generation.
  • Architecture: the Qwen3.5-2B-Base torso with its LM head removed, a ~1M-parameter pointer head, and rank-16 LoRA adapters. One forward pass, no decoding loop.
  • First release latency: 115 ms median (299 ms p95) on an RTX 3090; 153 ms on an M3 Pro.
  • On JevBench (231 public tasks) the current reference model gets 180 right (0.779). Gemma 4 variants reach 207 with the 26B model.
  • pip install strands-decider, then use it from the CLI or as a local HTTP server (/v1/systemone).
  • It's an experiment, not a managed AWS service. You run it.

Why now

The "decision model" idea got attention in September when TypeSafe AI came out of stealth with Jev, a proprietary model that returns typed decisions instead of text, available via waitlist at $0.042 per million input tokens. Strands credits Jev for putting the approach on the map and evaluates against JevBench. The difference is distribution: Jev is closed; Strands Decider ships weights, code and the full training recipe.

There's also a design argument. Ask a chat model "are your own tool arguments grounded?" and it can be talked into yes. A classifier that returns a number your if statement branches on doesn't negotiate.

The model classifies, your code decides

How it works

The decider keeps the transformer torso of Qwen3.5-2B-Base, throws away the vocabulary head, and replaces it with a pointer head. The input has a fixed format: state, question, numbered options, and an <answer> marker. The head scores each option's final hidden state against the hidden state at <answer>, a softmax turns those logits into probabilities, done. No tokens generated.

Strands Decider 2B architecture. Image: Strands Agents blog (AWS)

Two practical consequences: the head has no per-option parameters, so it can't learn "option 1 is usually right" and you can rename options without retraining; and only the LoRA adapters and the head are trained, which is why the repo can publish the recipe (about 11 hours on one RTX 3090, or ~70 minutes on eight H100s).

Three question types are supported: noul (yes/no, returns P(yes)), choice (one of N), and score (a level on an ordered rubric). You can ask many questions about one state in a single request; the state is encoded once.

Model JevBench (of 231) Brier (lower is better)
2B hobson-v19 (launch) 167 0.342
2B hobson-v21 176 0.323
2B qwen3.5-v1-2610 (reference) 180 0.280
E4B gemma4-v1-2610 187 0.243
26B-A4B gemma4-v1-2610 207 0.162

The README itself warns that gaps under ~10 tasks between single runs aren't reliable. For v19, Techstrong.ai reports 100% on easy tasks and 50.5% on hard ones: great at routine, mediocre at complex, which is the pitch.

Accuracy vs. Brier score across training versions. Image: Strands Agents blog (AWS)

Latency vs. prompt length on an RTX 3090. Image: Strands Agents blog (AWS)

Hands-on

pip install strands-decider

strands-decider ask StrandsAgents/strands-decider-2B-qwen3.5-v1-2610 \
  --state "Help! My payouts have been failing for 3 days! " \
  --choice "Which team should handle this?=billing,sales,retail" \
  --noul "Does this convey urgency?" \
  --score "How frustrated is the writer?=calm,frustrated,depressed"
Enter fullscreen mode Exit fullscreen mode

Sample output from the README (CPU run): billing at 0.773, urgency 0.858, and a frustration score of 1.03 with confidence 0.509. That last number is the model telling you it isn't sure, which is information an LLM doesn't give you.

As a server:

strands-decider serve StrandsAgents/strands-decider-2B-qwen3.5-v1-2610 --port 8000
curl -s localhost:8000/v1/systemone -H 'content-type: application/json' \
  -d '{"state": "Help! My payouts have been failing for 3 days!",
       "questions": {"is_urgent": {"type": "noul", "instructions": "Does this convey urgency?"}}}'
Enter fullscreen mode Exit fullscreen mode

The server binds to 127.0.0.1 and has no authentication. Don't put it behind a public Service.

Gating tool calls in a Strands agent

This is a trimmed version of examples/strands/ from the repo. The agent has an eager system prompt that makes it guess a city; the decider catches it:

from strands import Agent, tool
from strands.interventions import Guide, InterventionHandler, Proceed

YES = 0.45  # illustrative threshold; tune it on your own traffic

class ToolCallReviewer(InterventionHandler):
    name = "strands-decider-tool-call-reviewer"

    def __init__(self, decider):
        self._decider = decider

    def before_tool_call(self, event, **kwargs):
        state = build_state(event.agent.messages, event.tool_use)  # conversation + proposed call
        answers = self._decider.ask(state, QUESTIONS)  # args_grounded, premature (noul)
        if answers["args_grounded"]["noul"] < YES:
            return Guide(feedback="The arguments look guessed. Ask the user to confirm them.")
        if answers["premature"]["noul"] >= YES:
            return Guide(feedback="Too early to call this tool. Clarify with the user first.")
        return Proceed()
Enter fullscreen mode Exit fullscreen mode

Guide doesn't block the call; it hands the turn back to the model with feedback, and the agent ends up asking "which city?" instead of reporting Seattle's weather. Strands interventions also support Deny and Confirm (human approval).

Running it on Amazon EKS

Because the server only listens on localhost, the natural Kubernetes pattern is a sidecar in the agent's pod. A native sidecar (an init container with restartPolicy: Always) starts before the agent and lives as long as the pod. Two gotchas: there's no published container image, so build your own and bake the weights in; and httpGet probes will always fail because the kubelet hits the pod IP, not 127.0.0.1, so use an exec probe against http://127.0.0.1:<port>/health. A g6-family node (NVIDIA L4, 24 GB) is plenty for the 2B model.

Decider as a sidecar on Amazon EKS

Cost

The model is free; you pay for hardware. A g6.xlarge is about $0.805/hour on demand in us-east-1, roughly $588/month always on. Back-of-the-envelope (mine): ~8 sequential decisions/second at ~115 ms is on the order of 20M decisions a month; at 500 tokens each, that volume on a per-token service like Jev would be around $450. Self-hosting wins with sustained volume or privacy requirements; at low volume an idle GPU costs more. Start on CPU, share the GPU, and use the decider as a first-pass filter that escalates low-confidence cases to the LLM.

Limitations worth knowing

Per the model card: rephrased questions often get the same answer, long multi-step documents are the weak spot, score and noul transfer poorly to unfamiliar rubrics, and calibration was fitted on short classification, so validate thresholds on your own traffic. Training data and examples are English.

My take

The benchmark isn't the point (231 tasks is noisy). The point is the separation: the model classifies and returns a number, your code decides. That turns decisions hidden inside prompts into something you can test, version and audit with explicit thresholds. It's the same logic platform teams apply with Kubernetes admission policies: a deterministic checkpoint before something runs. It's not production-ready, and the official example says its thresholds are illustrative. But I'd run it as a tool-call guardrail for an internal agent, in a pod with a shared GPU, and compare its decisions against what your LLM decides today.

Originally published in Spanish on CloudAcademy.ar, with the full Kubernetes manifest and the complete intervention example.

Resources

Top comments (0)