> ## Documentation Index
> Fetch the complete documentation index at: https://yukta.infenia.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Classify, gate and score

> Route, gate and rate messages with a constrained model decision: a label, a yes or no, or a score you can branch on.

<dl />

**Use this when** a step needs judgement that a rule cannot make ("is this a bug or a question?", "is this reply safe to send?") and the answer should come back in a shape you can route on. For open-ended writing use [`LLM_CALL`](https://yukta.infenia.com/guides/ai/call-an-llm).

**Prerequisites:** the `CLASSIFY` plugin is present (`GET /api/plugins/CLASSIFY` answers `200`). Pick a backend:

* `SYSTEMONE` (default): a System One decision model on your own Ollama (`baseUrl` defaults to `http://localhost:11434`). Answers carry calibrated probabilities and cost no provider tokens.
* `LLM`: any chat model, through the same access as `LLM_CALL`. Probabilities are the model's own estimates.

Add `-H "X-API-Key: $YUKTA_API_KEY"` if your server has keys.

![Pick the decision plugin by the question: CLASSIFY picks a label, GATE answers yes or no, SCORE rates on a scale, ANOMALY spots the unusual and DEDUPE spots repeats. Each leaves on its own output ports.](https://yukta.infenia.com/images/diagrams/guide-classify-gate-and-score.light.svg)

Choose the plugin by the question you ask; each one leaves on ports you wire like any other branch.

## Steps

**1. Classify into labels and route on them**

`CLASSIFY` picks one label from your list. Describe every label: a small model depends on those descriptions. With `routeByLabel`, each label becomes an output port, plus `uncertain` for answers below `minProbability` (default 0.6). Save as `session.json`:

```json session.json theme={"system"}
{
  "sessionId": "decide",
  "name": "Decide",
  "workflows": {
    "triage": {
      "workflowId": "triage",
      "description": "Sort a message by kind",
      "nodes": [
        { "nodeId": "in", "type": "API_TRIGGER", "config": {} },
        {
          "nodeId": "kind",
          "type": "CLASSIFY",
          "config": {
            "model": "tev1:0.8b",
            "state": "payload.text",
            "labels": {
              "bug": "The user reports something that is broken or behaves wrongly",
              "feature": "The user asks for new behaviour",
              "question": "The user asks how something works"
            },
            "routeByLabel": true,
            "minProbability": 0.6
          }
        },
        { "nodeId": "bugs", "type": "CONSOLE_TERMINAL", "config": {} },
        { "nodeId": "features", "type": "CONSOLE_TERMINAL", "config": {} },
        { "nodeId": "questions", "type": "CONSOLE_TERMINAL", "config": {} },
        { "nodeId": "unsure", "type": "CONSOLE_TERMINAL", "config": {} }
      ],
      "edges": [
        { "source": "in", "target": "kind" },
        { "source": "kind", "target": "bugs", "sourcePort": "bug" },
        { "source": "kind", "target": "features", "sourcePort": "feature" },
        { "source": "kind", "target": "questions", "sourcePort": "question" },
        { "source": "kind", "target": "unsure", "sourcePort": "uncertain" }
      ]
    }
  }
}
```

The model name `tev1:0.8b` is an example; use the decision model you pulled. `state` is what the model sees (default the whole payload, cut to 15,000 characters). To use a chat model instead, add `"backend": "LLM", "provider": "anthropic", "apiKey": "decrypted:anthropic"` and its `model`.

```bash theme={"system"}
curl -X POST localhost:9858/api/sessions -H 'Content-Type: application/json' -d @session.json
curl -X POST localhost:9858/api/workflow/start -H 'Content-Type: application/json' \
  -d '{"sessionId":"decide","workflowId":"triage","payload":{"text":"The save button crashes the app"}}'
```

**2. Gate on a yes or no**

`GATE` asks a yes/no question and leaves on `pass` when the probability of yes reaches `threshold` (default 0.5), else `reject`. Put one in front of a risky node:

```json theme={"system"}
{
  "nodeId": "safe-to-send",
  "type": "GATE",
  "config": {
    "backend": "LLM", "provider": "anthropic", "model": "claude-sonnet-5-5", "apiKey": "decrypted:anthropic",
    "question": "Is this reply polite and free of personal data?",
    "state": "payload.reply",
    "threshold": 0.8
  }
}
```

**3. Score on a scale**

`SCORE` rates on ordered `levels` (2 to 26, lowest first). With `threshold` it leaves on `high` or `low`.

```json theme={"system"}
{ "nodeId": "urgency", "type": "SCORE",
  "config": { "model": "tev1:0.8b", "question": "How urgent is this ticket?",
              "levels": ["can wait", "this week", "today", "now"], "threshold": 2 } }
```

**4. Ask several things at once, or watch a stream**

* `ASK` takes up to 64 named `questions` (`choice`, `yesno`, `score`) in one call and writes `metadata.answers`; route with a `BRANCH` on `metadata['answers']['sentiment']['choice']`.
* `ANOMALY` and `DEDUPE` compare each message with a window of recent ones (ports `normal`/`anomalous` and `unique`/`duplicate`). By default the window is per node and per run, so a workflow started for every event never builds history; set `persist: session` to share it across runs.

## Verify it worked

```bash theme={"system"}
curl -s -H 'Accept: application/json' localhost:9858/api/sessions/decide/executions/$EXEC/results
```

The result comes from the `bugs` terminal, and the message metadata holds the decision: `classification` with `choice`, `probability`, `probabilities`, `label` and `uncertain`. The payload is unchanged. Send `{"text":"hmm"}`: a vague message should land on `unsure`. If the run fails with a model error, the Ollama endpoint is unreachable or the model is not pulled: the node fails and the node's `engine` policy applies ([Handle failures](https://yukta.infenia.com/guides/build/handle-failures.md)).

## Variations

* **Where the result lives.** Each plugin writes to `metadata[into]` (`classification`, `gate`, `score`, `answers`, `anomaly`, `dedupe`); change it with `into`.
* **Cap what the model sees.** `maxStateChars` (max 15,000), `stateKeep: tail` for build logs where the cause is last, and `onOverflow: fail` to reject oversize input instead of cutting it.
* **Latency.** `keepAlive` (such as `30m`) keeps an idle Ollama model loaded; `concurrency` (default 16) sets calls in flight.
* **Gate a tool call.** Put a `GATE` in front of `MCP_TOOL_CALL` asking whether the arguments the model proposed are safe: [Use tools and MCP servers](https://yukta.infenia.com/guides/ai/use-tools-and-mcp-servers.md).

## Related

* [Decision plugins reference](https://yukta.infenia.com/reference/plugins/decisions.md) for every option, port and default.
* [AI and deterministic work](https://yukta.infenia.com/concepts/ai-and-deterministic-work.md) for when to use a decision model instead of a chat model.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.