Decision models in Nussknacker: typed AI decisions for Kafka and API workflows

Decision models like TypeSafe Jev and Cloudflare Clef return typed answers with probabilities. See how to add one to a Nussknacker scenario and what you gain.

Decision models are the AI story of this autumn. On 15 September 2026 TypeSafe introduced Jev, the first of what it calls System One models. On 1 October Cloudflare released Clef and Clef-flash, open-weight decision models that use the same request format. Open replicas appeared in between. The idea behind all of them is simple: instead of generating text, the model reads your data, answers typed questions you defined in advance, and returns a probability for every allowed answer.

That is exactly the shape of decision a streaming or API workflow needs. Nussknacker now has a Decision Model component and a Decision AI integration that bring this model class into a scenario next to Apache Kafka sources, enrichers, filters and decision tables, in both streaming and request-response processing modes. This post explains what a decision model is, why it fits event processing better than a chat model, how to add one to a scenario without writing code, and what you get in return.

Nussknacker scenario with a Decision Model component classifying customer replies and routing them to three outcomes.
The sim-replacement-reply scenario in Nu Designer after the change: Customer reply → Understand customer reply (Decision Model) → Route the decision → three sinks.

What is a decision model?

A decision model takes a state (a message, a record, a JSON object) and a set of typed questions, and returns a typed answer for each question together with calibrated probabilities. It does not write a reply, explain its reasoning or produce JSON you have to parse. TypeSafe's System One documentation describes three question types:

Question type You define The model returns
Choice A set of named options with descriptions The selected option, a probability per option, a confidence value
Score Ordered levels, each described A probability-weighted score, a probability per level, a confidence value
Noul A yes/no question A single probability that the answer is yes

 

Cloudflare's Clef documentation uses the same three types and the same state plus questions request shape; Cloudflare describes Clef as API-compatible with Jev. Two vendors shipping the same interface within a month is the clearest signal that this is becoming a model category rather than one product.

The practical difference from a large language model is what comes out. An LLM returns a string you must parse and validate, and it can return something outside your schema. A decision model returns values from the answer space you defined, every time. Both TypeSafe and Cloudflare also position their models as much faster and cheaper per call than generative models; TypeSafe's pricing page lists Jev at $0.042 per million input tokens with no charge for output. We have not benchmarked either vendor ourselves, so treat the speed and cost figures in their announcements as their claims, and test on your own traffic.

Why decision models fit Nussknacker scenarios

Nussknacker scenarios already call AI models. The Integrations view lists OpenAI, Anthropic, Google AI Gemini, Amazon Bedrock, MistralAI and others as enrichers, and they are the right tool when a scenario needs generated text: a summary, a reply draft, an extracted field.

Most decisions inside a scenario are not like that. A Kafka event arrives and the scenario needs to know which category it belongs to, how severe it is, or whether a condition holds, and then it needs to branch. Until now the options were a deterministic rule (regex, lookup, decision table) or an LLM call followed by parsing. A decision model is the third option, and it fits the hot path of a Flink job or a request-response endpoint for three reasons:

The output is typed before the call is made. Nu Designer knows the result structure from the configured questions, so downstream expressions get field discovery and validation at design time, not at runtime.

Uncertainty is a value, not a vibe. Each Choice and Score answer carries probabilities and a confidence number. TypeSafe documents confidence as a statistic derived from the probability distribution (see Confidence). Your routing policy can threshold on it explicitly.

The model only interprets; the scenario still decides. The business action stays in a switch, a filter or a Decision Table next to the model node, where it is visible, versioned and testable.

Decision AI integration listed next to LLM integrations in Nussknacker.
The Integrations list filtered to AI, with Decision AI visible among OpenAI, Anthropic, Gemini, Bedrock and MistralAI.

 

Adding a decision model to a scenario

The demo scenario, sim-replacement-reply, handles a security check that most mobile operators run. When someone orders a SIM card replacement, the customer gets an SMS asking them to confirm. The reply comes back through Kafka, and the scenario has to decide one of three things: continue the replacement, block it and escalate, or ask for clarification.

Step 0: the regex version

The original scenario parsed the reply with a Variable node and a regular expression: a message starting with "yes" (or "tak") meant continue, one starting with "no" (or "nie") meant block, anything else meant ask again. It works for one-word answers and fails on a real one. During the demo this reply arrived:

"Yes, that's exactly the problem: I did NOT request this SIM replacement. Please block it immediately."

The regex sees "Yes" and routes it to Continue replacement. The customer just told us the opposite.

Regex-based YES/NO parsing of customer replies in a Nussknacker Variable node.
The Parse YES-NO Variable node with the SpEL expression matching (yes|tak) and (no|nie).

 

Step 1: configure the Decision AI integration once

An administrator adds a Decision AI integration from the Integrations view. It needs a name, a Base URL (the default is https://api.typesafe.ai/v1, entered without the /systemone suffix) and an API key, which is sent as a Bearer token. Check connection calls the model listing endpoint only; it does not run inference and no scenario data leaves the platform. The administrator selects the models to expose, and the documentation recommends choosing a versioned model ID over an alias such as jev-latest, because an alias moves when the vendor ships a new release. In the node's Model selector, moving aliases are marked (moves). Scenario authors pick the integration and a model in the node; the key and the endpoint stay in one place. See the Decision AI integration documentation for the full list of settings.

Configuring the TypeSafe Jev connection in Nussknacker's Decision AI integration.
The Decision AI form with name, Base URL, masked API key, Models field and the Check connection button.

 

Step 2: replace the regex with a Decision Model node

You can drag the Decision Model component from the AI group of the component panel and fill in the form, or ask Nu Assistant in Nu Designer. In the demo the request was:

replace yes/no matching with decision model to understand the customer sim replacement reply

The assistant swapped the Variable node for a Decision Model node named Understand customer reply, pointed the existing switch conditions at the new output, and explained its choices, including a confidence cutoff it picked for the confirmed branch and an invitation to change it. Nothing else in the scenario moved. The change was visible in Nu Designer before it was saved, so it could be reviewed like any other edit.

Nu Assistant replacing regex parsing with a Decision Model node in Nussknacker.
The Nu Assistant panel describing the swap and the new routing rules, with the updated graph behind it.

 

Step 3: read the questions the way the model reads them

Opening the node shows the Context (here a record with the question the customer was asked and their statement, built from #input with the in-field data mapper) and one Choice question, reply, with the instruction sent to the model and three options: confirmed, denied and unclear, each with a plain-language description. Options can also carry a Not for exclusion and examples, which is how you teach the model the difference between two categories that sound alike. Option lists can be fixed or resolved from data per event. The form notes that questions are evaluated independently against the same context, and that the output of this question is choice, confidence and probabilities. There is an Edit as JSON dialog for authors who prefer to paste a System One questions object, and a Treat context as data toggle that appends a reminder to evaluate the context as evidence rather than instructions (a mitigation, not a guarantee, against prompt injection).

The component documentation walks through a two-question variant of the same scenario: a Choice for the authorization reading plus a Probability of yes question, needsClarification, that drives the clarification path separately.

Choice question with confirmed, denied and unclear options in the Nussknacker Decision Model editor.
The Decision Model form with the reply Choice question expanded: instructions, three options with descriptions, Not for and Examples fields.

 

Step 4: deploy and watch the result on live data

After deploy, the same Kafka stream flows through the new version. Opening the node again shows incoming and outgoing records side by side. For the reply above, the outgoing record contains:

assessment
  model: "jev-1.13.0"
  reply
    choice: "denied"
    confidence: 1
    probabilities
      confirmed: 0
      denied: 1
      unclear: 0

The reply that started with "Yes" is now read as a denial, and the switch sends it to Block and escalate. Same scenario, same three outcomes, different judge.

Typed Decision Model output with choice, confidence and probabilities inspected on live data in Nussknacker.
The Decision Model node with the incoming customer statement on the left and the assessment record with choice: "denied" on the right.

 

What the scenario gains

A contract the editor can check. With the output variable named assessment, expressions such as #assessment.reply.choice or #assessment.reply.probabilities.denied are validated when you type them. A renamed option or a removed question shows up as an error in Nu Designer, not as a null in production.

Routing policy that is explicit. The switch after the model node holds the business rules: a denial always wins, a confirmation needs confidence above a threshold, everything else goes back to the customer. Those rules are plain SpEL conditions and move into a Decision Table when they need account data, limits or more than a handful of rows. One thing to keep in mind there: Decision Table returns all matching rows, so the scenario should say what happens on zero matches and on conflicting matches. The model does not silently decide what the business does, and a classified "confirmed" is still not identity verification.

No parsing layer. There is no prompt asking for JSON, no retry on malformed output, no toLowerCase().matches(...) on free text. The adapter validates answer IDs, probability ranges and sums before the result reaches the scenario. Probability of yes questions come back as a single number exposed as probabilityTrue; the component does not hide a threshold inside it, because that threshold is policy and belongs in the scenario.

Model versions under control. TypeSafe's documentation recommends pinning a versioned ID instead of the jev-latest alias once confidence thresholds have been tuned. The integration lets the administrator save versioned IDs, and #assessment.model carries the identifier the service reported for each event, so a model change is something you choose and can audit. The documentation is careful here: the reported name can be an alias or a request echo, so it is evidence for your logs, not proof of an immutable model version.

Operational visibility. The component emits the same metric family as the LLM Chat component (llm.calls, llm.latency, llm.errors, llm.tokens), tagged with the node, integration and model, so decision-model traffic shows up in existing dashboards. Failures stay separate from uncertainty. In the default Assessment result mode an error fails the event; in Detailed result mode the output carries a status, the assessment or a typed error, and timing metadata (queue wait, HTTP time, total duration, request ID), so the scenario can route failures explicitly. A timeout does not turn into a fabricated probability. Concurrency, queue size, timeout and request size are configurable per integration, and the documentation's advice applies: filter out events that do not need interpretation before they reach the model node.

Testing on real traffic. The incoming and outgoing record views in Nu Designer let you compare what the model received with what it returned, record by record, before and after a change to a question. The Testing tab does the same with hand-picked replies before deployment.

When to use a decision model, a rule, or an LLM

Situation Reach for
The input is an exact code, a known event type or a literal answer A filter, switch or Decision Table
The input is free text or a record that needs interpretation, and the outcome is one of a known set A Decision Model node
The outcome is text: a summary, a draft, an extracted sentence An LLM enricher
Both: understand first, then write Decision Model to classify and gate, LLM only on the branch that needs it

The same pattern applies beyond customer replies: ticket and intent routing, incident triage by severity, billing dispute classification, document categorisation, and guardrails that score an LLM's input or output before it goes further.

Which decision models does Nussknacker support?

The Decision AI integration currently supports TypeSafe Jev through the System One API. Cloudflare documents Clef and Clef-flash as using the same request and response shape and adds multimodal input, and open-weight models such as Kev and Laya use it too, so the category is converging on one contract. Clef through Cloudflare Workers AI and decision models served by Ollama are not supported by this integration yet. The Base URL is configurable, but the integration's model discovery and request handling are built for the Jev endpoint, so point it elsewhere only for a Jev-compatible deployment.

See it in the demo

The short video below walks through the swap end to end: the regex version, the integration setup, the Nu Assistant request, the question form, deploy, and the live record that the regex got wrong.

Adding a System One decision model to a running Nussknacker scenario with Nu Assistant.

 

FAQ

What is a decision model?

A model that reads an input state and answers typed questions (Choice, Score, yes/no) with calibrated probabilities instead of generating text. TypeSafe calls its models System One models; Cloudflare calls Clef a decision model. Both use the same request format.

How is a decision model different from an LLM?
An LLM generates a string you parse; a decision model returns one of the answers you defined, with a probability for each. Decision models are designed for the hot path of a workflow; LLMs are designed for producing content.

Do I need to write code to use a decision model in Nussknacker?
No. The integration is configured in the Integrations view, the questions are defined in the Decision Model form or pasted as JSON, and routing is done with the usual switch, filter and Decision Table components. Nu Assistant can make the change from a one-sentence request.

Does Nussknacker support Cloudflare Clef or self-hosted decision models?
Not yet. The integration currently supports TypeSafe Jev through the System One API; Clef on Cloudflare Workers AI and decision models served by Ollama are not supported by this integration yet.

Can a decision model run inside a Flink streaming job?
Yes. The Decision Model component is an enricher available in streaming scenarios on Apache Flink and in request-response scenarios alike. Each Flink subtask has its own concurrency and queue limits for model calls.

Want to add decision models to your own Kafka or API workflows? Book a Nussknacker demo with your use case.