How to Implement Explainable Reasoning in Semantica: A Complete Guide to Transparent AI Inference

To implement explainable reasoning in Semantica, instantiate a Reasoner for inference, then pass the resulting InferenceResult, Proof, or Explanation objects to an ExplanationGenerator to produce natural-language narratives and structured reasoning paths.

Semantica's layered reasoning stack converts raw logical inference into human-readable justifications. This article walks through the complete implementation using actual source files from the semantica-agi/semantica repository, covering deductive forward-chaining, formal proofs, and abductive hypothesis generation.

Core Components for Explainable Reasoning

Semantica's explainability architecture rests on four specialized classes:

Component File Path Purpose
Reasoner semantica/reasoning/reasoner.py Unified forward/backward-chaining engine for rule-based inference
ExplanationGenerator semantica/reasoning/explanation_generator.py Converts inference outputs to natural language with full reasoning paths
DeductiveReasoner semantica/reasoning/deductive_reasoner.py Generates formal Proof objects for logical verification
AbductiveReasoner semantica/reasoning/abductive_reasoner.py Creates and ranks explanatory hypotheses from observations

All components share progress tracking and logging infrastructure via utils/progress_tracker.py and utils/logging.py for audit trails.

Step 1: Initialize the Reasoner

Start by creating a Reasoner instance with optional iteration limits:

from semantica.reasoning import Reasoner

reasoner = Reasoner(max_iterations=100)

The Reasoner stores facts as strings (e.g., Person(John)) and rules as Rule dataclasses. Rule parsing occurs in Reasoner._parse_rule_definition at line 51 of reasoner.py.

Step 2: Define Rules and Facts

Add IF-THEN Rules

rule = "IF Person(?x) AND LivesIn(?x, ?city) THEN CitizenOf(?x, ?city)"
reasoner.add_rule(rule)

The add_rule method automatically deduplicates identical rules.

Register Base Facts

facts = [
    "Person(John)",
    "LivesIn(John, Berlin)",
    "Person(Alice)",
    "LivesIn(Alice, Paris)"
]
for f in facts:
    reasoner.add_fact(f)

Step 3: Run Inference

Execute forward chaining to derive new facts:

new_facts = reasoner.infer_facts(facts)
print(new_facts)

# → ['CitizenOf(John, Berlin)', 'CitizenOf(Alice, Paris)']

This returns conclusions only. The full trace—including which rules fired and which premises were used—remains accessible as InferenceResult objects.

Step 4: Generate Natural-Language Explanations

This is the critical step for explainable reasoning in Semantica: transform technical inference data into comprehensible output.

from semantica.reasoning import ExplanationGenerator

explain_gen = ExplanationGenerator(detail_level="detailed")

# Retrieve first inference result from forward chaining

inference_result = reasoner.forward_chain()[0]

explanation = explain_gen.generate_explanation(inference_result)
print(explanation.natural_language)

Example output:


Given the premises: Person(John), LivesIn(John, Berlin), we conclude: CitizenOf(John, Berlin) using rule 'rule_1'.

The Explanation object contains:

  • natural_language: Human-readable narrative
  • reasoning_path: Structured ReasoningPath with premises, applied rule, and conclusion

The path construction logic resides in ExplanationGenerator._explain_inference_result at line 90 of explanation_generator.py.

Step 5: Generate Formal Proofs for Auditability

For deductive reasoning with formal verification:

from semantica.reasoning import DeductiveReasoner, Premise

deductive = DeductiveReasoner()

premises = [
    Premise("p1", "All humans are mortal"),
    Premise("p2", "Socrates is human")
]
conclusions = deductive.apply_logic(premises)

proof = deductive.prove_theorem("Socrates is mortal")
print(proof.valid)  # → True

Feed the Proof into the same generator:

proof_expl = explain_gen.generate_explanation(proof)
print(proof_expl.natural_language)

This produces a validated logical argument with step-by-step justification.

Step 6: Explain Abductive Hypotheses

For observation-to-best-explanation inference:

from semantica.reasoning import AbductiveReasoner, Observation

abductive = AbductiveReasoner(ranking_strategy="simplicity")

obs = Observation("obs1", "Person(John) is in City(Berlin)")
hypotheses = abductive.generate_hypotheses([obs])
best = abductive.rank_hypotheses(hypotheses)[0]

print(best.explanation)  # → "Hypothesis based on rule: ..."

The ExplanationGenerator handles abductive results seamlessly:

abduct_expl = explain_gen.generate_explanation(best)
print(abduct_expl.natural_language)

Complete Working Example

from semantica.reasoning import (
    Reasoner,
    ExplanationGenerator,
    DeductiveReasoner,
    AbductiveReasoner,
    Observation,
    Premise,
)

# Initialize components

reasoner = Reasoner(max_iterations=50)
explain = ExplanationGenerator(detail_level="verbose")
deductive = DeductiveReasoner()
abductive = AbductiveReasoner(ranking_strategy="plausibility")

# Load rule chain

rules = [
    "IF Person(?x) AND LivesIn(?x, ?city) THEN CitizenOf(?x, ?city)",
    "IF CitizenOf(?x, ?city) THEN EligibleToVote(?x, ?city)",
]
for r in rules:
    reasoner.add_rule(r)

# Add base facts

facts = ["Person(John)", "LivesIn(John, Berlin)"]
for f in facts:
    reasoner.add_fact(f)

# Forward-chain and explain each inference

print("=== FORWARD CHAINING EXPLANATIONS ===")
inferred = reasoner.forward_chain()
for res in inferred:
    expl = explain.generate_explanation(res)
    print(f"\nInference: {res.conclusion}")
    print(f"Explanation: {expl.natural_language}")

# Deductive proof

print("\n=== DEDUCTIVE PROOF ===")
prem = [
    Premise("p1", "All humans are mortal"),
    Premise("p2", "Socrates is human")
]
deductive.apply_logic(prem)
proof = deductive.prove_theorem("Socrates is mortal")
proof_expl = explain.generate_explanation(proof)
print(f"Valid: {proof.valid}")
print(f"Explanation: {proof_expl.natural_language}")

# Abductive reasoning

print("\n=== ABDUCTIVE EXPLANATION ===")
obs = Observation("obs1", "EligibleToVote(John, Berlin)")
abduct_hyp = abductive.generate_hypotheses([obs], reasoner=reasoner)
if abduct_hyp:
    best_hyp = abductive.rank_hypotheses(abduct_hyp)[0]
    abduct_expl = explain.generate_explanation(best_hyp)
    print(f"Best hypothesis: {abduct_expl.natural_language}")

Key Source Files for Explainable Reasoning

File Function Critical Methods
semantica/reasoning/reasoner.py Core inference engine add_rule(), add_fact(), forward_chain(), infer_facts()
semantica/reasoning/explanation_generator.py Explanation construction generate_explanation(), _explain_inference_result()
semantica/reasoning/deductive_reasoner.py Formal proof generation apply_logic(), prove_theorem()
semantica/reasoning/abductive_reasoner.py Hypothesis generation generate_hypotheses(), rank_hypotheses()
semantica/utils/progress_tracker.py Execution tracing start_tracking(), update_tracking(), stop_tracking()
semantica/utils/logging.py Audit logging get_logger()

Summary

  • Initialize a Reasoner with max_iterations and load rules/facts via add_rule() and add_fact()
  • Execute forward chaining with forward_chain() or infer_facts() to produce InferenceResult objects
  • Transform any result type—InferenceResult, Proof, or Explanation—into natural language using ExplanationGenerator.generate_explanation()
  • Select detail_level ("compact", "detailed", "verbose") to control explanation granularity
  • Audit complete reasoning paths through explanation.reasoning_path for compliance or debugging

Frequently Asked Questions

What explanation detail levels does Semantica support?

Semantica supports three levels: "compact" for brief summaries, "detailed" for standard narratives with rule citations, and "verbose" for complete premise-to-conclusion traces including intermediate steps. Pass the desired level when instantiating ExplanationGenerator.

Can Semantica explain backward-chaining inferences?

Yes. The Reasoner.forward_chain() method shown above returns InferenceResult objects, but the same ExplanationGenerator accepts results from any inference mode. The generator's generate_explanation() method handles all reasoning types uniformly through polymorphic dispatch.

How does formal proof generation differ from standard inference?

Standard inference (Reasoner.infer_facts()) derives new facts efficiently. Formal proof generation (DeductiveReasoner.prove_theorem()) produces a Proof object with explicit logical validation you can verify independently. The Proof.valid boolean and step-by-step structure support mathematical rigor and regulatory compliance.

What ranking strategies work with abductive reasoning?

The AbductiveReasoner accepts ranking_strategy="simplicity" for Occam's razor preference, "plausibility" for prior-weighted scoring, or custom strategies. This determines which hypothesis the generator explains when multiple explanations fit an observation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →