The Jev Decision System

Applies to: ChengOS v0.2.0+ | Last verified: 2026-09-26 | Source: crates/cheng-llm/src/decision.rs, crates/cheng-llm/src/decision_service.rs, crates/cheng-nodes/src/nodes/builtin/ai/decision_branch.rs, config/decision_calibration.toml, crates/cheng-llm/tests/fixtures/decision/

ChengOS can put a small, fast decision model in front of your agents. Before any large language model runs, the decision model reads the user's latest request and picks one of the routes you declared on the canvas — "email", "weather", "code" — or none of them. Each route leads to an agent that carries only the tools it needs; everything unclear goes to the general agent you already had.

This page explains why the system exists, how it is built, and what it measured. To build a router, read Semantic Routing with Decision Branch. For credentials and providers, read Decision Providers & TypeSafe Jev.

Why a decision system

One general agent with every tool attached works, but every turn pays for it:

The usual alternative, a single wrapper tool that lists the others on demand (Tool Hub), trades context for an extra round trip — and in the measurements below it did not save context at all on turns that actually needed a tool.

A decision model solves the problem one step earlier: it chooses the path before the chat model sees anything. It never answers the user, never calls a tool, and can only choose among routes the workflow author declared.

How it works

Every routed turn passes two independent decision points:

user message
  -> ai/decision_branch        choose a route (or the fallback) — decides context
  -> focused agent, or the general agent on fallback
  -> concrete tool call, if any
  -> approval policy           execute, ask the user, or deny — decides permission

The first decision only makes the context smaller. The second one — the deterministic approval policy — is still the only thing that grants permission. A route can never unlock a tool call that the approval policy would stop.

Inside ChengFlow the parts are:

PartRole
ai/decision_branchThe canvas node. You write route IDs and one-sentence descriptions; each route becomes a route_<id> port, plus a permanent fallback_out.
DecisionService (cheng-llm)Builds a provider-neutral Choice question, enforces the deadline, validates the answer against the declared set, and records a text-free audit summary.
Decision providersTypeSafe Jev (hosted, the default), Laya (runs on your own hardware), and the LLM baseline (any connected chat model, uncalibrated).
Calibration profilesMeasured thresholds per provider × model × language × task, in config/decision_calibration.toml.

Decision providers are deliberately not chat providers: they never appear in a chat model picker, and a chat model becomes a decision provider only through the explicit LLM-baseline option.

Design rules

These hold for every provider and every workflow:

What it measured

The go/no-go evaluation ran on 2026-09-23 against routing-eval-v2: 157 labeled requests (90 English, 67 Chinese) covering short and long single-intent requests, chit-chat, ambiguous and multi-intent requests, referential follow-ups ("make it shorter"), new topics after a previous route, and route lists of 3, 8, and 16. The decision model was jev-1.13.0.

The full interactive report — charts, per-category breakdown, threshold sweep — is published here: Jev semantic routing evaluation (Chinese). Its key figures:

Result
Routing accuracy156 / 157 (99.4%; bootstrap 95% interval 98.1%–100%)
Answering-model context on tool-needing turns≈ 9× smaller — 4,277 → 469 tokens (gpt-6-luna), 5,209 → 565 (GLM), median
Routing latency0.57 s p50, 0.70 s p95 (chat-model routers: 1.4–2.9 s)
End-to-end completionOn par with the general agent (±2 of 101), 5–9 above Tool Hub

Routing decisions, all 157 requests. A wrong route is the only error that loses capability — the focused agent lacks the tool it needs. A safe fallback just sends the turn to the general agent.

RouterCorrectSafe fallbackWrong routep50 / p95 latency
Jev jev-1.13.0, calibrated156010.57 / 0.70 s
glm-4.5-air (uncalibrated)153311.87 / 5.91 s
glm-5 (uncalibrated)152321.92 / 5.24 s
gpt-4.1-mini (uncalibrated)146562.91 / 3.66 s
gpt-6-luna (uncalibrated)1372001.39 / 1.83 s

End-to-end completion, 101 three-route requests (email / weather / code, backed by the real mail, web, and file_ops_hub tools). A tool-needing request counts when the right tool is called; chit-chat and ambiguous requests count when no tool is called.

Chat modelGeneral agentTool HubChat-model routerJev router
gpt-6-luna92859290
glm-4.5-air91849393
glm-589828787

Median context on the 63 tool-needing requests (input tokens seen by the answering model, all turns; Jev's own ~462 input tokens are billed by TypeSafe and enter no LLM context):

Chat modelGeneral agentTool HubAfter Jev routingReduction
gpt-6-luna4,2774,7234699.1×
glm-4.5-air5,2094,8605659.2×
glm-55,2094,8585659.2×

What the numbers say:

Choosing a provider

TypeSafe JevLayaLLM baseline
RunsHosted (api.typesafe.ai)laya serve on your hardwareAny connected chat model
Request text leaves the deploymentYes, every routed turnNoDepends on the chat model
Calibrated probabilitiesYesOnly for a pinned model fileNo — threshold mode None
Accuracy on routing-eval-v2156/15744–51 of 90 (en), 36 of 67 (zh), every wrong route multi-intentModel-dependent (137–153/157)
Warm latency0.57 s p5010–15 ms p95 on an RTX A40001.4–2.9 s p50
Route limit1615 (the fallback takes one option); fewer on the English model16

Jev is the default because it is the most accurate, the fastest hosted option, and ships with calibration. Choose Laya when request text must stay on your own hosts: it is conservative — it falls back far more often than Jev and never gets the previous route, so sticky follow-ups do not apply — but it answers in milliseconds. A two-stage cascade (Laya first, a local chat model only for its uncertain turns) recovers 7–9 correct routes per scenario over Laya alone while calling the chat model on about 46% of turns. Neither local option approaches Jev; both keep your data at home.

Pairing routing with context compaction

Routing shrinks the first turn. Long runs grow through tool results instead, and a focused agent can be tuned for that too. Starting with the release after v0.2.0, each agent/react_agent takes an optional compaction preset — standard (today's behavior), aggressive, or lenient — so the agent behind each route can compact the way its work needs:

chat/input → ai/decision_branch ─route_email──► agent/react_agent  (standard)
                                ├route_code───► agent/react_agent  (lenient)
                                └route_data───► agent/react_agent  (aggressive)

The router itself is untouched; the preset is set on the agent, never inferred from a route name. On the describe → call workload the aggressive preset's early archival of already-used tool schemas cut cumulative request tokens by 34.8% in the offline corpus. It stays limited to aggressive until it is measured on real replays.

Limits and what's next

Next steps