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 narrativereasoning_path: StructuredReasoningPathwith 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
Reasonerwithmax_iterationsand load rules/facts viaadd_rule()andadd_fact() - Execute forward chaining with
forward_chain()orinfer_facts()to produceInferenceResultobjects - Transform any result type—
InferenceResult,Proof, orExplanation—into natural language usingExplanationGenerator.generate_explanation() - Select
detail_level("compact", "detailed", "verbose") to control explanation granularity - Audit complete reasoning paths through
explanation.reasoning_pathfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →