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
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 cityReference 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.