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_explanation handles 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:

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 →