Guide · Decision models
Decision models
DML separates generation from decision. Alongside task/N and
prompt/N, it can route a bounded semantic decision to a
decision model and branch on a typed answer. Your active
model is the default; Jev (TypeSafe System One) is the
calibrated backend.
What a decision model is
A decision model answers a small, closed question about an explicit state. It never writes prose and never sees prior memory, so its answer is bounded and reproducible. Every decision is one of four kinds:
| Kind | Answers | DML value |
|---|---|---|
choose | One of a closed set of options | The option atom |
rate | One of an ordered list of levels | The level atom |
verify | A yes/no proposition | yes | no | unknown |
probability | A yes/no proposition | A number in [0, 1] |
Because answers are constrained to the options and levels you supply, downstream logic never has to parse free text or guess at a label.
DML predicates
Ask several questions in one batched call with judge/2, and bind the answers in order:
judge(Message, [
choose("Which team should handle this?",
[billing-"Charges and refunds", orders-"Delivery", account-"Login"]) - Team,
rate("How frustrated is the customer?", [calm, frustrated, angry]) - Frustration,
verify("Does the message ask for a refund?") - Refund,
probability("Is this urgent?") - Urgency
]),
( Refund == yes, Team == billing
-> answer("Escalating to the refund queue.")
; format(string(R), "route=~w frustration=~w urgency=~w", [Team, Frustration, Urgency]),
answer(R)
).
One-off predicates are also available:
choose(State, Question, Options, Choice)
rate(State, Question, Levels, Level)
verify(State, Question, Truth) % yes | no | unknown
probability(State, Question, P) % number in [0, 1]
holds(State, Question) % succeeds when verify == yes
holds(State, Question, Threshold) % succeeds when probability >= Threshold
Judgments are memoized per run by backend, model, state, and questions, so backtracking reuses an answer instead of re-sampling the model.
Backends
Decision predicates are implemented by swappable backends. Each declares its capabilities, and the answer carries its basis.
| Backend | Calibrated | Probability | Credentials |
|---|---|---|---|
llm (default) | no | self-reported | pi's active model and credentials |
jev | yes | calibrated | TYPESAFE_API_KEY |
mock / mockJev | no / yes | yes | none |
The llm backend is the default, so decision predicates work with
no new credentials. Jev (TypeSafe System One) is opt-in and
adds calibrated probabilities, confidence, independent questions, and
structured criteria. The SDK never reads or stores credentials: the API key
is supplied by the caller. A mockJev backend advertises Jev-like
calibrated capabilities so the calibrated path can be developed and tested
without a key.
Capability gating
A skill that depends on calibration should fail clearly rather than silently
run on an uncalibrated backend. require_judgment/2 records a
required capability, and with_judgment/2 scopes a named backend
to a goal.
% Scope a calibrated backend to this decision.
with_judgment(jev, decide(State, Decision)).
% Fail at the next judgment if 'calibrated' is unavailable.
require_judgment(calibrated, (
probability("Is this urgent?", P),
(P >= 0.8 -> escalate(State) ; standard(State))
)).
Configure the SDK
import {
createDeepClause,
createLLMJudgeBackend,
createJevJudgeBackend,
createMockJevJudgeBackend,
} from "deepclause-sdk";
const sdk = await createDeepClause({
model,
llmBackend,
judgeBackends: {
llm: createLLMJudgeBackend({ llmBackend }),
jev: createJevJudgeBackend({ apiKey: process.env.TYPESAFE_API_KEY }),
mock: createMockJevJudgeBackend(),
},
defaultJudge: "llm",
});
The backend used by a judgment resolves in this order:
with_judgment/2 scope, then a per-run override, then
defaultJudge, then the first registered backend.
sdk.getJudgeCapabilities() reports what the active backend can
do.
In the pi extension
Enable Jev in .pi/deepclause/config.json; the extension reads
the key from the named environment variable and never stores it.
{
"judgment": {
"default": "llm",
"jev": {
"enabled": true,
"model": "jev-latest",
"apiKeyEnv": "TYPESAFE_API_KEY"
}
}
}
Then select the backend per run or as the default:
/dc-run skills/triage.dml --judge=jev
/dc-judge enable
/dc-judge default jev
If Jev is enabled but the key is missing, the extension warns and falls back to llm.
Evidence, not authorization
A decision model produces evidence; DML owns policy. Thresholds, weights, routing, and every side effect stay in Prolog. Calibrated probabilities can inform a decision, but money-moving or irreversible actions keep their hard gates and human approvals — and a skill that requires a calibrated backend falls back to its fallback clause when that backend is unavailable.
Links
Judgment backends reference → Worked judgment examples → DML SDK & CLI guide → API reference →