We now have the three symbolic tools of the module: the logic of 06-01 to represent knowledge, the expert system of 06-02 to decide with rules and the Bayesian networks of 06-03 to decide under uncertainty. This lesson takes them out of the NovaMarket laboratory and places them in the world: where rule- and knowledge-based systems have been and are used, what knowledge they encode, why in each case rules are chosen over machine learning (explainability, regulation, little data) and where their limits lie. We will go through medicine, finance, industry, agriculture, law and public administration, education and e-commerce, and see what expert systems have become today: business rule engines, DMN decision tables, knowledge graphs and, above all, the combination with the ML and LLMs we announced at the close of 05-05: the neurosymbolic approach. In code we will build a decision table for routing NovaMarket's customer-service tickets and a runnable neurosymbolic example in which a model estimates the probability of a defect and the rule engine of 06-02 decides, with the human review band of 02-04, whether the warranty is accepted automatically or handed to a person. It matters because this is how rules survive and thrive in today's AI: not competing with the networks, but governing them.

Contents

  1. Overview: where rule-based systems live
  2. Medicine: from MYCIN to clinical alerts
  3. Finance: scoring, regulatory compliance and business rules
  4. Industry and maintenance: diagnosis and configuration
  5. Agriculture, law and public administration, education
  6. Customer service and e-commerce: the NovaMarket case
  7. Summary table: sector, system, knowledge and why rules
  8. Expert systems today: BRMS, DMN, knowledge graphs
  9. Neurosymbolic: rules and models together
  10. Code: DMN decision table for ticket routing
  11. Code: a runnable neurosymbolic example
  12. Common Mistakes and Tips
  13. Exercises
  14. Conclusion

  1. Overview: where rule-based systems live

After the winter of the late 1980s (01-01), the term "expert system" fell out of use, but the technology did not: it was renamed business rule engine, decision support system or decision automation, and it was folded into corporate software. Today there are explicit rules behind almost every repetitive decision with regulatory consequences: whether a bank grants you a card, whether a prescription triggers an alert, whether a tax return is accepted, whether your support ticket goes to one team or another. The reason is the same in every sector and you already know it from this module: when the decision is defined by a regulation or a policy, must be justified case by case, there is little data or the edge cases matter, written rules beat the learned model, or at least accompany it. Let us go through the sectors with that lens: what knowledge is encoded, why rules, and what limits they have.

  1. Medicine: from MYCIN to clinical alerts

  • MYCIN (06-02) never made it to the clinic, but its heirs did: clinical decision support systems (CDSS) integrated into the electronic health record. The most widespread: drug interaction and dosage alerts: rules of the form "IF the patient takes anticoagulant A AND anti-inflammatory B is prescribed THEN bleeding risk alert", with thousands of drug pairs, contraindications by allergy, adjustments for renal function, and clinical protocols (practice guidelines) encoded as decision trees.
  • Knowledge encoded: pharmacology, clinical guidelines, diagnostic criteria; written by expert committees and updated with the evidence.
  • Why rules: legal liability requires being able to say why an alert fired (or did not); clinical guidelines already are rules; serious events are rare (little data to learn them from); medical device regulation requires validation and traceability.
  • Limits: alert fatigue (too many irrelevant alerts that the physician ends up ignoring: the precision problem of 04-05 in human form), maintaining the base at the pace of the evidence, and the inability of rules to handle image diagnosis or risk prediction, where the ML of modules 4 and 5 dominates. The typical combination: a model estimates the risk of sepsis, a rule decides when and whom to alert.

  1. Finance: scoring, regulatory compliance and business rules

  • Rule-based scoring and credit policies: "IF income < X OR job tenure < 6 months THEN deny"; today they coexist with risk models, but the policy rules (minimum age, defaulter lists, regulatory limits) run before and after the model.
  • Regulatory compliance and anti-money laundering (AML): rules for detecting suspicious transactions required by regulation (amounts split just under the reporting threshold, risk countries, cash deposit patterns), know-your-customer (KYC) and sanctions rules. They are encoded literally from the law and from the regulator's circulars.
  • Business rule engines for insurance pricing, commission settlement, product eligibility, handling of simple claims.
  • Why rules: the law is a rule and the regulator asks "why was this transaction flagged?"; the right to an explanation for credit decisions (GDPR, 02-04) requires readable reasons; fraud changes and the rules are edited in hours without retraining.
  • Limits: fraudsters learn the rules and skirt them; many false alarms; thousands of accumulated rules with interactions nobody remembers (XCON's disease). That is why modern systems combine hard rules (regulation) with models (subtle patterns) and with the analysts' review band.

  1. Industry and maintenance: diagnosis and configuration

  • Configuration: XCON (06-02) configured computers; its descendants are product configurators (a car with its compatible options, an industrial machine, a telecoms tariff), where the rules express constraints "this board needs that power supply", a constraint satisfaction problem (03-02) rather than a learning one.
  • Fault diagnosis: decision trees written by engineers for breakdowns of turbines, locomotives, production lines; service manuals are expert systems on paper. With sensors, they are combined with anomaly detection (04-02) that triggers the diagnostic tree.
  • Process control: fuzzy control (06-03) in furnaces, cement plants, air conditioning.
  • Why rules: the knowledge is in the heads of engineers who retire (knowledge preservation); serious failures are scarce; in industrial safety the behaviour must be certifiable.
  • Limits: incomplete coverage of new failures; the trees go stale with every revision of the equipment.

  1. Agriculture, law and public administration, education

  • Agriculture: advisory systems for irrigation, fertilisation and pest treatment ("IF soil moisture < X AND no rain forecast THEN irrigate N litres"), and for diagnosing crop diseases from visible symptoms. They encode agronomy and regulations on the use of plant protection products; today the perception part (leaf photos) is done by a CNN (05-04) and the recommendation part is still rules.
  • Law and public administration: eligibility for grants and benefits, tax calculation, licence processing: the law is encoded in rules and tables (the administration's own forms are decision trees). Tax filing assistants are giant expert systems updated with every budget law. It is the ground where the demand for explanation and equal treatment is highest and where a wrong automated decision has the most consequences (02-04): rules allow auditing and appeal; a model learned from past case files would inherit the biases of those files. As with returns, the regulations are set and reviewed by a legal professional; the system applies them.
  • Education: intelligent tutors (since the 1980s) model the knowledge of the subject and the production rules a student applies when solving a problem (in algebra, for example), compare their steps with the correct rules and the known wrong ones, and adapt the hints. They encode pedagogy and typical errors; today they are combined with student knowledge models and with LLMs for the dialogue.

  1. Customer service and e-commerce: the NovaMarket case

Here NovaMarket recognises itself completely:

  • Promotion rule engines: "3 for 2 on capsules", "free shipping from €49", "coupon not combinable with sales", exclusions by category; thousands of shops run them on every cart. It is pure logic, with conflicts between promotions resolved by priority, exactly as in 06-02.
  • Returns and warranty policies: the case 8 expert system we built.
  • Ticket routing: which team, with what priority and with what automatic actions handles each incident, depending on the type, the amount, the customer and the channel (code in section 10).
  • Fraud prevention: hard rules (more than N orders to the same address with different cards) in front of and behind the case 3 risk model.
  • Assistants: the case 7 chatbot with RAG (05-05) answers questions; but it delegates deciding a return to the rule engine, because an LLM cannot guarantee that it applies the policy.
  • Why rules: the policy belongs to the company and changes with marketing and with the law; every decision towards the customer must be explainable; the edge cases (opened hygiene product, extended warranty) are few and are not learned well.
  • Limits: cause diagnosis (case 9) does not fit crisp rules and needs the network of 06-03; reading reviews and photos needs the networks of module 5.

  1. Summary table: sector, system, knowledge and why rules

Sector Typical system Knowledge encoded Why rules and not (only) ML Main limit
Medicine Drug interaction alerts, protocols (heirs of MYCIN) Pharmacology, clinical guidelines Legal liability, rare events, device regulation Alert fatigue, maintenance
Finance Policy scoring, AML, KYC, pricing Law, regulator's circulars, risk policy Right to explanation, the norm is a rule, fast change Fraudsters skirt the rules; false alarms
Industry Configurators (XCON), diagnostic trees, fuzzy control Product constraints, engineers' experience Preserve knowledge, scarce failures, certification New failures, obsolescence
Agriculture Irrigation/treatment advisor, pest diagnosis Agronomy, plant protection regulations Justifiable recommendations; perception done by the CNN Local variability
Law and public administration Eligibility, taxation, licences Legislation Equal treatment, appeal, audit; bias of past case files Ambiguous law; volume of changes
Education Intelligent tutors Subject rules and typical errors Explainable step-by-step feedback Coverage of student strategies
E-commerce Promotions, returns (case 8), ticket routing, anti-fraud Commercial policy, consumer regulations Own, changing policy; explanation to the customer; edge cases Diagnosis and perception need ML

  1. Expert systems today: BRMS, DMN, knowledge graphs

  • BRMS (Business Rules Management Systems): rule engines with an editor for business users, versioning, testing and deployment: Drools (Java, open source), IBM ODM, FICO Blaze, among others. They are EMYCIN in a corporate suit: they separate the rules (owned by the business, editable by Diego) from the code (owned by IT), with Rete for efficiency and salience for priorities.
  • DMN (Decision Model and Notation, OMG standard): a graphical notation for modelling decisions, whose central piece is the decision table: rows = rules, columns = inputs and outputs, and a hit policy that says what to do if several rows match: UNIQUE (only one may match), FIRST (the first in order), PRIORITY (the one with the highest output priority), COLLECT (all of them, to accumulate actions or add up). It is the conflict resolution of 06-02, standardised and readable by an auditor. We will program it in section 10.
  • Knowledge graphs: the first-order logic of 06-01 at scale: entities and relations (NovaClean —is_a→ vacuum cleaner —category→ home, NovaClean —compatible_with→ bag B-12) with queries and reasoning (ontologies, type inference). Google, Wikidata, product catalogues and drug databases are knowledge graphs; for NovaMarket, a graph of products and compatibilities would give exact answers ("which bags fit my vacuum cleaner?") that an LLM would hallucinate.
  • Workflow engines and RPA that execute rules inside business processes; and spreadsheets, the most widely used expert system in the world, with their nested IF(...)s without trace or tests: precisely what a BRMS fixes.

  1. Neurosymbolic: rules and models together

02-02 announced the neurosymbolic trend and 05-05 left it pending. Now we can be concrete. Networks perceive and estimate; rules represent, decide and guarantee. Three combination patterns, all applicable at NovaMarket:

Pattern Who does what NovaMarket example
ML/LLM → rules (the model feeds the rule) The model estimates a probability or extracts a datum; the rule uses it as a fact and decides with the policy p_defective from the model of modules 4/5 enters the engine of 06-02: automatic warranty, human review or withdrawal (section 11). The CNN of 05-04 classifies the package photo; the network of 06-03 diagnoses
Rules → LLM (the rules govern the model) Rules that validate, filter or correct the outputs of an LLM before they reach the user The case 7 assistant drafts the reply, but the decision on whether a return applies is made by the engine; safety rules block replies that promise refunds or reveal other customers' data; verification that the figure quoted exists in the retrieved document
LLM → knowledge (the model helps build the symbolic part) The LLM reads documents and proposes rules or facts that an expert validates Extract from the returns policy and the regulations a draft of IF ... THEN rules that Diego reviews; extract entities and relations for the product graph; suggest initial CPTs that are then estimated from incidents.csv

And the most important in practice is the first, because it resolves the dilemma of 05-05: exploiting the perception and the probabilities of the models without giving up the explainability and control the AI Act requires. The final decision is a readable rule; the model is one more sensor, with its uncertainty turned into a human review band. There is also research on deeper integration (networks that learn logical rules, differentiable reasoning, LLMs that call theorem provers) that is outside this course; 08-03 mentions it among the trends.

  1. Code: DMN decision table for ticket routing

Every incident that enters NovaMarket's customer service must go to a team with a priority, and sometimes trigger automatic actions. We model it as two decision tables: the routing one, with a "first matching row" policy (FIRST: rows go from most specific to most general, with a final default row), and the actions one, with an "all matching rows" policy (COLLECT: several actions can accumulate). Each input cell can be a value, a set of values, a condition (function) or "any".

ANY = None

def matches(cell, value):
    """Does the input value match the table cell?"""
    if cell is ANY:
        return True
    if callable(cell):                  # condition as a function, e.g. lambda x: x > 300
        return cell(value)
    if isinstance(cell, (set, tuple, list)):
        return value in cell
    return value == cell

INPUTS = ["incident_type", "amount", "vip_customer"]

# Each row: (input cells in the order of INPUTS, outputs)
ROUTING_TABLE = [
    (("product_not_delivered", lambda x: x > 300, ANY),  {"team": "logistics_senior", "priority": "high"}),
    (("product_not_delivered", ANY,               True), {"team": "logistics_senior", "priority": "high"}),
    (("product_not_delivered", ANY,               ANY),  {"team": "logistics",        "priority": "medium"}),
    (("defective_product",     ANY,               ANY),  {"team": "warranties",       "priority": "medium"}),
    (({"damaged_package", "wrong_product"}, ANY,  ANY),  {"team": "warehouse",        "priority": "medium"}),
    (("delay",                 ANY,               True), {"team": "support_vip",      "priority": "medium"}),
    (("delay",                 ANY,               ANY),  {"team": "support",          "priority": "low"}),
    ((ANY,                     ANY,               ANY),  {"team": "support",          "priority": "low"}),   # default
]

# Second table: complementary actions (several rows can apply at once)
ACTIONS_TABLE = [
    ((ANY,                     ANY,               True), {"action": "coupon_5_euros"}),
    ((ANY,                     lambda x: x > 300, ANY),  {"action": "phone_call"}),
    (("product_not_delivered", ANY,               ANY),  {"action": "open_carrier_trace"}),
    (("damaged_package",       ANY,               ANY),  {"action": "request_packaging_photo"}),
]

def evaluate_table(table, ticket, policy="first"):
    """policy='first': return the output of the first matching row (DMN FIRST).
       policy='all': return the list of outputs of every matching row (DMN COLLECT)."""
    hits = []
    for i, (cells, output) in enumerate(table, start=1):
        if all(matches(cell, ticket[inp]) for cell, inp in zip(cells, INPUTS)):
            hits.append((i, output))
            if policy == "first":
                break
    return hits

tickets = [
    dict(id="T-9001", incident_type="product_not_delivered", amount=450, vip_customer=True),
    dict(id="T-9002", incident_type="delay", amount=35, vip_customer=False),
    dict(id="T-9003", incident_type="damaged_package", amount=120, vip_customer=True),
    dict(id="T-9004", incident_type="invoice_query", amount=0, vip_customer=False),
]
for t in tickets:
    row, output = evaluate_table(ROUTING_TABLE, t, "first")[0]
    actions = [o["action"] for _, o in evaluate_table(ACTIONS_TABLE, t, "all")]
    print(f"{t['id']} ({t['incident_type']}, {t['amount']} €, vip={t['vip_customer']}): "
          f"row {row} → {output['team']}/{output['priority']}; actions: {actions or '-'}")

print("\nRouting with the 'all' policy for T-9001:")
for row, output in evaluate_table(ROUTING_TABLE, tickets[0], "all"):
    print(f"  row {row}: {output}")

Output:

T-9001 (product_not_delivered, 450 €, vip=True): row 1 → logistics_senior/high; actions: ['coupon_5_euros', 'phone_call', 'open_carrier_trace']
T-9002 (delay, 35 €, vip=False): row 7 → support/low; actions: -
T-9003 (damaged_package, 120 €, vip=True): row 5 → warehouse/medium; actions: ['coupon_5_euros', 'request_packaging_photo']
T-9004 (invoice_query, 0 €, vip=False): row 8 → support/low; actions: -

Routing with the 'all' policy for T-9001:
  row 1: {'team': 'logistics_senior', 'priority': 'high'}
  row 2: {'team': 'logistics_senior', 'priority': 'high'}
  row 3: {'team': 'logistics', 'priority': 'medium'}
  row 8: {'team': 'support', 'priority': 'low'}

Explanation:

  • matches is the cell matcher: four cell types cover most real DMN tables (any, condition, set, value). evaluate_table goes through the rows in order and applies the policy: with "first" it stops at the first match; with "all" it accumulates them.
  • The routing table with FIRST requires the rows to be ordered from specific to general: ticket T-9001 (not delivered, €450, VIP) matches rows 1, 2, 3 and 8, as the evaluation with "all" shows, and row 1 wins by order. That is the delicate point of FIRST: the order is the priority, and a row inserted in the wrong place silently changes decisions. That is why DMN also offers UNIQUE (which forces only one row to match and detects overlaps at design time) and PRIORITY.
  • The actions table with COLLECT is the natural use of "all that fit": T-9001 accumulates coupon (VIP), call (amount > 300) and carrier trace (not delivered); T-9002 none.
  • T-9004 shows the default row: an incident type that was not foreseen goes to support with low priority instead of being left without a route (the "no applicable rule" of 06-02, resolved by design).

Diego can read and edit these two tables in a spreadsheet; IT can version them and test them with a suite of tickets, like cases A-E of 06-02. That is a BRMS in miniature.

  1. Code: a runnable neurosymbolic example

We close the module with the "model → rules" pattern. A customer requests a return, claiming (or not) a defect. A model (here an already-trained logistic regression, simulated with fixed weights: in production it would be model.predict_proba from 04-04 or the MLP of 05-03, fed with the text of the request, the photo and the product's history) estimates p_defective. That probability enters as one more fact into the rule engine of 06-02, together with three new rules that apply the human review band of 02-04 (THRESHOLD = 0.40, BAND = 0.20) and a fourth that prevents automatically denying a warranty when the customer claims a defect and the model rules it out.

We copy from 06-02 only the essentials (Rule and a compact InferenceEngine without the question interface):

import math, operator

# ---- Minimal pieces of the expert system of 06-02 (same ideas, without the question interface) ----
OPERATORS = {"==": operator.eq, "!=": operator.ne, "<=": operator.le, "<": operator.lt,
             ">=": operator.ge, ">": operator.gt, "in": lambda a, b: a in b}

class Rule:
    def __init__(self, name, conditions, conclusions, priority=0, justification=""):
        self.name, self.conditions, self.conclusions = name, conditions, conclusions
        self.priority, self.justification = priority, justification
    def applicable(self, facts):
        return all(a in facts and OPERATORS[op](facts[a], v) for a, op, v in self.conditions)

class InferenceEngine:
    def __init__(self, rules, facts):
        self.rules, self.facts, self.trace, self.fired = rules, dict(facts), [], set()
    def run(self, goal="decision"):
        while goal not in self.facts:
            candidates = [r for r in self.rules if r.name not in self.fired and r.applicable(self.facts)]
            if not candidates:
                return None
            rule = max(candidates, key=lambda r: (r.priority, len(r.conditions)))   # priority > specificity
            detail = ", ".join(f"{a}={self.facts[a]!r}" for a in dict.fromkeys(a for a, _, _ in rule.conditions))
            self.facts.update(rule.conclusions)
            self.fired.add(rule.name)
            self.trace.append(f"{rule.name} because {detail} → {rule.conclusions} ({rule.justification})")
        return self.facts[goal]

# ---- "Neuro" part: a model that estimates P(defective) from the request ----
WEIGHTS = {"bias": -2.0, "mentions_not_working": 2.2, "mentions_scratch": -1.5,
           "product_defect_rate": 6.0, "days_since_delivery": -0.004, "photo_attached": 0.8}

def p_defective(request):
    """Simulates the model of modules 4/5 (an already-trained logistic regression):
    weighted sum of features + sigmoid. In production it would be model.predict_proba."""
    z = WEIGHTS["bias"] + sum(WEIGHTS[k] * float(request[k]) for k in WEIGHTS if k != "bias")
    return 1 / (1 + math.exp(-z))

# ---- "Symbolic" part: the warranty rules of 06-02 plus those of the review band of 02-04 ----
THRESHOLD, BAND = 0.40, 0.20      # the same values as in 02-04

def neurosymbolic_rules():
    return [
        Rule("N1", [("p_defective", ">=", THRESHOLD + BAND)], {"defective": True}, priority=20,
             justification="the model estimates a defect with high confidence: accepted as a fact"),
        Rule("N2", [("p_defective", ">=", THRESHOLD), ("p_defective", "<", THRESHOLD + BAND)],
             {"decision": "human_review", "reason": "grey zone of the defect model"}, priority=20,
             justification="human review band of 02-04: a person confirms"),
        Rule("N3", [("p_defective", "<", THRESHOLD)], {"defective": False}, priority=20,
             justification="the model rules out the defect with confidence"),
        Rule("N4", [("claims_defect", "==", True), ("defective", "==", False), ("days_since_delivery", ">", 14)],
             {"decision": "human_review", "reason": "the customer claims a defect and the model rules it out"}, priority=15,
             justification="denying a warranty is a decision with significant effects: it is not automated"),
        Rule("R1", [("defective", "==", True), ("new_product", "==", True), ("days_since_delivery", "<=", 1095)],
             {"decision": "warranty", "return_shipping": "free"}, priority=10,
             justification="3-year legal guarantee; shipping paid by NovaMarket"),
        Rule("R2", [("defective", "==", True), ("days_since_delivery", ">", 1095)],
             {"decision": "denied", "reason": "outside the legal guarantee period"}, priority=9,
             justification="after 3 years, paid technical service"),
        Rule("R4", [("days_since_delivery", "<=", 14), ("used", "==", False), ("original_label", "==", True)],
             {"decision": "withdrawal_refund", "return_shipping": "paid by the customer"}, priority=5,
             justification="14-day withdrawal"),
        Rule("R8", [("days_since_delivery", ">", 14), ("defective", "==", False)],
             {"decision": "denied", "reason": "out of period and no defect"}, priority=2,
             justification="Diego's rule"),
    ]

REQUESTS = [
    dict(id="S-101", product="NovaClean vacuum cleaner", claims_defect=True, days_since_delivery=200, used=True, original_label=False, new_product=True,
         mentions_not_working=1, mentions_scratch=0, product_defect_rate=0.12, photo_attached=1),
    dict(id="S-102", product="NovaBrew coffee maker", claims_defect=True, days_since_delivery=45, used=True, original_label=False, new_product=True,
         mentions_not_working=1, mentions_scratch=0, product_defect_rate=0.04, photo_attached=0),
    dict(id="S-103", product="NovaSound headphones", claims_defect=True, days_since_delivery=30, used=True, original_label=True, new_product=True,
         mentions_not_working=0, mentions_scratch=1, product_defect_rate=0.02, photo_attached=0),
    dict(id="S-104", product="NovaView monitor", claims_defect=False, days_since_delivery=6, used=False, original_label=True, new_product=True,
         mentions_not_working=0, mentions_scratch=0, product_defect_rate=0.03, photo_attached=0),
]
for s in REQUESTS:
    facts = dict(s)
    facts["p_defective"] = round(p_defective(s), 3)              # the model's output enters as one more fact
    engine = InferenceEngine(neurosymbolic_rules(), facts)
    decision = engine.run("decision")
    print(f"{s['id']} {s['product']}: p_defective={facts['p_defective']} → {decision} ({engine.facts.get('reason') or engine.facts.get('return_shipping')})")
    for step in engine.trace:
        print("   ", step)

Output:

S-101 NovaClean vacuum cleaner: p_defective=0.715 → warranty (free)
    N1 because p_defective=0.715 → {'defective': True} (the model estimates a defect with high confidence: accepted as a fact)
    R1 because defective=True, new_product=True, days_since_delivery=200 → {'decision': 'warranty', 'return_shipping': 'free'} (3-year legal guarantee; shipping paid by NovaMarket)
S-102 NovaBrew coffee maker: p_defective=0.565 → human_review (grey zone of the defect model)
    N2 because p_defective=0.565 → {'decision': 'human_review', 'reason': 'grey zone of the defect model'} (human review band of 02-04: a person confirms)
S-103 NovaSound headphones: p_defective=0.029 → human_review (the customer claims a defect and the model rules it out)
    N3 because p_defective=0.029 → {'defective': False} (the model rules out the defect with confidence)
    N4 because claims_defect=True, defective=False, days_since_delivery=30 → {'decision': 'human_review', 'reason': 'the customer claims a defect and the model rules it out'} (denying a warranty is a decision with significant effects: it is not automated)
S-104 NovaView monitor: p_defective=0.137 → withdrawal_refund (paid by the customer)
    N3 because p_defective=0.137 → {'defective': False} (the model rules out the defect with confidence)
    R4 because days_since_delivery=6, used=False, original_label=True → {'decision': 'withdrawal_refund', 'return_shipping': 'paid by the customer'} (14-day withdrawal)

Explanation:

  • p_defective is the subsymbolic part: a weighted sum of features (whether the text mentions "not working", whether it mentions a scratch, the product's historical defect rate, the days elapsed, whether there is a photo) passed through the sigmoid, the neuron of 05-01. Its weights are opaque by nature; we do not try to explain them: we treat it as a sensor that returns a probability.
  • Rules N1-N3 turn that probability into symbolic facts with the band of 02-04: ≥ 0.60 is accepted as a defect, between 0.40 and 0.60 goes to human review, < 0.40 is ruled out. They have the highest priority so that they are evaluated before the policy. N4 is a GDPR/AI Act safeguard: if the customer claims a defect and the model rules it out, we do not deny automatically; a person confirms. Without N4, S-103 would go to R8 and be denied on the strength of an opaque calculation, exactly what 02-04 forbids.
  • Then the policy rules of 06-02 (R1, R2, R4, R8) act, unchanged: the final explanation is the chain "N1 (the model estimates it at 0.715) → R1 (legal guarantee, free shipping)", readable by the customer and by the auditor, and in which the model appears with its number and its thresholds.
  • The four cases cover the outcomes: automatic warranty (S-101), grey zone (S-102), claim ruled out by the model → person (S-103) and ordinary withdrawal where the model simply confirms there is no defect (S-104). Changing the thresholds or the rules does not require touching the model; retraining the model does not require touching the rules. That separation is the value of the neurosymbolic approach.

Common Mistakes and Tips

  • Choosing ML "because it's AI". If the decision is defined by a regulation or a policy, write the rule; the model, if anything, estimates the uncertain facts the rule needs.
  • Choosing rules for what nobody can write down. Recognising a photo or forecasting demand does not fit into rules: modules 4 and 5.
  • FIRST tables with carelessly ordered rows. The order is the priority. Test with "all" to see the overlaps, or use UNIQUE when the rows must be mutually exclusive.
  • Rules with no owner and no tests. Every table or rule base needs an owner (Diego), versioning and a suite of cases that runs on every change.
  • Letting the LLM decide. Let it draft, summarise, extract and propose; let the decision with effects on the customer be made by a rule or a person, and let a rule validate what the LLM says.
  • Model thresholds hidden in the code. Thresholds are policy (how much risk is accepted): keep them in explicit, visible rules such as THRESHOLD and BAND, not buried in a function.
  • Forgetting the human review band. The model gets things wrong; the band is where its uncertainty turns into human work instead of into an error towards the customer.

Exercises

Exercise 1. Add to ROUTING_TABLE a new rule Diego asks for: defective_product tickets with an amount above €500 must go to warranties_senior with high priority. In which position must the row go for the "first" policy to work? Check with a €700 ticket and an €80 one and explain what would happen if you put it at the end.

Exercise 2. Run the neurosymbolic example with request S-105: NovaSound headphones, claims_defect=False, 30 days, used, with label, and the same model features as S-103. What does the system decide and why is it different from S-103? Then change BAND to 0.40 and run the four original cases again: which requests change decision? What is the cost and what is the benefit of widening the band?

Exercise 3. For the case 7 assistant (LLM with RAG, 05-05), design without code three output validation rules for the model before showing its answer to the customer (the "rules → LLM" pattern of section 9): what each one checks, what it does if it fails and which module of the course the need comes from. Then indicate one task in which you would use the "LLM → knowledge" pattern for the returns system and what human control you would put in place.

Solutions

Solution 1. The row (("defective_product", lambda x: x > 500, ANY), {"team": "warranties_senior", "priority": "high"}) must go before the general defective_product row (currently row 4), because it is more specific; placed at the end it would never be reached with the "first" policy: row 4 matches first and the €700 ticket would go to warranties/medium. With the row in its place, the €700 one goes to warranties_senior/high and the €80 one to warranties/medium. It is the same principle as the specificity of 06-02, only in DMN FIRST you apply it yourself through the order.

Solution 2. S-105 gets p_defective = 0.029 (same features as S-103), N3 sets defective = False, and since the customer does not claim a defect N4 does not apply; with 30 days and no defect, R8 denies: "out of period and no defect", Diego's rule, now backed by the model. It is different from S-103 because there the customer claimed a defect and automatically denying against that claim requires a person. With BAND = 0.40 the grey zone becomes [0.40, 0.80): S-101 (0.715) no longer goes to automatic warranty and moves to human review; S-102 stays in review; S-103 and S-104 do not change. Benefit: fewer warranties accepted through model error; cost: more human work and more waiting for customers with clear defects. The width of the band is a business decision made with the cost matrix of 04-05, not a technical parameter.

Solution 3. Example validation rules: (1) "IF the answer contains a figure in days or euros AND that figure does not appear in the retrieved chunks THEN replace it with the generic answer with a link to the policy and log it" (hallucinations, 05-05); (2) "IF the answer contains a commitment to refund or to accept a warranty THEN remove it and refer the decision to the rule engine of 06-02" (the LLM does not decide, 02-04); (3) "IF the answer contains personal data that do not belong to the authenticated customer (another name, another order number) THEN block and alert" (GDPR, 02-03). "LLM → knowledge" pattern: ask the LLM to read the current returns policy and the applicable regulations and propose a draft of IF ... THEN rules with their justification and examples; human control: Diego validates each rule, a legal professional reviews those that depend on the regulations, and the suite of cases A-E (and the new ones) is run before activating any proposed rule.

Conclusion

In this lesson we saw that rule- and knowledge-based systems are not a relic of the 1980s but the decision layer of most business systems: drug alerts and protocols in medicine, policy scoring and anti-money laundering in finance, configurators and diagnostic trees in industry, agricultural advisors, eligibility and taxation in public administration, intelligent tutors and, in NovaMarket's e-commerce, promotions, returns, ticket routing and anti-fraud; in all of them, rules because the norm commands, explanation is required, data is scarce or the edge cases matter, and with the limits of maintenance, coverage and uncertainty we already knew. We placed their current forms (BRMS such as Drools, DMN decision tables with their hit policies, knowledge graphs) and made the neurosymbolic approach concrete in three patterns: the model feeds the rule, the rule governs the LLM, and the LLM helps build the knowledge. In code, the decision table routed NovaMarket's tickets with FIRST and COLLECT, and the neurosymbolic example turned a model's p_defective into a fact of the 06-02 engine, deciding automatic warranty, human review or withdrawal with the band of 02-04 and a trace that cites the model and the rule.

With this we close module 6 and, with it, the conceptual journey through the two halves of AI. You now know how to represent knowledge with logic and chain rules (06-01), build an expert system with priorities, explanation and interface (06-02), reason with probability and Bayesian networks and decide by expected utility (06-03) and combine rules with models where each performs best (06-04). Marta and Diego now have the rules of case 8, the diagnosis network of case 9 and the judgement to know which part of each problem goes to rules, which to ML and which to a person. Notice how we did it: the whole module in pure Python, with dictionaries, lists, itertools and fractions, whereas in modules 4 and 5 we used scikit-learn and PyTorch, and we have been mentioning in passing Prolog, CLIPS, Drools, experta or pgmpy without installing them. It is time to put that ecosystem in order: which languages are used in AI and why Python dominates, what NumPy, pandas and Matplotlib contribute, which libraries exist for each task we have seen and in which environments people work. That is module 7, Tools and Programming Languages in AI, which starts in 07-01 comparing Python with Prolog, Lisp, R, Julia and the production languages.

Fundamentals of Artificial Intelligence (AI)

Module 1: Introduction to Artificial Intelligence

Module 2: Basic Principles of AI

Module 3: Algorithms in AI

Module 4: Machine Learning

Module 5: Neural Networks and Deep Learning

Module 6: Logic and Expert Systems

Module 7: Tools and Programming Languages in AI

Module 8: Projects and Case Studies

Module 9: Exercises and Practice

Module 10: Additional Resources

© Copyright 2026. All rights reserved