Confidence¶
Confidence is a second axis. The answer tells you what; confidence tells you whether to act on it unattended.
What it actually measures¶
Confidence is a statistic over the answer's own probability distribution — how concentrated it is. A flat distribution means the options or levels did not separate well for this input.
That is not the same as the answer being wrong. It means the question did not decide cleanly, which is a different problem and wants a different response: not "reject this answer", but "do not let software act on this one alone".
Nouls carry no confidence, by design
A noul's value already is its certainty. A 0.5 is the undecided case, so there is
nothing a separate statistic would add. Only Choice and Score report confidence.
Where confidence lives¶
Confidence is a field on the answer, not something you compute. Two of the four answer
records carry it, and the gate and the judge are the two things that read it — the judge
from either kind, the gate through decide(ChoiceAnswer) or the raw
decide(action, confidence) overload:
classDiagram
direction TB
class NoulAnswer {
<<record>>
+double value
}
class ChoiceAnswer {
<<record>>
+String value
+double confidence
}
class ScoreAnswer {
<<record>>
+double value
+double confidence
}
class JevConfidenceGate {
+decide(ChoiceAnswer) Decision
+floor() double
+requiredFor(String) double
}
class Decision {
<<enumeration>>
EXECUTE
CONFIRM
ESCALATE
}
class Outcome {
<<enumeration>>
PASSED
FAILED
INCONCLUSIVE
}
class JevJudge {
+minConfidence
+failOnInconclusive
}
JevConfidenceGate ..> ChoiceAnswer : decide(answer)
JevConfidenceGate --> Decision
JevJudge ..> ChoiceAnswer : reads confidence
JevJudge ..> ScoreAnswer : reads confidence
JevJudge --> Outcome
note for NoulAnswer "no confidence field:<br/>the value is the certainty"
Confidence-gated routing¶
Tier your thresholds by what the action costs when it is wrong. A universal floor keeps anything genuinely undecided away from automation; above it, expensive actions demand more than cheap ones.
JevConfidenceGate holds that policy in one place:
JevConfidenceGate gate = JevConfidenceGate.builder()
.floor(0.60d) // nothing acts unattended below this
.require("transfer_funds", 0.85d) // this one costs more when wrong
.build();
switch (gate.decide(response.choice("intent"))) {
case EXECUTE -> perform(intent);
case CONFIRM -> askTheUserToConfirm(intent);
case ESCALATE -> handOverToAHuman();
}
sequenceDiagram
autonumber
participant App
participant Gate as JevConfidenceGate
participant Human
App->>Gate: decide(response.choice("intent"))
alt confidence below the floor
Gate-->>App: ESCALATE
App->>Human: hand over
else below the action's own requirement
Gate-->>App: CONFIRM
App->>Human: ask to confirm
else at or above the requirement
Gate-->>App: EXECUTE
App->>App: perform(intent)
end
The two thresholds are independent: the floor is about the answer being undecided at all, the per-action requirement is about what the action costs when it is wrong.
| Confidence | Low-stakes action | High-stakes action |
|---|---|---|
| below the floor | escalate | escalate |
| floor … requirement | execute | ask the user to confirm |
| at or above requirement | execute | execute |
Confidence in the judge¶
JevJudge treats a low-confidence criterion as undecided, not
failed. It is reported as INCONCLUSIVE and does not block:
JevJudge judge = JevJudge.builder(typeSafeClient)
.score("helpfulness", rubric, 2.0d)
.minConfidence(0.5d) // below this, a criterion is INCONCLUSIVE
.failOnInconclusive(false) // the default: undecided does not block
.build();
Turn failOnInconclusive(true) on where shipping an unverified answer is worse than
failing. Leaving it off is right when an occasional unverifiable answer is acceptable and
a false rejection is not.
Stability is a different question¶
Confidence describes one answer. It says nothing about whether you would get the same answer again — and a value that lands near a threshold may be noise rather than judgement.
JevConsistency answers that by sampling:
JevConsistency.Report report = JevConsistency.sample(client, state, questions, 15);
// The questions whose samples straddle the threshold you are about to rely on
List<String> shaky = report.unstableAt(0.70d);
A criterion whose samples fall on both sides of its threshold makes a decision that is not reproducible. Either move the threshold, sharpen the question, or route that case to a human.
See Also¶
- JevConfidenceGate — the gate as a reusable policy
- JevConsistency — measuring stability across samples
- The Three Primitives
- TypeSafe confidence documentation