Classify failures with a decision model
A classifier judge gives each trace exactly one label. By default the labels are the
failure modes in your agent’s taxonomy, read fresh on every
run. You can also supply your own inline label set. A classifier can run on an ordinary chat model
or on a decision model, such as typesafe/jev-1.13 on OpenRouter. A decision model returns a
choice, a confidence and a probability for every label, quickly and cheaply.
At a glance
| Where | Judges → Create judge → Type = Classifier |
| Labels | Project failure modes (live), plus a none option for healthy traces, or Inline labels |
| Engines | Auto (the default: follows the connection’s model), LLM (chat model) or Decision model |
| Needs | An LLM connection. For a decision model, an OpenRouter connection whose model is a decision model |
| Writes | One score per trace under the judge’s Metric key |
| API | POST /api/judges with "type": "classifier" |
Which engine to use
| LLM (chat model) | Decision model | |
|---|---|---|
| Returns | A label, a confidence and a written reason | A label, a confidence and per-label probabilities |
| Written rationale | Yes | No. Decision models choose; they don’t explain |
| Cost and speed | A full chat completion per trace | Much cheaper and faster per trace |
| Best for | Investigating why traces fail, or reviewing a few by hand | Labelling large volumes of traces when the label itself is what you need |
The engine is part of the judge. The connection is not: you pick it when you test or run the judge, the same as for every other judge (see How runs pick a connection). A decision-model classifier needs a connection whose model is a decision model.
Auto is the default engine. It picks the engine from the connection each run uses: a model
whose id starts with typesafe/ runs as a decision model, and any other model runs as a chat
model. Choose LLM (chat model) or Decision model only when you want to fix the engine
regardless of the connection.
Set it up
Add an OpenRouter connection for the decision model
Open Settings → LLM providers and add a connection with API format = OpenRouter, your
OpenRouter API key, and Model = typesafe/jev-1.13. Test it and save it. You don’t need to make
it the default. See LLM connections.
Pin a dated version such as typesafe/jev-1.13, not a floating alias like
~typesafe/jev-latest. A floating alias can move to a new model without warning, which changes
your labels and makes runs from different weeks impossible to compare.
Create the classifier judge
Go to Judges → Create judge, give it a name, and set Type = Classifier. Then choose:
- Target type: Trace or Session.
- Labels from:
- Project failure modes (live) labels each trace with one of your failure modes, or
nonewhen the trace looks healthy. - Inline labels uses a label set you write in the form, with a name and a definition for each
label. Treat it as a complete set: there is no
noneoption, and a trace that fits no label is skipped.
- Project failure modes (live) labels each trace with one of your failure modes, or
- Engine: leave it on Auto (from the connection’s model) and the OpenRouter connection you pick at run time selects the decision model, or set Decision model to fix it.
- Metric key: the name the scores are stored under, for example
issue_class_jev. Leave it blank to derive it from the judge name. It can’t be changed once the judge exists, and it must not already be used by another judge in the agent.issue_classis reserved for the built-in Issue Classification judge, which always uses Project failure modes (live). - Evidence to attach: which parts of each trace the classifier reads. By default that is the input, the output, the full message trajectory and the session metadata.
The live preview on the right lists the candidate labels and shows the exact request the engine will receive for a sample trace. For a decision model that is the Decisions API request; for a chat model it is the prompt. If your agent has no failure modes yet, the preview tells you so: until you add some, runs are skipped.
Test it on a real trace
In the preview, pick a sample trace, choose your OpenRouter connection under Connection for the test run, and click Test run. You’ll see the chosen Label, its confidence, and the Probabilities for every candidate, highest first. A decision model gives no written reason, and the panel says so. Nothing is saved.
Click Create judge when the labels look right.
Run it and read the results
Enable it as a scorer, then click Run now and pick the OpenRouter connection as the scoring connection. To score new traces automatically instead, set Apply for to Continuous (score new traces). Each scored trace gets one score under your metric key. Find the scores on the Scores page and in each trace’s scores.
Example: create one over the API
The same judge, created with POST /api/judges:
curl -X POST "$NEENS_BASE_URL/api/judges" \
-H "Authorization: Bearer $NEENS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Issue class (Jev)",
"type": "classifier",
"scope": "trace",
"level": "project",
"definition": {
"method": "classification",
"target_type": "trace",
"taxonomy_source": "failure_modes",
"metric_key": "issue_class_jev",
"engine": "decisions",
"required_data": ["input", "output", "messages", "metadata"]
}
}'To let the connection’s model choose the engine (the Auto setting), leave engine out of the
definition: a connection whose model id starts with typesafe/ then runs it as a decision model.
For your own labels, set "taxonomy_source": "inline" and add a complete label set:
"labels": [
{ "name": "refund_issued", "definition": "The agent confirmed a refund was issued to the customer." },
{ "name": "refund_denied", "definition": "The agent told the customer the refund request was declined." },
{ "name": "escalated", "definition": "The agent handed the conversation to a human agent." }
]The request is rejected with a 422 and a message saying what to fix when an inline classifier
has no labels, engine is present but isn’t llm or decisions, the metric key is
issue_class, the metric key is already used by another judge in the agent, or the metric key
isn’t a valid slug.
To try a definition before saving it, send it to POST /api/judges/test-run with
"type": "classifier" and an optional connection_id. Without a connection_id, the test uses the
agent’s default connection.
How it works
- The taxonomy is read at run time. With Project failure modes (live), each run reads your agent’s current failure modes. When you add, rename or reword a failure mode, the next run uses the change, with no need to edit the judge.
- Definitions become the decision criteria. Each failure mode’s (or inline label’s) name and definition is what the model chooses between. The definitions shape the labels more than any other setting.
- One label per trace. The classifier writes a single categorical score per trace under its metric key, alongside the confidence. With a decision model it also keeps the per-label probabilities.
- No match. With the live taxonomy, a healthy trace is labelled
none. With inline labels, a trace that fits no label is skipped and gets no score.
Tips
- Compare it with the built-in classifier before you rely on it. Run your decision-model classifier on the same traces as the built-in Issue Classification judge and compare the two metric keys on the Scores page. Look into the traces where they disagree before you switch over.
- Write concrete, behavioural definitions. “The agent called a search tool for a question that needed the orders tool” classifies far better than “tool problems”. Describe what can be observed in the trace, not the cause you suspect.
- Keep the labels distinct. Two failure modes with overlapping definitions split the probability between them and lower the confidence on both. Merge or sharpen them in your taxonomy.
- Pin the model version on the connection (see the warning above), and create a new connection when you deliberately move to a newer model.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| The preview says the agent has no failure-mode taxonomy | No failure modes exist yet, so runs are skipped | Add failure modes (see Issues & failure modes), or switch to Inline labels |
| The test run fails with a connection error | The chosen connection isn’t visible to this agent, or its model isn’t a decision model | Choose an OpenRouter connection whose model is typesafe/jev-1.13 (or another decision model) in Connection for the test run |
| Inline labels: many traces get no score | Traces that fit no inline label are skipped | Add the missing label so your label set covers every outcome |
| No reason appears on the scores | Decision models don’t return a written rationale | Use Engine = LLM (chat model) when you need reasons |
Create judge is disabled and the metric key shows a “reserved” error (over the API: a 422) | issue_class belongs to the built-in Issue Classification judge | Choose another key, such as issue_class_jev |
Saving fails because the metric key is already in use (over the API: a 422) | Another judge in this agent already writes scores under that key | Choose a key no other judge in the agent uses |