Core Concepts

LRS-Agents is built on principles from Active Inference and Predictive Processing. This guide explains the key concepts.

Active Inference

What is Active Inference?

Active Inference is a theory from neuroscience that explains how biological agents (like humans) make decisions. The core idea:

Agents act to minimize surprises about their sensory observations.

In LRS-Agents, this means:

  • Precision (γ): Confidence in predictions

  • Prediction Errors: Surprises that update beliefs

  • Free Energy: A measure of surprise to minimize

Why Active Inference for AI?

Traditional agents:

  • Need explicit error handling

  • Require manual fallback strategies

  • Don’t learn from failures

Active Inference agents:

  • Adapt automatically when surprised

  • Balance exploration vs exploitation naturally

  • Track uncertainty explicitly

The Free Energy Principle

Expected Free Energy (G)

LRS agents select actions by minimizing Expected Free Energy:

\[G = \text{Epistemic Value} - \text{Pragmatic Value}\]

Where:

  • Epistemic Value: Information gain (reduces uncertainty)

  • Pragmatic Value: Expected reward (achieves goals)

Low G = Good Policy

Policies with low G are preferred because they:

  1. Reduce uncertainty about the world

  2. Achieve desired outcomes

  3. Balance exploration and exploitation

Example Calculation

Consider two policies:

Policy A: Try familiar tool

  • Epistemic value: 0.2 (low uncertainty)

  • Pragmatic value: 4.0 (high success rate)

  • G = 0.2 - 4.0 = -3.8 ✓ (Good!)

Policy B: Try novel tool

  • Epistemic value: 1.5 (high uncertainty)

  • Pragmatic value: 2.0 (unknown success rate)

  • G = 1.5 - 2.0 = -0.5 (Less good)

When precision is high, agent chooses Policy A (exploit). When precision is low, agent might explore Policy B.

Precision Tracking

What is Precision?

Precision (γ) represents the agent’s confidence in its world model:

  • High precision (γ > 0.7): Agent is confident, exploits knowledge

  • Medium precision (γ ≈ 0.5): Agent balances explore/exploit

  • Low precision (γ < 0.4): Agent is uncertain, explores

How Precision Updates

Precision is modeled as a Beta distribution with parameters α and β:

\[\gamma = \frac{\alpha}{\alpha + \beta}\]

Updates are asymmetric:

  • Success (low prediction error): Slow increase

    \[\alpha \leftarrow \alpha + \eta_{gain} \cdot (1 - \delta)\]
  • Failure (high prediction error): Fast decrease

    \[\beta \leftarrow \beta + \eta_{loss} \cdot \delta\]

where δ is the prediction error.

Example

from lrs.core.precision import PrecisionParameters

precision = PrecisionParameters()
print(f"Initial: {precision.value}")  # 0.5

# Success
precision.update(0.1)
print(f"After success: {precision.value}")  # 0.52 (slight increase)

# Failure
precision.update(0.9)
print(f"After failure: {precision.value}")  # 0.42 (larger decrease)

Hierarchical Precision

Three Levels

LRS agents track precision at three hierarchical levels:

  1. Abstract: High-level goals and strategies

  2. Planning: Action sequences and policies

  3. Execution: Individual tool executions

Why Hierarchy?

Different levels have different timescales:

  • Execution: Changes every step (fast)

  • Planning: Changes when policies fail (medium)

  • Abstract: Changes when strategies fail (slow)

Error Propagation

Errors propagate upward when they exceed a threshold:

from lrs.core.precision import HierarchicalPrecision

hp = HierarchicalPrecision(propagation_threshold=0.7)

# High error at execution
hp.update('execution', prediction_error=0.95)

# Error propagates to planning
# Planning error is attenuated: 0.95 * 0.5 = 0.475
# If still above threshold, propagates to abstract

This prevents:

  • Over-reaction: Not every tool failure requires strategy change

  • Under-reaction: Persistent failures trigger higher-level adaptation

Prediction Errors

What Are They?

Prediction errors measure how surprising an observation is:

\[\delta = |\text{predicted} - \text{observed}|\]

In LRS-Agents, each tool execution returns a prediction error in [0, 1]:

  • 0.0: Perfectly predicted (deterministic operations)

  • 0.5: Medium surprise

  • 1.0: Completely unexpected

How to Set Them

Guidelines for tool implementers:

def get(self, state):
    try:
        result = execute_operation()

        if operation_is_deterministic:
            return ExecutionResult(True, result, None, 0.0)
        elif operation_usually_succeeds:
            return ExecutionResult(True, result, None, 0.1)
        else:
            return ExecutionResult(True, result, None, 0.3)

    except ExpectedError:
        return ExecutionResult(False, None, str(e), 0.6)
    except UnexpectedError:
        return ExecutionResult(False, None, str(e), 0.95)

Why They Matter

Prediction errors drive adaptation:

  • Low errors: Precision increases → Exploit current strategy

  • High errors: Precision decreases → Explore alternatives

Tool Lenses

What is a Lens?

A lens is a bidirectional interface inspired by functional programming:

  • get(): Read from environment (forward)

  • set(): Update belief state (backward)

Why Lenses?

Lenses provide:

  1. Bidirectionality: Both observe and update

  2. Composability: Chain tools with >> operator

  3. Type safety: Input/output schemas

  4. Statistics: Automatic call/failure tracking

Example

from lrs.core.lens import ToolLens, ExecutionResult

class DatabaseTool(ToolLens):
    def get(self, state):
        """Observe: Query database"""
        query = state.get('query')
        result = self.db.execute(query)
        return ExecutionResult(True, result, None, 0.1)

    def set(self, state, observation):
        """Update: Store result in belief state"""
        return {**state, 'db_result': observation}

Composition

Tools compose naturally:

pipeline = FetchTool() >> ParseTool() >> ExtractTool()

result = pipeline.get(state)
# Executes FetchTool, then ParseTool, then ExtractTool
# Short-circuits on first failure

Policy Selection

How Policies are Selected

  1. Generate candidate policies (via LLM or exhaustive search)

  2. Evaluate each policy by calculating G

  3. Select via precision-weighted softmax:

\[P(policy_i) = \frac{e^{-\beta \cdot G_i}}{\sum_j e^{-\beta \cdot G_j}}\]

where β (inverse temperature) depends on precision.

Precision-Weighted Selection

High Precision (γ > 0.7)

  • Low temperature → Deterministic

  • Select policy with lowest G

  • Exploitation: Use what works

# High precision
probs = [0.85, 0.10, 0.05]  # Mostly best policy

Low Precision (γ < 0.4)

  • High temperature → Stochastic

  • Uniform-ish selection

  • Exploration: Try alternatives

# Low precision
probs = [0.40, 0.35, 0.25]  # More uniform

Adaptation

When Does It Happen?

Adaptation is triggered when precision drops below threshold (default 0.4).

What Happens?

  1. Temperature increases: More exploration

  2. Epistemic weight increases: Favor information gain

  3. Alternative tools considered: Explore registry

  4. Strategy shifts: From exploit to explore

Example Scenario

Step 1: Try API (precision = 0.5)
        ✓ Success (error = 0.1)
        precision → 0.52

Step 2: Try API (precision = 0.52)
        ✗ Timeout (error = 0.9)
        precision → 0.42

Step 3: Try API (precision = 0.42)
        ✗ Timeout (error = 0.9)
        precision → 0.32  ← Below threshold!

[ADAPTATION TRIGGERED]

Step 4: Explore alternatives
        Temperature: 0.7 → 1.5
        Consider: cache, database, retry_api

Step 5: Try cache (precision = 0.32)
        ✓ Success (error = 0.05)
        precision → 0.38

Step 6: Try cache (precision = 0.38)
        ✓ Success (error = 0.05)
        precision → 0.44  ← Recovery!

Multi-Agent Concepts

Social Precision

In multi-agent systems, agents track social precision (trust) in other agents:

from lrs.multi_agent.social_precision import SocialPrecisionTracker

tracker = SocialPrecisionTracker("agent_a")
tracker.register_agent("agent_b")

# After observing agent_b's action
tracker.update_social_precision(
    "agent_b",
    predicted_action="fetch",
    observed_action="cache"  # Different!
)
# Social precision decreases

Communication as Information-Seeking

Agents communicate when:

  • Social precision is low (uncertain about others)

  • Environmental precision is high (not a personal problem)

This treats communication as active sensing to reduce social uncertainty.

Putting It All Together

The LRS Agent Loop

1. Generate policy proposals
   ├─ Via LLM (for 10+ tools)
   └─ Via exhaustive search (for <10 tools)

2. Evaluate each policy
   ├─ Calculate epistemic value
   ├─ Calculate pragmatic value
   └─ Compute G = epistemic - pragmatic

3. Select policy
   ├─ Precision-weighted softmax
   └─ High γ → exploit, Low γ → explore

4. Execute policy
   ├─ Run tools in sequence
   ├─ Observe prediction errors
   └─ Short-circuit on failure

5. Update precision
   ├─ Low error → increase precision
   └─ High error → decrease precision

6. Check adaptation
   ├─ If γ < threshold → adapt
   └─ If goal achieved → terminate

7. Repeat from step 1

Key Takeaways

  1. Precision drives behavior

    • High precision → Exploit (use what works)

    • Low precision → Explore (try alternatives)

  2. Prediction errors update precision

    • Low errors → Increase confidence

    • High errors → Decrease confidence

  3. G balances explore/exploit

    • Epistemic value: Information gain

    • Pragmatic value: Expected reward

  4. Adaptation is automatic

    • No manual error handling needed

    • Agent explores when uncertain

  5. Hierarchy prevents over-reaction

    • Errors attenuate as they propagate

    • Different timescales for different levels

Next Steps