Semantica ExplanationGenerator Implementation for Traceable Reasoning
Semantica's ExplanationGenerator converts raw reasoning artifacts into human-readable, auditable reports through a modular architecture built on structured dataclasses, type-specific dispatch methods, and integrated progress tracking.
The ExplanationGenerator is the core component in the semantica-agi/semantica repository that transforms outputs from logical inference engines into transparent, explainable results. Located in semantica/reasoning/explanation_generator.py, this class bridges the gap between machine reasoning and human understanding by capturing every step of the inference process with full traceability.
Core Dataclasses for Structured Explanation Output
The implementation defines four layered dataclasses that create a complete audit trail from individual reasoning steps to final natural-language narratives.
ReasoningStep: Atomic Logical Operations
The ReasoningStep dataclass captures a single logical move with complete provenance:
# Defined in semantica/reasoning/explanation_generator.py, lines 44-55
@dataclass
class ReasoningStep:
step_id: str
description: str
rule_applied: Optional[str] # Reference to the rule or axiom used
input_facts: List[Fact]
output_fact: Fact
confidence: float # 0.0 to 1.0
metadata: Dict[str, Any] = field(default_factory=dict)
Every field serves traceability: rule_applied links to source knowledge, input_facts and output_fact establish data lineage, and confidence enables uncertainty quantification throughout the chain.
ReasoningPath: Complete Inference Chains
ReasoningPath aggregates multiple steps into a traversable sequence:
# Defined in semantica/reasoning/explanation_generator.py, lines 57-66
@dataclass
class ReasoningPath:
path_id: str
steps: List[ReasoningStep]
start_facts: List[Fact]
final_conclusion: Fact
total_confidence: float # Aggregated from step confidences
metadata: Dict[str, Any] = field(default_factory=dict)
This structure enables backward tracing from any conclusion to its foundational premises and forward simulation of how evidence propagates through rules.
Justification and Explanation: Human-Readable Output
The Justification dataclass bundles structured evidence with natural-language interpretation, while the top-level Explanation object provides the complete deliverable:
# Justification: lines 69-78
@dataclass
class Justification:
conclusion: Fact
reasoning_path: ReasoningPath
supporting_evidence: List[Evidence]
overall_confidence: float
explanation_text: str # Generated natural-language rationale
# Explanation: lines 82-92
@dataclass
class Explanation:
explanation_id: str
explanation_type: Literal["inference", "proof", "abductive", "generic"]
conclusion: Fact
reasoning_path: Optional[ReasoningPath]
justification: Optional[Justification]
natural_language: str # Full narrative summary
metadata: Dict[str, Any] = field(default_factory=dict)
The explanation_type field explicitly tags the reasoning paradigm used, enabling downstream systems to apply appropriate trust models for forward chaining, deductive proofs, or abductive hypotheses.
Initialization and Configuration
The generator supports runtime tuning via constructor parameters:
# Constructor implementation: lines 95-128
def __init__(
self,
config: Optional[Dict[str, Any]] = None,
*,
generate_nl: bool = True, # Enable natural-language generation
detail_level: str = "standard", # "minimal", "standard", or "detailed"
progress_tracker: Optional[ProgressTracker] = None
):
The progress_tracker parameter wires observability into the explanation pipeline, emitting phase updates suitable for logging or UI progress bars.
Main Entry Point: generate_explanation
The generate_explanation method implements polymorphic dispatch based on input type, routing to specialized private handlers:
| Input Type | Handler Method | Purpose |
|---|---|---|
InferenceResult |
_explain_inference_result (lines 30-73) |
Forward-chaining rule applications |
Proof |
_explain_proof (lines 138-167) |
Deductive reasoner output |
AbductiveExplanation |
_explain_abductive (lines 76-108) |
Hypothetical reasoning from observations |
| Any other object | _explain_generic (lines 119-126) |
Fallback with stringified representation |
Each handler follows a consistent pattern: extract structured data, build ReasoningStep objects, assemble into ReasoningPath, and conditionally invoke natural-language generation.
Inference Result Explanation
For forward-chaining outputs from semantica/reasoning/reasoner.py:
# _explain_inference_result: lines 30-73
def _explain_inference_result(self, result: InferenceResult) -> Explanation:
# Build reasoning path from premises to conclusion
steps = []
for activation in result.rule_activations:
step = ReasoningStep(
step_id=self._generate_id(),
description=f"Applied rule {activation.rule_name}",
rule_applied=activation.rule_name,
input_facts=activation.matched_facts,
output_fact=activation.inferred_fact,
confidence=activation.confidence * result.premise_confidence
)
steps.append(step)
path = ReasoningPath(
path_id=self._generate_id(),
steps=steps,
start_facts=result.input_facts,
final_conclusion=result.conclusion,
total_confidence=self._aggregate_confidence(steps)
)
# Generate natural language if enabled
nl_text = self._nl_generator.summarize_path(path) if self.generate_nl else ""
return Explanation(
explanation_id=self._generate_id(),
explanation_type="inference",
conclusion=result.conclusion,
reasoning_path=path,
justification=None, # Optional: can build later via justify_conclusion
natural_language=nl_text
)
Proof Explanation
For deductive proofs from semantica/reasoning/deductive_reasoner.py:
# _explain_proof: lines 138-167
def _explain_proof(self, proof: Proof) -> Explanation:
steps = []
for proof_step in proof.steps:
step = ReasoningStep(
step_id=str(proof_step.step_number),
description=proof_step.tactic_description,
rule_applied=proof_step.axiom_or_theorem,
input_facts=[proof_step.premise] if proof_step.premise else [],
output_fact=proof_step.consequent,
confidence=1.0 # Proofs have certainty unless fuzzy logic enabled
)
steps.append(step)
# Similar path construction and NL generation...
The translation preserves the tactic_description from the proof assistant, maintaining the mathematical rigor of the original deduction.
Abductive Explanation
For hypothetical reasoning from semantica/reasoning/abductive_reasoner.py:
# _explain_abductive: lines 176-208
def _explain_abductive(self, abduction: AbductiveExplanation) -> Explanation:
# Reverse direction: observations → best hypothesis
steps = []
# Step 1: Observed facts
steps.append(ReasoningStep(
step_id="observation",
description="Observed phenomena requiring explanation",
rule_applied=None,
input_facts=[],
output_fact=Fact("Observations", value=abduction.observations),
confidence=1.0
))
# Steps 2..n: Competing hypotheses with plausibility scores
for hypothesis in abduction.candidate_hypotheses:
steps.append(ReasoningStep(
step_id=f"hypothesis_{hypothesis.id}",
description=f"Abduced explanation: {hypothesis.description}",
rule_applied=hypothesis.abduction_rule,
input_facts=hypothesis.explains_observations,
output_fact=hypothesis.conclusion,
confidence=hypothesis.plausibility
))
# Final step: Selected best explanation
steps.append(ReasoningStep(
step_id="selection",
description=f"Selected best explanation by {abduction.selection_criterion}",
rule_applied=abduction.selection_criterion,
input_facts=[h.conclusion for h in abduction.candidate_hypotheses],
output_fact=abduction.best_hypothesis.conclusion,
confidence=abduction.best_hypothesis.plausibility
))
This handler explicitly marks the abductive leap from observations to hypotheses, a critical transparency requirement for diagnostic and scientific reasoning systems.
Progress Tracking Integration
All handlers report phase transitions to the optional progress tracker:
# Lines 41-78 demonstrate the tracking pattern
self.progress_tracker.start_phase("Identifying reasoning type...")
# ... type detection ...
self.progress_tracker.start_phase("Generating explanation...")
# ... step construction ...
self.progress_tracker.record_success(explanation_id)
This observability layer ensures that long-running explanations on complex knowledge bases remain debuggable and user-responsive.
Auxiliary Utility Methods
The class provides two convenience APIs for common access patterns:
show_reasoning_path
Extracts only the structured path without narrative overhead:
# Lines 129-145
def show_reasoning_path(self, reasoning_artifact) -> ReasoningPath:
explanation = self.generate_explanation(reasoning_artifact)
if explanation.reasoning_path is None:
raise ValueError("No reasoning path available for this artifact type")
return explanation.reasoning_path
justify_conclusion
Builds a full Justification with aggregated evidence analysis:
# Lines 146-167
def justify_conclusion(
self,
conclusion: Fact,
reasoning_path: ReasoningPath,
evidence_sources: Optional[List[Evidence]] = None
) -> Justification:
supporting_evidence = evidence_sources or self._gather_evidence(reasoning_path)
return Justification(
conclusion=conclusion,
reasoning_path=reasoning_path,
supporting_evidence=supporting_evidence,
overall_confidence=self._compute_overall_confidence(reasoning_path, supporting_evidence),
explanation_text=self._nl_generator.justify(reasoning_path, supporting_evidence)
if self.generate_nl else ""
)
This method supports adversarial audit scenarios where external evidence must be cross-referenced against the internal reasoning chain.
Complete Usage Example
from semantica.reasoning import ExplanationGenerator, Reasoner
from semantica.reasoning.deductive_reasoner import DeductiveReasoner
# Forward-chaining inference with full traceability
reasoner = Reasoner()
inference = reasoner.run_rules(rules=security_rules, facts=network_events)
generator = ExplanationGenerator(
generate_nl=True,
detail_level="detailed",
progress_tracker=console_tracker # Logs to stdout/structured logging
)
# Generate complete explanation
explanation = generator.generate_explanation(inference)
print(f"Explanation ID: {explanation.explanation_id}")
print(f"Type: {explanation.explanation_type}")
print(f"\nNatural Language Summary:\n{explanation.natural_language}")
# Iterate through structured reasoning chain
for step in explanation.reasoning_path.steps:
print(f"\n Step {step.step_id}: {step.description}")
print(f" Rule: {step.rule_applied}")
print(f" Confidence: {step.confidence:.3f}")
print(f" Inputs: {[f.predicate for f in step.input_facts]}")
print(f" Output: {step.output_fact.predicate}")
# Create formal justification for compliance reporting
justification = generator.justify_conclusion(
conclusion=inference.conclusion,
reasoning_path=explanation.reasoning_path
)
print(f"\nCompliance Report:\n{justification.explanation_text}")
Architectural Files in the Explanation Pipeline
| File | Role in Traceable Reasoning |
|---|---|
semantica/reasoning/explanation_generator.py |
Core implementation with all dataclasses and generation logic |
semantica/reasoning/reasoner.py |
Produces InferenceResult objects for forward-chaining explanations |
semantica/reasoning/deductive_reasoner.py |
Provides Proof objects with theorem/axiom references |
semantica/reasoning/abductive_reasoner.py |
Generates AbductiveExplanation with plausibility-ranked hypotheses |
semantica/utils/progress_tracker.py |
Observability infrastructure for long-running explanations |
Summary
- Structured dataclasses (
ReasoningStep,ReasoningPath,Explanation) create complete audit trails from raw inference to human-readable output - Polymorphic dispatch via
generate_explanationhandles forward chaining, deductive proofs, and abductive reasoning through specialized private methods - Confidence propagation at every step enables uncertainty-aware explanation and risk assessment
- Optional natural-language generation produces compliance-ready narratives without sacrificing structured traceability
- Progress tracking integration makes explanation generation observable for production deployments
Frequently Asked Questions
What makes Semantica's explanations "traceable"?
Every ReasoningStep explicitly records the rule applied, input facts, output fact, and confidence score. This creates a directed acyclic graph from any conclusion back to its foundational premises, satisfying audit requirements for high-stakes AI decisions.
Can I disable natural language generation for performance?
Yes. Pass generate_nl=False to the ExplanationGenerator constructor. The generator will still produce complete Explanation objects with structured ReasoningPath data, skipping the NL summarization step entirely.
How does the generator handle different reasoning types?
The generate_explanation method inspects the input object's type and dispatches to one of four private handlers: _explain_inference_result for forward chaining, _explain_proof for deductive proofs, _explain_abductive for hypothetical reasoning, or _explain_generic as a fallback. Each handler constructs appropriate ReasoningStep representations for its reasoning paradigm.
Where are the explanation dataclasses defined?
All core dataclasses—ReasoningStep, ReasoningPath, Justification, and Explanation—are defined in semantica/reasoning/explanation_generator.py at lines 44-92 according to the source code structure.
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 →