# How SymbolicAI Implements Design by Contract for LLMs: A Deep Dive into the @contract Decorator

> Explore how SymbolicAI uses the @contract decorator to implement Design by Contract for LLMs. Discover pre-condition validation post-condition verification and self-healing retry logic.

- Repository: [ExtensityAI/symbolicai](https://github.com/extensityai/symbolicai)
- Tags: deep-dive
- Published: 2026-03-01

---

**SymbolicAI implements Design by Contract for LLMs through the `@contract` decorator in [`symai/strategy.py`](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py), which wraps large language model calls in a rigorous lifecycle of pre-condition validation, post-condition verification, and self-healing retry logic with exponential back-off.**

SymbolicAI is a neuro-symbolic programming framework that bridges traditional software engineering and large language models. By applying **Design by Contract for LLMs**, the framework treats every LLM interaction as a formal contract with explicit pre-conditions, post-conditions, and automatic recovery mechanisms. This approach ensures that probabilistic model outputs meet deterministic software requirements without sacrificing the flexibility of natural language processing.

## The Contract Lifecycle for LLM Operations

When a class inherits from `Expression` (defined in [`symai/components.py`](https://github.com/extensityai/symbolicai/blob/main/symai/components.py)) and applies the `@contract` decorator to its `forward` method, SymbolicAI enforces a strict six-stage lifecycle for every LLM invocation.

### Pre-condition Validation

Before the LLM call executes, the decorator checks for an optional `pre()` method on the expression class. This method validates inputs, reshapes data, or verifies that required context is present. If `pre()` raises a `ContractViolation`, the contract lifecycle immediately enters the remedy phase.

The wrapper in [`symai/strategy.py`](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py) calls `expr.pre()` (if present) and catches violations before proceeding to the engine call.

### LLM Invocation via Forward

The actual LLM request occurs through the `forward` method. The decorator forwards arguments to the engine retrieved from [`symai/backend/engine_repository.py`](https://github.com/extensityai/symbolicai/blob/main/symai/backend/engine_repository.py), which returns a `(result, metadata)` tuple. This stage is transparent to the contract logic, allowing any backend engine to be used without modification.

Engine selection and invocation happen in [`symai/backend/engine_repository.py`](https://github.com/extensityai/symbolicai/blob/main/symai/backend/engine_repository.py), while the wrapper simply orchestrates the call.

### Post-condition Verification

After receiving the LLM response, the decorator executes an optional `post()` method if defined. This method receives the result and can verify its type, shape, or content against expected constraints. Like `pre()`, a `ContractViolation` raised here triggers the remedy and retry mechanism.

The `@contract` wrapper executes `post()` after receiving the engine response but before returning to the caller.

### Remedy and Automatic Retry

If any contract stage fails, SymbolicAI implements a self-healing retry loop. The decorator retries the operation up to **8 times** using exponential back-off calculated as `base_delay * 2 ** attempt`. Users may define a `remedy()` method that executes between retries to attempt automatic fixes, such as adjusting prompts, sanitizing outputs, or applying fallback logic.

The retry logic lives entirely within the `@contract` implementation in [`symai/strategy.py`](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py).

### Metadata Enrichment

Throughout the lifecycle, the decorator enriches the `metadata` dictionary with contract-specific telemetry, including timestamps, attempt counts, and error traces. This enables observability and debugging without requiring manual instrumentation of every LLM call.

## Implementation Details of the @contract Decorator

The core logic resides in **[`symai/strategy.py`](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py)**, where the `@contract` decorator is implemented as a higher-order function wrapping `Expression` subclasses.

### ContractContext and State Tracking

The decorator instantiates a **`ContractContext`** data class to track the current attempt number and store exception details across retries. This context object ensures state isolation between concurrent contract executions, preventing race conditions when multiple LLM calls run simultaneously.

### Exponential Back-off Strategy

Retry delays follow the formula `base_delay * 2 ** attempt`, with a configurable maximum delay to prevent excessive wait times. This strategy balances quick recovery from transient LLM failures with protection against overwhelming the backend service during outages.

### Exception Hierarchy

All contract violations inherit from **`ContractError`**, a base exception class that distinguishes between contract failures (which trigger retries) and other runtime errors (which fail fast). This hierarchy allows upstream components to implement fine-grained error handling strategies.

## Practical Example: Validating LLM Arithmetic

The following example demonstrates a complete contract implementation for an LLM-powered addition operation:

```python

# example.py

from symai import Symbol, Expression
from symai.strategy import contract

class AddNumbers(Expression):
    """Add two numbers using an LLM prompt."""

    def __init__(self, a: Symbol, b: Symbol):
        self.a = a
        self.b = b

    @contract
    def forward(self):
        # Prompt the LLM to compute a + b

        prompt = f"Add {self.a.value} and {self.b.value} and return the sum as an integer."
        return self.engine.forward(prompt)

    # Optional pre‑condition: ensure inputs are ints

    def pre(self):
        if not isinstance(self.a.value, int) or not isinstance(self.b.value, int):
            raise ValueError("Inputs must be integers")

    # Optional post‑condition: result must be an int and correct

    def post(self, result):
        if not isinstance(result, int):
            raise TypeError("LLM did not return an integer")
        # simple sanity check

        if result != self.a.value + self.b.value:
            raise AssertionError("Incorrect sum returned")

    # Optional remedy: fall back to Python arithmetic on first failure

    def remedy(self, attempt):
        if attempt == 0:
            # Bypass the LLM entirely

            return self.a.value + self.b.value

```

Usage requires only instantiating the class and calling it:

```python

# usage.py

from symai import Symbol
from example import AddNumbers

x = Symbol(7)
y = Symbol(5)

adder = AddNumbers(x, y)
result, meta = adder()
print("Result:", result)   # → 12

print("Metadata:", meta)   # contains attempt count, timestamps, etc.

```

The `@contract` decorator handles all validation, retry logic, and metadata collection without additional boilerplate.

## Key Source Files

| File | Role |
|------|------|
| [`symai/strategy.py`](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py) | Implements the `@contract` decorator, retry logic, and `ContractContext`. |
| [`symai/components.py`](https://github.com/extensityai/symbolicai/blob/main/symai/components.py) | Defines `Expression`, the base class for contract-enabled operations. |
| [`symai/backend/engine_repository.py`](https://github.com/extensityai/symbolicai/blob/main/symai/backend/engine_repository.py) | Manages LLM engine selection and invocation for contract calls. |
| [`symai/models/base.py`](https://github.com/extensityai/symbolicai/blob/main/symai/models/base.py) | Provides `LLMDataModel` for structured output validation in contracts. |
| `symai/ops/` | Contains primitive operators that leverage contract safety for LLM calls. |

## Summary

- **SymbolicAI implements Design by Contract for LLMs** through the `@contract` decorator in [`symai/strategy.py`](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py), wrapping LLM calls in a safety lifecycle.
- **The contract lifecycle** includes pre-condition validation, LLM invocation, post-condition verification, and automatic remedy/retry with exponential back-off.
- **Self-healing retries** occur up to 8 times using the formula `base_delay * 2 ** attempt`, with optional custom `remedy()` logic for automatic recovery.
- **Rich metadata** is automatically collected during execution, enabling observability without manual instrumentation.
- **Minimal boilerplate** is required: developers simply inherit from `Expression` and optionally implement `pre()`, `post()`, or `remedy()` methods.

## Frequently Asked Questions

### What is Design by Contract in the context of LLMs?

Design by Contract (DbC) in SymbolicAI applies traditional software engineering contracts—pre-conditions, post-conditions, and invariants—to large language model interactions. It treats LLM calls as formal operations where inputs must satisfy specific constraints before execution, and outputs must meet validation criteria before being returned to the application. This bridges the gap between deterministic software requirements and probabilistic model outputs.

### How does the @contract decorator handle failures?

When a `pre()` or `post()` method raises a `ContractViolation`, or when the LLM call itself fails, the `@contract` decorator initiates a retry loop. It attempts the operation up to 8 times with exponential back-off delays calculated as `base_delay * 2 ** attempt`. If a `remedy()` method is defined, it executes between retries to attempt automatic fixes, such as adjusting prompts or sanitizing malformed outputs. Only after exhausting all retries does the decorator raise a `ContractError`.

### Can I customize the retry behavior in SymbolicAI?

Yes, developers can customize retry behavior through multiple mechanisms. The exponential back-off base delay and maximum retry count (default 8) are configurable parameters in the contract implementation. More importantly, you can override the `remedy()` method in your `Expression` subclass to implement domain-specific recovery logic—such as falling back to Python calculations, reformulating prompts, or switching to alternative LLM engines—before the next retry attempt occurs.

### Where is the contract logic implemented in the source code?

The core contract logic resides in **[`symai/strategy.py`](https://github.com/extensityai/symbolicai/blob/main/symai/strategy.py)**, which defines the `@contract` decorator, `ContractContext`, and the retry orchestration. The base class `Expression` that enables contract functionality is defined in **[`symai/components.py`](https://github.com/extensityai/symbolicai/blob/main/symai/components.py)**. Engine selection for LLM calls happens in **[`symai/backend/engine_repository.py`](https://github.com/extensityai/symbolicai/blob/main/symai/backend/engine_repository.py)**, while structured output validation support is provided by `LLMDataModel` in **[`symai/models/base.py`](https://github.com/extensityai/symbolicai/blob/main/symai/models/base.py)**. Primitive operators that leverage contracts are located in the **`symai/ops/`** directory.