Blog Post
When a Run Produces Nothing, Read the Proof
The worst failure in a rule engine is not a wrong answer. It is no answer.
A wrong answer leaves evidence. A rule fired, values changed, and the trace shows you which rule produced what. You can argue with it. But when a run comes back with an empty result and a clean log, there is nothing to argue with. Nothing matched, and the run has no opinion about why.
Everyone has debugged this the same way: comment out conditions until something fires, then work forward from there. It finds the answer eventually. It also takes an afternoon.
Silence is a shape
The reason “nothing fired” is hard to diagnose is that a forward-chaining run has no notion of what it was trying to do. It evaluates rules against the facts it has. If none of them match, it has done its job correctly and has nothing further to say.
Goal-driven evaluation gives the run an intention. You name the key you want established, and the engine works backwards to find the rules that could produce it. Which means that when it fails, it fails somewhere specific, and it knows where.
The tree names the missing input
A goal-driven run records a proof tree per goal: the goal at the top, the candidate rules considered for it, and the sub-goals their premises spawned nested underneath. When a goal cannot be established, that tree is a diagnosis.
premiumRate no rule that writes this key matched
Rate From Employment s0 did not match
employmentStatus nothing writes this key
Three lines, and the answer is in the last one. The goal failed because its only candidate did not match. The candidate did not match because a key its condition reads is not supplied by your inputs, not produced by any rule, and not a derived fact. It was never going to match, on any input, on any day.
The deepest node reading nothing writes this key is nearly always the thing to fix. Everything above it is downstream consequence.
The near misses are recorded too
A goal that fails because no candidate matched is one story. A goal that fails with candidates that did fire is a different one, and the tree distinguishes them:
- did not match - the rule was considered and its condition was false.
- fired but did not set the key - the rule ran, but the branch it took wrote something else. Usually a conditional write inside an action.
- already fired with these inputs - the rule had run earlier in this same run with identical inputs, so it was suppressed.
- not tried, activation limit reached - the run hit its cap before reaching this candidate.
Each of those points at a different fix, and none of them is discoverable from an empty output.
Do it before you ship, too
The Verify tab catches the same class of problem without running anything. Point a goal-driven ruleset at it and it reports a goal nothing can establish as an error, because every run will report it unproven. It also flags rules whose written keys cannot be determined without running them, since the backwards search cannot see those and a goal only that rule could establish will look unreachable.
Verify reads the rule set as it stands in the editor, including unsaved edits. Between the tree at run time and the check at authoring time, “nothing happened” stops being a mystery you debug and becomes a finding you read.
Explainability cuts both ways
Most of the industry talks about explainability as a property of correct answers: this decision was made, here is why. That is the easy half. The half that costs people afternoons is explaining the decision that never got made.
A proof tree does both, because it is a record of the search, not a record of the output. It is as informative when the search fails as when it succeeds.
Visit RuleEngine.ai to try it.
The AI Rule Engine Team