Skip to content

Decision Agents

A decision agent answers small, structured questions about text the model supplies. When the model calls the decision_agent tool, Polytoken sends the text plus up to sixteen named questions to your configured decision backend and returns one typed answer per question. The model decides when a judgment helps. You decide which backend Polytoken uses. When no backend is configured, the system prompt tells the model, so it will not attempt the tool and will let you know if you ask it to use one.

Decision calls are fast and cheap compared with a full model turn, so they suit high-frequency judgments: does this message convey urgency, which team should handle this ticket, how frustrated is the customer. Results are model estimates, not verified facts. Treat them as signals to reason over, never as ground truth.

Every question has a name the model chooses. Answers come back under the same names, so one call can carry several unrelated questions.

A yes/no condition over the input text, phrased as a statement.

{
"name": "wants_cancellation",
"type": "predicate",
"condition": "the customer is asking to cancel their subscription"
}

The answer carries a boolean value and, when the backend reports one, the probability behind it. When the backend returns only a probability, Polytoken derives the boolean at the 0.5 threshold and appends a disclosure warning to the result so the model knows the boolean was derived.

A pick-one decision among two or more named options, each with a short description of when it applies.

{
"name": "department",
"type": "choice",
"prompt": "which team should handle this message?",
"options": [
{ "name": "billing", "description": "payments, invoicing, refunds" },
{ "name": "technical", "description": "bugs, outages, integrations" },
{ "name": "sales", "description": "pricing, upgrades, new accounts" }
]
}

The answer names the selected option and includes the full probability distribution over every option, plus a confidence value when the backend reports one.

Backends report probabilities with different precision. Polytoken judges each backend against its own documented output precision. A strict backend must sum to 1 within 0.000001. The System One family reports four-decimal probabilities, so Polytoken lets a distribution from that family miss 1 by one rounding step per option. Either way, Polytoken renormalizes a distribution that lands inside its backend’s bound so it sums to one (with a warning), and the response keeps the raw probabilities the backend actually sent alongside the normalized ones, so you can always see both. Polytoken rejects a distribution outside the bound as a malformed response.

A rating on an ordered scale you define. Each level has a name, a description, and a numeric value. Levels must be listed in increasing value order.

{
"name": "frustration",
"type": "score",
"prompt": "how frustrated is the customer?",
"levels": [
{ "name": "calm", "description": "no frustration expressed", "value": 0 },
{ "name": "frustrated", "description": "clear annoyance", "value": 1 },
{ "name": "very_angry", "description": "explicit anger or threats to leave", "value": 2 }
]
}

The answer includes the selected level, the probability distribution over every level, and a probability-weighted value computed over your level values. That weighted value can land between levels. Polytoken recomputes it from the normalized reported probabilities and your level values, never from the backend’s own arithmetic, and appends a warning when it differs from the value the backend reported.

A successful call returns one JSON document: backend provenance (the configured instance, the backend family, and the concrete model that answered), one answer per question in request order, and a warnings list. Warnings disclose every transformation Polytoken applied: derived predicate booleans, distributions that needed renormalization to sum to one, and score values Polytoken recomputed. A backend can decline to answer a single question; that answer is a refusal, not an error, and the other answers are unaffected.

Failures return a short normalized code and message, such as rate_limited or authentication. Polytoken makes exactly one attempt per call: there are no retries and no fallback to another backend.

Decision backends live under integrations.decisions in your configuration file. You may configure several backends, and active names the single one that serves calls. Selection is exclusive: exactly one backend is active, and Polytoken never rotates or fails over between backends. This is a deliberate design choice, so a judgment always comes from the model you chose.

Polytoken supports two backend families in this release:

  • TypeSafe (type: typesafe): the Jev decision models at docs.typesafe.ai. Get an API key at typesafe.ai. model defaults to jev-latest, a rolling alias for the newest release; the result’s provenance reports the concrete version that answered.
integrations:
decisions:
active: primary
backends:
primary:
type: typesafe
key: ${TYPESAFE_API_KEY}
model: jev-latest
  • System One hosts (type: systemone): any host that speaks the System One decision contract. The host comes from base_url, so a provider speaking the same contract is a configuration entry rather than new Polytoken code. Neuralwatt Clef Flash is the first configuration:

    integrations:
    decisions:
    active: neuralwatt
    backends:
    neuralwatt:
    type: systemone
    base_url: https://api.neuralwatt.com
    model: clef-flash
    key: ${NEURALWATT_API_KEY}

    base_url is the origin before /v1: Polytoken appends /v1/systemone to it, so the value above serves https://api.neuralwatt.com/v1/systemone. A trailing slash is fine. A value that already ends in a /v1 segment fails validation with a message explaining the convention, because it would double the segment. Clef Flash access requires enrollment with Neuralwatt; a model your account cannot reach, and a model name that does not exist, comes back as a not-found rejection. Unlike typesafe, the systemone family has no default model, so set the host’s model name explicitly. A key configured on an inference provider is never reused for a decision backend; give each decision backend its own key.

The key is usually an environment reference, resolved when Polytoken loads its configuration. With no active backend, the tool returns an error naming integrations.decisions.active so the model can tell you what is missing.

Other backend families were evaluated and are not implemented in this release: OpenAI Decisions, Liquid d1, Cloudflare Clef, OpenRouter, and self-hosted System One servers. Configuring one of these types fails configuration validation with a pointer to the compatibility notes, so a typo cannot silently disable your decision calls.

A configuration written for this release can fail to load on an older Polytoken build: older builds reject the systemone type and the base_url key instead of ignoring them.

The Decisions pane in the interactive configurator (polytoken config ui) edits backend instances and selects the active one without editing the file by hand. The pane offers the backend type as a selector, and a systemone instance asks for its base URL and model. Saving several backends without an active selection fails with the same validation message the loader produces. See Application Configuration for the full key reference.

The first decision_agent call in a session asks for your approval, because the tool sends your text to the configured backend. An explicit permission rule can allow it always:

permissions:
allow:
- tool: decision_agent

See Permission Rules for rule syntax and scopes.