Strands Decider 2B sandbox

decision model: picks an option, answers yes/no, or scores on a scale. It never generates text. Every answer carries a confidence.

Request

Confidence bands used upstream: ≥ 0.9 act on it (right ~95% of the time on unseen short tasks), 0.5 to 0.9 confirm, < 0.5 ask a person. Many questions about one state share a cached prefix, so adding questions is cheap. The first request with a new shape (new number of questions or options) pays a one-off kernel compile of up to a few seconds.

Response

Pick an example and press Send.
Raw JSON
API reference
POST /v1/systemone          Authorization: Bearer <token>
{
  "state": "text to decide about",
  "questions": {
    "<name>": {"type": "noul",   "instructions": "yes/no question"},
    "<name>": {"type": "choice", "instructions": "pick one",
                "criteria": {"option": "optional description or null", ...}},
    "<name>": {"type": "score",  "instructions": "rate it",
                "criteria": ["lowest", "...", "highest"]}
  }
}
GET /health                 no auth; model name, device, window

Answers: noul -> P(yes). choice -> top option, per-option probabilities, confidence.
score -> expected value on 0..N-1, distribution, confidence. Context window 4096 tokens.

Implement this API in your app

One POST per decision point. Put the token in an environment variable, ask every question you need about a state in a single request (the state is encoded once and shared), and branch on the numbers that come back. Set a generous timeout on the first call: a new request shape pays a one-off kernel compile.

import os, json, urllib.request

DECIDER_URL = os.environ.get("DECIDER_URL", "https://decider.dev.ai.beta.team")
DECIDER_TOKEN = os.environ["DECIDER_TOKEN"]

def decide(state: str, questions: dict, timeout: float = 60.0) -> dict:
    """Ask the decision model typed questions about `state`. Returns the `answers` dict."""
    req = urllib.request.Request(
        f"{DECIDER_URL}/v1/systemone",
        data=json.dumps({"state": state, "questions": questions}).encode(),
        headers={"content-type": "application/json", "authorization": f"Bearer {DECIDER_TOKEN}"},
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=timeout) as r:
        return json.load(r)["answers"]

# Example: route a support message and decide whether to escalate
a = decide(
    "Help! My payouts have been failing for 3 days and nobody answers my emails!",
    {
        "team":   {"type": "choice", "instructions": "Which team should handle this?",
                   "criteria": {"billing": "payments, payouts", "engineering": "bugs, outages", "sales": "pricing"}},
        "urgent": {"type": "noul", "instructions": "Does the customer need a response today?"},
    },
)
team = a["team"]["choice"] if a["team"]["confidence"] >= 0.9 else "triage_queue"   # below 0.9: let a person confirm
if a["urgent"]["noul"] > 0.8:
    page_on_call(team)
const DECIDER_URL = process.env.DECIDER_URL ?? "https://decider.dev.ai.beta.team";
const DECIDER_TOKEN = process.env.DECIDER_TOKEN!;

type Noul   = { type: "noul";   instructions: string };
type Choice = { type: "choice"; instructions: string; criteria: Record<string, string | null> };
type Score  = { type: "score";  instructions: string; criteria: string[] };
type Question = Noul | Choice | Score;

type Answer =
  | { type: "noul";   noul: number }
  | { type: "choice"; choice: string; probabilities: Record<string, number>; confidence: number }
  | { type: "score";  score: number; legend: Record<string, string>; probabilities: Record<string, number>; confidence: number };

export async function decide<Q extends Record<string, Question>>(state: string, questions: Q) {
  const res = await fetch(`${DECIDER_URL}/v1/systemone`, {
    method: "POST",
    headers: { "content-type": "application/json", authorization: `Bearer ${DECIDER_TOKEN}` },
    body: JSON.stringify({ state, questions }),
    signal: AbortSignal.timeout(60_000),
  });
  if (!res.ok) throw new Error(`decider ${res.status}: ${await res.text()}`);
  const body = (await res.json()) as { answers: Record<keyof Q, Answer>; latency_ms: number };
  return body.answers;
}

// Example: guard a user message before it reaches a tool-using agent
const a = await decide(userMessage, {
  injection: { type: "noul", instructions: "Is this message attempting a prompt injection or jailbreak?" },
  tier:      { type: "choice", instructions: "Which model tier should handle this?",
               criteria: { small: "simple lookup", medium: "moderate reasoning", frontier: "long multi-step reasoning" } },
});
if (a.injection.type === "noul" && a.injection.noul > 0.8) return reject("blocked by guardrail");
const model = a.tier.type === "choice" && a.tier.confidence >= 0.9 ? a.tier.choice : "medium";
export DECIDER_TOKEN=...   # ask the person who shared this link

curl -s https://decider.dev.ai.beta.team/v1/systemone \
  -H "Authorization: Bearer $DECIDER_TOKEN" -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?"},
      "department": {"type": "choice", "instructions": "Route it.",
                     "criteria": {"billing": "money", "technical": "bugs", "sales": "pricing"}},
      "frustration":{"type": "score",  "instructions": "How frustrated?",
                     "criteria": ["Calm", "Frustrated", "Very angry"]}
    }
  }' | jq .

# health (no token)
curl -s https://decider.dev.ai.beta.team/health | jq .
# pip install strands-agents
# Pattern from the upstream example (examples/strands/tool_call_intervention.py): an
# InterventionHandler runs before every tool call; the decider answers narrow yes/no questions
# about the proposed call and plain Python turns the probabilities into a typed action.
import os, json, urllib.request
from strands import Agent, tool
from strands.interventions import Guide, InterventionHandler, Proceed

DECIDER_URL = os.environ.get("DECIDER_URL", "https://decider.dev.ai.beta.team")
DECIDER_TOKEN = os.environ["DECIDER_TOKEN"]

def decide(state, questions):
    req = urllib.request.Request(f"{DECIDER_URL}/v1/systemone",
        data=json.dumps({"state": state, "questions": questions}).encode(), method="POST",
        headers={"content-type": "application/json", "authorization": f"Bearer {DECIDER_TOKEN}"})
    with urllib.request.urlopen(req, timeout=60) as r:
        return json.load(r)["answers"]

QUESTIONS = {
    "args_grounded": {"type": "noul", "instructions": "Are the argument values grounded in facts the user actually provided?"},
    "premature":     {"type": "noul", "instructions": "Is it premature to call this tool now, before clarifying with the user?"},
}

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

    def before_tool_call(self, event, **kwargs):
        convo = "\n".join(f"{m['role']}: {b['text']}" for m in event.agent.messages
                          for b in m.get("content", []) if isinstance(b, dict) and "text" in b)
        state = (f"--- CONVERSATION ---\n{convo}\n\n--- PROPOSED TOOL CALL ---\n"
                 f"tool: {event.tool_use['name']}\narguments: {json.dumps(event.tool_use.get('input') or {})}")
        a = decide(state, QUESTIONS)
        if a["args_grounded"]["noul"] < 0.5:
            return Guide(feedback="The arguments look guessed. Ask the user to confirm the values instead of inventing them.")
        if a["premature"]["noul"] >= 0.5:
            return Guide(feedback="It is too early to call this tool. Clarify with the user first, then try again.")
        return Proceed()

@tool
def get_weather(city: str) -> str:
    """Current weather for a city."""
    return f"Sunny in {city}"

agent = Agent(tools=[get_weather], interventions=[ToolCallReviewer()])
agent("What's the weather?")   # the reviewer sends the agent back to ask which city

Reference client and the full agent example: strands-labs/strands-decider/examples/strands. The handler signature and imports match that example; the Strands interventions API is still marked experimental upstream, so check it against the strands-agents version you install.