Sunday 14 December 2025, 05:52 PM
Building expert systems for real world decision making
Expert systems deliver fast, explainable, auditable decisions; pair rules with ML, design clear ontologies, traceability, and human-in-loop to manage risk.
Why expert systems still matter
Expert systems sound a bit retro in a world obsessed with deep learning, but they’re still quietly running the show in many places that demand reliable, explainable decisions. Think loan underwriting, clinical triage, compliance checks, troubleshooting industrial equipment, and approving refunds at scale. The core promise is simple: capture the best of human expertise in a consistent, auditable form so decisions are fast, fair, and explainable.
And here’s the kicker: expert systems play nicely with machine learning. Rules handle policy, safety, and domain logic; models handle predictions and ranking. Together, they give you a decision you can actually defend. That’s a big deal when you’re dealing with real people, real money, or real risk.
What is an expert system
An expert system is software that mimics how a domain expert makes decisions. It has a knowledge base of rules, an inference engine that figures out which rules apply, a working memory of facts about the current case, and an explanation layer so it can tell you why it did what it did.
- Knowledge base: The rules, thresholds, definitions, and exceptions.
- Working memory: Known facts for the specific case (e.g., “age=62,” “credit_history=thin”).
- Inference engine: Applies rules to facts to produce new facts or decisions.
- Explanation: How and why it arrived at the conclusion (the “because” part).
- Interface: UI or API to feed facts in and get results out.
If that sounds like a fancy flowchart, you’re not far off. The twist is you can update and expand it without rewriting the whole thing, and it can explain itself step-by-step.
When to use rules instead of pure AI
You don’t need an expert system for everything. But there are sweet spots where rules shine:
- You need auditable, explainable decisions.
- Policy or regulations drive the outcome.
- There’s limited data to train a model, or the outcome space is small and well-defined.
- Cost of a wrong answer is high.
- You need consistent outcomes across humans and time.
- Domain expertise exists but is scattered in people’s heads or PDFs.
Use machine learning when you need pattern recognition, anomaly detection, or prediction from high-dimensional data. Use rules for policies, guardrails, and “if X then Y” logic. Often the best system uses both: models generate signals, rules orchestrate decisions.
Core components you need
- Concepts and ontology: Clear definitions for terms and values. What exactly counts as “high risk”? What’s a “customer complaint”? Agree on terms first.
- Decision schema: The outputs and their types (approve/deny/escalate, triage levels, risk buckets).
- Rule engine: Applies rules in an order and handles conflicts.
- Facts pipeline: Collects and normalizes inputs (from forms, databases, ML services).
- Explanation builder: Traces which rules fired and why.
- Feedback loop: Users can appeal, add context, or flag errors.
- Change management: Versioning, approvals, and rollout control.
Choosing a reasoning strategy
There are two classic strategies:
- Forward chaining: Start with facts, apply rules to derive new facts, repeat until done. Great for processing streams of input and classification.
- Backward chaining: Start with a hypothesis (“approve loan”) and work backward to see if requirements are met. Great for troubleshooting or Q&A.
Many real systems are hybrid. A common design is forward chaining for quick wins and automatic checks, then backward chaining to ask the user targeted questions only if needed.
Handling uncertainty without losing trust
Real life is messy. Inputs can be incomplete or conflicting. You have options:
- Certainty factors: Rules contribute weighted evidence toward a conclusion. Simple and intuitive for experts.
- Probability: Use Bayesian updates if you have reliable base rates and likelihoods.
- Fuzzy logic: Good when concepts are inherently vague (“temperature is high”).
- Thresholds and bands: Set decision thresholds and a gray zone that triggers human review.
- Conflict resolution: When rules disagree, resolve by specificity, recency, or priority (salience).
Whatever you choose, make the uncertainty visible to users and downstream systems. Provide confidence scores and reasons, not just yes/no.
Designing your knowledge
Structure matters. A well-shaped knowledge base is easier to maintain and safer to change.
- Ontology: Define entities (customer, device, claim), attributes, and relationships.
- Decision tables: Where conditions map neatly to outcomes. They’re easy to read and test.
- Rule forms: Use consistent templates, e.g., “If conditions, then conclusion BECAUSE rationale.”
- Taxonomy of exceptions: Naming and grouping exceptions helps governance.
- Guardrails vs heuristics: Separate hard rules (legal/policy) from soft guidance (expert judgment). Treat them differently in explanations and testing.
Capturing expertise from people
Experts don’t think in rules; they tell stories. Your job is to extract decision patterns.
Try these techniques:
- Think-aloud: Ask experts to narrate their reasoning as they solve cases.
- Contrasting cases: Show borderline examples and ask what tips the scale.
- 20 questions: Start with a decision and ask the minimum questions needed to reach it.
- Card sorting: Sort conditions by importance or decision stage.
- Shadowing and logs: Mine historical decisions and explanations.
- Red team: Ask another expert to challenge the rules for edge cases.
Document not just what they decide, but why and in what order they consider information.
Writing good rules
A few guidelines keep rules crisp and maintainable:
- One purpose per rule: Don’t cram multiple decisions into one rule.
- Preconditions explicit: Don’t rely on implicit assumptions from other rules.
- Concrete language: Avoid vague terms unless using fuzzy logic.
- Normalized values: Standardize units, categories, and missing-data handling.
- Avoid hidden dependencies: If rule B needs rule A, say so with preconditions or priority.
- Prefer specific over general: Specific rules should override broad heuristics.
- Add rationale: A short “because” clause helps explanations.
Example templates:
- Eligibility: If age ≥ 18 and identity_verified, then eligible_because_of_basic_criteria.
- Risk: If three or more late_payments in 12 months, then risk = high because repayment history poor.
- Escalation: If result uncertain and transaction_amount > $10k, then escalate_to_human.
Building a minimal rule engine
You don’t have to reinvent the wheel, but a small engine helps you prototype and learn. Here’s a tiny forward-chaining engine in Python with explanation traces and simple priorities. It’s not production-ready, but it illustrates the mechanics.
from typing import Callable, Dict, List, Tuple
Fact = Tuple[str, any] # e.g., ("age", 62) or ("risk", "high")
Explanation = List[str]
class Rule:
def __init__(self, name: str, salience: int, when: Callable[[Dict[str, any]], bool],
then: Callable[[Dict[str, any]], Tuple[List[Fact], str]]):
self.name = name
self.salience = salience
self.when = when # returns True if rule applies
self.then = then # returns (new_facts, rationale)
def run_engine(initial_facts: Dict[str, any], rules: List[Rule], max_cycles: int = 50):
facts = dict(initial_facts)
fired = []
for _ in range(max_cycles):
applicable = [r for r in rules if r.when(facts)]
if not applicable:
break
# resolve conflicts by salience (higher first), then name for stability
applicable.sort(key=lambda r: (-r.salience, r.name))
rule = applicable[0]
new_facts, rationale = rule.then(facts)
# merge new facts; detect changes
changed = False
for k, v in new_facts:
if facts.get(k) != v:
facts[k] = v
changed = True
fired.append(f"{rule.name}: {rationale}")
if not changed:
# avoid infinite loops
break
return facts, fired
# Example rules
rules = [
Rule(
name="basic-eligibility",
salience=10,
when=lambda f: f.get("age", 0) >= 18 and f.get("identity_verified", False) is True and "eligible" not in f,
then=lambda f: ([("eligible", True)], "Age >= 18 and identity verified.")
),
Rule(
name="high-risk-history",
salience=8,
when=lambda f: f.get("late_payments_12m", 0) >= 3 and f.get("risk") != "high",
then=lambda f: ([("risk", "high")], "Three or more late payments in last 12 months.")
),
Rule(
name="medium-risk-history",
salience=7,
when=lambda f: 1 <= f.get("late_payments_12m", 0) < 3 and f.get("risk") not in ("high", "medium"),
then=lambda f: ([("risk", "medium")], "One to two late payments in last 12 months.")
),
Rule(
name="approval-decision",
salience=5,
when=lambda f: f.get("eligible") is True and f.get("risk") in ("low", None) and "decision" not in f,
then=lambda f: ([("decision", "approve")], "Eligible with low risk.")
),
Rule(
name="manual-review",
salience=5,
when=lambda f: f.get("eligible") is True and f.get("risk") in ("medium", "high") and "decision" not in f,
then=lambda f: ([("decision", "review")], "Eligible but risk not low; manual review needed.")
),
Rule(
name="reject-if-ineligible",
salience=9,
when=lambda f: f.get("eligible") is False and "decision" not in f,
then=lambda f: ([("decision", "reject")], "Not eligible by basic criteria.")
),
Rule(
name="default-eligible-false",
salience=3,
when=lambda f: "eligible" not in f and f.get("age") is not None and f.get("identity_verified") is not None,
then=lambda f: ([("eligible", False)], "Did not meet eligibility rule; default to not eligible.")
)
]
if __name__ == "__main__":
input_facts = {"age": 29, "identity_verified": True, "late_payments_12m": 2, "risk": "low"}
facts, trace = run_engine(input_facts, rules)
print("Decision facts:", facts)
print("Trace:")
for step in trace:
print(" -", step)
In a real system, you’d also handle:
- Certainty or confidence for each rule.
- Backward chaining questions to fill missing facts.
- Rule grouping, versioning, and per-tenant configuration.
- Performance and concurrency.
But this skeleton is enough to show how facts evolve and how explanations get built.
Testing, validation, and traceability
Treat your expert system like critical infrastructure, even if it starts small.
- Unit tests for rules: For each rule, create positive, negative, and edge cases.
- Decision set tests: Define canonical scenarios and expected outcomes. Put them under version control.
- Mutation tests: Randomly tweak inputs to find brittle logic.
- Golden traces: Store explanation traces alongside outcomes so differences are visible in code reviews.
- Shadow mode: Run the system alongside humans or legacy logic before you flip the switch.
- Bias checks: Slice results by demographic or segment where appropriate and lawful. Check false positives and false negatives separately.
Always log the exact rule version, inputs, and outputs so you can reconstruct any decision later. That’s your audit trail.
Keeping humans in the loop
Expert systems thrive when they collaborate with people.
- Gray zones: Define confidence thresholds or business thresholds that trigger human review.
- Overrides: Allow humans to override decisions with a reason. Capture those reasons to improve rules.
- Progressive disclosure: Show a simple explanation by default; let users drill down into details if needed.
- Conversational questioning: If backward chaining needs more facts, ask targeted, minimal questions instead of long forms.
- Safety stops: Escalate anytime the system’s inputs look off (e.g., missing critical facts, contradictory signals).
The goal isn’t to replace experts; it’s to amplify their reach and give them superpowers.
Operational concerns
Moving from a prototype to production brings a different set of challenges.
- Performance: Precompute common derivations; cache stable facts; avoid O(n^2) rule scans by indexing conditions.
- Idempotency: Running the engine twice should not produce different results unpredictably.
- Concurrency: Lock facts or use immutable snapshots per decision to avoid cross-talk.
- Deployment: Version rules independently from code. A change in rules shouldn’t require a code deploy.
- Observability: Metrics for throughput, latency, % auto-approved, % escalated, top rules firing, top overrides.
- Alerting: Watch for spikes in rejects, escalations, or unknown states.
- Resilience: Timeouts and fallbacks if upstream data or ML services are unavailable.
Governance, ethics, and safety
If your decisions affect people, you need governance from day one.
- Policy separation: Encode hard constraints clearly and keep them easy to audit.
- Documentation: For each rule, list owner, rationale, data dependencies, and validation tests.
- Approval workflow: Require sign-off from domain owners for policy changes.
- Fairness and bias: Regularly audit decisions for disparate impact where appropriate and lawful.
- Transparency: Provide plain-language explanations and accessible appeal paths.
- Data minimization: Only collect what you need; handle missing data safely rather than guessing wildly.
- Kill switch: Ability to roll back or pause a rule set quickly.
Good governance isn’t red tape; it’s how you earn and keep trust.
Maintaining and evolving the system
Expert systems aren’t “set and forget.” They’re living documents.
- Change logs: Every rule change has a ticket, reason, and test updates.
- Versioning: Tag rule sets and store them in source control.
- Scheduled reviews: Calendar reviews with domain experts to confirm thresholds still make sense.
- Drift detection: Compare distribution of inputs and outcomes over time; alert on shifts.
- Retire dead rules: Measure rule utility. If a rule never fires, archive it.
- Modularize: Group rules by subdomain (eligibility, risk, pricing) so teams can own parts.
A healthy operational cadence keeps the system aligned with reality.
A realistic rollout plan
Here’s a pragmatic way to bring an expert system to life:
- Pick one decision. Scope tightly. Write the desired outputs and guardrails first.
- Gather 50–200 real cases. Label them with “what should happen” and why.
- Draft the ontology. Define terms and allowed values.
- Build a small rule set with decision tables for the obvious cases.
- Add a basic engine and explanation trace (even if it’s just a spreadsheet or proto code).
- Test on your labeled cases. Iterate on gaps and contradictions.
- Pilot in shadow mode. Compare to current process. Measure impact and disagreements.
- Introduce humans in the loop for gray cases. Capture overrides and feedback.
- Operationalize: logging, versioning, approvals, metrics.
- Expand scope gradually. Add ML signals where they help and rules where they protect.
The fastest way to stall is to aim for a grand, universal ontology. Start specific, get wins, expand carefully.
Common mistakes and how to avoid them
- Giant rules: Break them into smaller, testable ones. If you can’t name it, split it.
- Hidden thresholds: Make thresholds explicit and configurable. Document the rationale.
- Copy-pasting text policy: Convert prose to structured conditions; prose is ambiguous.
- Ignoring missing data: Define default behaviors and confidence handling upfront.
- Overfitting to history: Past decisions may include bias. Validate with policy and outcomes, not just “what we did before.”
- No feedback loop: Give users a way to correct decisions and route feedback to rule owners.
- Untested exceptions: Edge cases matter most. Budget time to hunt them.
- Mixing policy and heuristics: Keep them separate so policy changes don’t break heuristics and vice versa.
Final thoughts
Expert systems aren’t flashy, but they’re practical, approachable, and incredibly useful when you need real-world decisions you can trust. They turn scattered expertise into consistent outcomes, make compliance easier, and complement AI rather than competing with it.
If you take nothing else away, remember this:
- Start small with one decision and a clear output schema.
- Shape your knowledge carefully—terms, thresholds, and exceptions matter.
- Keep explanations first-class; they’re your safety net and your sales pitch.
- Embrace a human in the loop for uncertainty and change.
- Treat rules like code: test them, version them, and observe them in production.
Build with care, and you’ll have a decision engine that feels less like a black box and more like a well-trained teammate—fast, fair, and happily accountable.