feat(workflows): make the deterministic intent router real - #2501
Open
Hoang130203 wants to merge 1 commit into
Open
feat(workflows): make the deterministic intent router real#2501Hoang130203 wants to merge 1 commit into
Hoang130203 wants to merge 1 commit into
Conversation
config/n8n/08-m4-deterministic-voice.json was a manualTrigger plus a
sticky note saying "Customize the nodes below to match your setup", with
"connections": {} — an empty canvas behind a featured catalog card
advertising "Intent classification with deterministic routing".
Now it classifies and routes:
POST /webhook/ods-intent {utterance}
-> llama-server classify (temperature 0, max_tokens 8)
-> constrain the answer to the allowed set
-> Switch: smart_home | timer | question | unknown
The "deterministic" part is the point, so it is built in rather than
left to the prompt:
- The classifier runs at temperature 0 and is told to answer with one
lowercase word.
- Its reply is lowercased, stripped to [a-z_], and matched against the
allowed set. Anything else — a sentence, a made-up label, an empty
completion — becomes `unknown`.
- A Switch node, not the model, selects the branch, with a named
fallback output for unknown.
So a hallucinated label cannot route a smart-home command somewhere
unintended; it lands in the branch meant for "I could not classify this".
All four outputs converge on the responder so the workflow runs as
shipped and you can see the classification before wiring real actions. A
sticky note on the Switch says what each branch is for.
This was referenced Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
config/n8n/08-m4-deterministic-voice.jsonwas a placeholder —manualTrigger"connections": {}— behind a featured catalog cardadvertising "Intent classification with deterministic routing".
The "deterministic" part is built in, not prompted for
The card promises deterministic routing, so the workflow enforces it rather
than trusting the model to behave:
temperature: 0withmax_tokens: 8and istold to answer with a single lowercase word.
[a-z_], and matched against theallowed set. A sentence, a made-up label, a chatty preamble, or an empty
completion all normalise to
unknown.fallback output for
unknown.That ordering matters: a hallucinated label cannot route a smart-home command
into a branch that actuates something. It lands in the branch that exists for
"I could not classify this". The raw label is returned alongside the
constrained one, so you can see when the model went off-script.
All four Switch outputs converge on the responder, so the workflow runs
correctly as shipped and you can watch classifications before wiring real
actions to the branches. A sticky note on the Switch says what each branch is
for.
AI Assistance
AI assisted with drafting the node graph, the classifier prompt, and this
description. I confirmed the Switch node's rule/fallback parameter shape
against the node definition (
rules.values→conditions,renameOutput,outputKey;options→fallbackOutput,renameFallbackOutput) rather thanfrom memory, and validated the file before pushing.
Release Lane
release/2.6.xmainStable hotfix reason:
Changed Surface
(One JSON file under
config/n8n/. An import payload for n8n; no ODS codeexecutes it. The catalog entry is unchanged.)
Risk And Validation
git diff --checkrelease/2.6.xCommands/results:
Caveat: Docker is not running on my dev host, so I could not run a real
utterance through llama-server and watch the Switch fire. The validation is
static against the real node definitions. The normalisation and whitelist are
plain JS in a Set node, so their behaviour does not depend on the model — the
part I cannot demonstrate is the model's own label quality, which is exactly
what the whitelist exists to contain. Happy to get a live run before merge.
Operational Change Check
An import payload for n8n. Nothing in the installer, compose stack,
ods-cli,or dashboard-api executes it. No existing install changes until a user imports
it.
Notes For Reviewers
The intent set is a placeholder I had to choose.
smart_home,timer,question,unknownare a reasonable voice-assistant starting set and matchwhat the card implies, but "M4" suggests this template came from a specific
milestone with its own intent taxonomy. If there is a canonical ODS intent list
I did not find, tell me and I will swap it — the shape of the workflow does not
change, only the strings in the prompt, the whitelist, and the Switch rules.
Branches deliberately converge. I could have left three outputs
disconnected to make "fill this in" obvious, but a workflow that half-runs on
import is worse than one that runs and returns its classification. The sticky
note carries the intent.
Part of the series making the 18 stub templates real: #2496, #2497, #2498,
#2499, #2500. Independent files, no overlapping lines.