# How Semantica's Reasoning Layer Works Without LLMs: Symbolic Rule‑Based Inference

> Discover how Semantica achieves deterministic logical inference using symbolic rule-based reasoning and chaining without relying on LLMs. Explore pure symbolic algorithms.

- Repository: [Semantica /semantica](https://github.com/semantica-agi/semantica)
- Tags: deep-dive
- Published: 2026-09-09

---

**Semantica implements deterministic logical inference through forward chaining, backward chaining, and abductive reasoning using pure symbolic algorithms, eliminating any dependency on large language models.**

The `semantica‑agi/semantica` repository provides a fully deterministic reasoning subsystem that operates entirely on structured facts and logical rules rather than neural networks. This article examines the source code to explain how the reasoning layer processes knowledge, prevents infinite loops, and generates explanations without LLM involvement.

## Core Architecture: The Symbolic Engine

### The Reasoner Facade

The entry point for all inference operations is the `Reasoner` class defined in [`semantica/reasoning/reasoner.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/reasoner.py). This façade manages the fact base, rule registry, and inference strategies through three primary modes: **forward chaining**, **backward chaining**, and **action execution**.

Facts are stored as plain strings (e.g., `"Person(John)"`) inside a Python `set`, providing O(1) membership testing. The `add_fact` method normalizes dictionary‑style knowledge graph objects into this canonical string format, ensuring consistency across the inference pipeline.

### Rule and Action Definitions

Logical rules are encoded as immutable dataclasses in the same [`reasoner.py`](https://github.com/semantica-agi/semantica/blob/main/reasoner.py) file. A `Rule` instance contains:

- **Conditions**: List of predicate patterns to match
- **Conclusion**: The derived fact template
- **Priority**: Execution precedence for conflict resolution
- **Confidence**: Certainty factor for the implication
- **Actions**: Side‑effects such as `AssertAction`, `RetractAction`, or external function calls

Rules can be defined programmatically as `Rule` objects or parsed from compact string syntax like `IF Person(?x) AND Mortal(?x) THEN Mortal(?x)`. The `_parse_rule_definition` method converts these strings into structured `Rule` instances, enabling declarative knowledge engineering without Python code.

## Pattern Matching and Unification

Variable binding operates through regular‑expression‑based pattern matching in the `_match_pattern` method. When processing a predicate like `Person(?x)`, the engine extracts variable names and maps them to concrete values from the fact base through a variable binding table.

The `_substitute` method then generates concrete conclusions by replacing variables with their bound values. For example, given the binding `?x = John` and the conclusion template `Mortal(?x)`, the system produces the grounded fact `Mortal(John)`.

## Forward and Backward Chaining

**Forward chaining** proceeds iteratively through the `Reasoner.forward_chain` method. The engine scans each rule’s conditions against the current fact set, generates bindings via pattern matching, and asserts new conclusions until reaching a fixed point.

**Backward chaining** uses `Reasoner.backward_chain` to prove specific goals. The algorithm recursively matches the target against known facts and then against rule conclusions, unifying variables to establish proof trees.

```python
from semantica.reasoning import Reasoner

# Initialise the engine

r = Reasoner()

# Add facts

r.add_fact("Person(John)")
r.add_fact("Mortal(Humans)")

# Add a rule: IF Person(?x) THEN Mortal(?x)

r.add_rule("IF Person(?x) THEN Mortal(?x)")

# Run inference

new_facts = r.infer_facts(["Person(John)"])
print(new_facts)          # → ['Mortal(John)']

```

## Activation Tracking: Preventing Infinite Loops

To guarantee termination and idempotent side‑effects, the reasoning layer implements **activation tracking**. The `_make_activation_key` method creates unique identifiers for each rule‑binding combination, while `_fired_activations` maintains a registry of previously executed activations.

This mechanism ensures that actions fire exactly once per distinct binding, even when the same conclusion is derived through multiple paths. Consequently, the system prevents infinite loops in cyclic rule sets while ensuring that external side‑effects (such as database writes or event emissions) occur only when new information is actually inferred.

## Abductive Reasoning Without Neural Models

The `AbductiveReasoner` class in [`semantica/reasoning/abductive_reasoner.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/abductive_reasoner.py) generates explanatory hypotheses for observations using purely symbolic methods. The engine enumerates rules whose conclusions could explain the observed facts through the `_rule_explains_observation` method.

Each candidate hypothesis receives a score based on configurable `HypothesisRanking` strategies—**simplicity**, **plausibility**, **consistency**, and **coverage**. The system ranks candidates and trims the result set to the configured `max_hypotheses` without invoking any neural model or LLM API.

```python
from semantica.reasoning import AbductiveReasoner, Observation

ar = AbductiveReasoner(
    max_hypotheses=3,
    ranking_strategy="plausibility"
)

# Observation we want to explain

obs = Observation(
    observation_id="obs1",
    description="Patient has fever and cough"
)

hypotheses = ar.generate_hypotheses([obs])
best = ar.rank_hypotheses(hypotheses)[0]

print(best.explanation)   # e.g. "Hypothesis based on rule: IF Infection(?d) THEN Fever(?d)"

```

## Integrating Actions with Knowledge Graphs

Actions bridge the reasoning engine with external systems. The `AssertAction` with `write_back=True` enables bidirectional synchronization between the inference engine and knowledge graphs.

```python
from semantica.reasoning import Reasoner, AssertAction

# Fake graph with simple add_fact method

class SimpleGraph:
    def __init__(self):
        self.entities = []
    def add_fact(self, fact):
        self.entities.append(fact)

graph = SimpleGraph()
r = Reasoner(knowledge_graph=graph)

# Rule that asserts a new fact and writes it back to the graph

r.add_rule({
    "rule_id": "r1",
    "name": "CreateBirthFact",
    "conditions": ["Person(?x)"],
    "conclusion": "Born(?x)",
    "actions": [AssertAction(fact="Born(?x)", write_back=True)]
})

r.add_fact("Person(Alice)")
r.forward_chain()

print(graph.entities)  # → ['Born(Alice)']

```

## Observability and Provenance

The reasoning layer supports deterministic debugging through the optional `ProgressTracker` utility referenced in the implementation. This component records each inference step, variable binding, and rule activation, creating complete provenance trails without AI‑generated narratives. When integrated, the tracker captures the progression from initial facts through intermediate conclusions to final results, enabling full reproducibility of the symbolic inference chain.

## Summary

- **Symbolic foundation**: The reasoning layer stores facts as strings in Python sets and processes them through immutable `Rule` dataclasses in [`semantica/reasoning/reasoner.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/reasoner.py).
- **Deterministic inference**: Forward chaining (`forward_chain`) and backward chaining (`backward_chain`) derive conclusions using regex‑based pattern matching (`_match_pattern`) and variable substitution (`_substitute`).
- **Loop prevention**: Activation tracking via `_make_activation_key` and `_fired_activations` ensures actions execute exactly once per unique binding, preventing infinite loops.
- **Abductive explanations**: The `AbductiveReasoner` generates and ranks hypotheses using configurable strategies without LLM involvement.
- **External integration**: `AssertAction` and related action classes enable side‑effects such as knowledge graph updates and event emissions.
- **Full observability**: Optional progress tracking and provenance logging provide complete visibility into inference steps.

## Frequently Asked Questions

### How does Semantica's reasoning layer differ from LLM‑based reasoning?

Semantica uses deterministic symbolic algorithms—forward chaining, backward chaining, and abductive logic—operating on structured facts and rules defined in [`semantica/reasoning/reasoner.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/reasoner.py). Unlike LLMs, which generate probabilistic text based on training data, this reasoning layer guarantees reproducible conclusions through exact pattern matching and logical unification, with no neural network inference involved.

### Can the reasoning engine handle recursive or cyclic rule sets without infinite loops?

Yes. The implementation uses activation tracking through `_make_activation_key` and the `_fired_activations` registry to ensure each distinct rule‑binding combination executes exactly once. This mechanism prevents infinite recursion even when rules reference each other cyclically, while still allowing valid transitive inferences to complete.

### What strategies does the abductive reasoner use to rank hypotheses?

According to [`semantica/reasoning/abductive_reasoner.py`](https://github.com/semantica-agi/semantica/blob/main/semantica/reasoning/abductive_reasoner.py), the `AbductiveReasoner` supports four ranking strategies: **simplicity** (fewest assumptions), **plausibility** (prior probability), **consistency** (conflict with known facts), and **coverage** (explained observations). These are configured via the `ranking_strategy` parameter and applied during the `rank_hypotheses` method execution.

### Is it possible to extend the reasoning layer with custom actions?

Yes. The `Action` class hierarchy in the reasoning module allows custom implementations beyond the built‑in `AssertAction` and `RetractAction`. Custom actions can perform arbitrary side‑effects such as API calls, database updates, or event emissions, and they integrate with the activation tracking system to ensure deterministic execution.