Engineering Notes

Engineering Notes

Thoughts and Ideas on AI by Muthukrishnan

Active Inference and the Free Energy Principle How Agents Minimize Surprise Instead of Maximizing Reward

26 Feb 2026

The Free Energy Principle, a theory from neuroscience that is gaining traction in AI agent research, proposes that agents do not maximize reward at all. Instead, they minimize surprise. It offers a unified explanation of perception, learning, and action under a single mathematical objective.

Concept Introduction

An agent’s senses deliver data that may or may not match its internal model of the world. When there is a mismatch, the agent has two options:

  1. Update its beliefs to better explain what it is sensing (perception).
  2. Move into a position that matches its predictions (action).

Both options reduce the gap between the agent’s internal model and the actual sensory data. This gap is called surprise (formally, surprisal). The Free Energy Principle says: a well-adapted agent always acts to minimize surprise. Perception and action are not separate processes. They both serve to keep the agent’s model aligned with reality.

Surprisal is the negative log probability of an observation: $-\log p(o)$. A well-designed agent should make this low over time, consistently finding itself in states it predicted.

But $-\log p(o)$ is intractable for complex models, because it requires integrating over all possible hidden world-states $s$. So instead, we minimize an upper bound on surprisal called variational free energy:

$$F = \mathbb{E}_{q(s)}\left[\log q(s) - \log p(o, s)\right]$$

Here, $q(s)$ is the agent’s approximate posterior belief about world-states, and $p(o, s)$ is its generative model (its internal theory of how states cause observations). Minimizing $F$ does two things simultaneously:

Crucially, the agent can also minimize $F$ through action, by selecting actions $a$ that change observations $o$ so they become consistent with prior predictions. This is called active inference.

Historical & Theoretical Context

The intellectual roots run deep. In the 1860s, Hermann von Helmholtz proposed that perception is “unconscious inference”: the brain is a prediction machine that infers hidden causes from sensory data. This idea was formalized in the Bayesian brain hypothesis in the 1980s and 90s, and later in predictive coding (Rao & Ballard, 1999), which proposed that the brain transmits only prediction errors, not raw sensory signals.

Karl Friston at University College London unified these ideas into the Free Energy Principle around 2005–2010. His claim is provocative: every living system that persists over time (bacteria, brains, robots) must implicitly minimize free energy. It’s not a design choice; it’s a thermodynamic necessity for self-organization.

From an AI perspective, this offers a principled alternative to reinforcement learning. Instead of a hand-crafted reward function, agent “preferences” are encoded as prior beliefs about which states the agent expects to occupy. Reward becomes a special case of low-surprisal states.

Algorithms & Math

Perception: Belief Updating

Given new observation $o$, the agent updates its beliefs $q(s)$ by minimizing $F$ with respect to $q$:

$$q^*(s) = \arg\min_q F = p(s | o)$$

In practice this is done with gradient descent or variational message passing:

$$\Delta q(s) \propto -\frac{\partial F}{\partial q(s)}$$

Action: Acting to Confirm Predictions

Action is selected to minimize the expected free energy (EFE) of future trajectories $\pi$:

$$G(\pi) = \mathbb{E}_{q(o_\tau, s_\tau | \pi)}\left[\log q(s_\tau | \pi) - \log p(o_\tau, s_\tau)\right]$$

This can be decomposed into two terms:

$$G(\pi) = \underbrace{-\mathbb{E}[\log p(o_\tau)]}_{\text{goal-directed (pragmatic)}} - \underbrace{\mathbb{E}[\log p(o_\tau | s_\tau) / q(s_\tau)]}_{\text{epistemic (information gain)}}$$

The first term drives the agent toward preferred (low-surprisal) outcomes. The second term drives exploration, rewarding actions that resolve uncertainty about hidden states. Curiosity and goal-seeking are baked into the same objective.

Pseudocode

# Active Inference Loop
initialize generative_model P(o, s)  # prior over states and observations
initialize belief q(s) = prior P(s)

for each timestep t:
    observe o_t

    # Perception: update beliefs
    q(s) = minimize_F(q, o_t, generative_model)

    # Planning: evaluate policies by expected free energy
    for each policy π in policy_space:
        G(π) = compute_EFE(q, π, generative_model)

    # Action: sample from softmax over -G(π)
    π* = sample(softmax(-G))
    execute(π*[0])

Design Patterns & Architectures

Active inference maps cleanly onto a generative model architecture:

graph TD
    A[Sensory Observations] --> B[Perception Module]
    B --> C[Belief State q_s]
    C --> D[Generative Model P_o_s]
    D --> E[Expected Free Energy G_pi]
    E --> F[Policy Selection]
    F --> G[Action Execution]
    G --> A
    H[Prior Preferences P_o] --> D
  

The generative model is the heart of the system. It encodes:

This architecture connects to familiar patterns:

Practical Application

The pymdp library provides a clean Python implementation for discrete state-space active inference:

import pymdp
from pymdp import utils
from pymdp.agent import Agent
import numpy as np

# Define a 2-state, 2-observation environment
num_states = [2]     # hidden states: [left, right]
num_obs = [2]        # observations: [see_left, see_right]
num_actions = [2]    # actions: [go_left, go_right]

# Likelihood: P(obs | state) — identity mapping for simplicity
A = utils.obj_array_zeros([[2, 2]])
A[0] = np.array([[0.9, 0.1],   # P(see_left | left, right)
                 [0.1, 0.9]])  # P(see_right | left, right)

# Transition: P(next_state | state, action)
B = utils.obj_array_zeros([[2, 2, 2]])
B[0][:, :, 0] = np.eye(2)        # action=go_left: stay put (simplified)
B[0][:, :, 1] = np.eye(2)[::-1]  # action=go_right: swap states

# Prior preferences: agent prefers to be on the right
C = utils.obj_array_zeros([[2]])
C[0] = np.array([0.0, 2.0])  # log preference for see_right

# Prior beliefs about initial state
D = utils.obj_array_uniform([[2]])

agent = Agent(A=A, B=B, C=C, D=D)

# Active inference loop
observation = [0]  # start: see_left
for step in range(5):
    qs = agent.infer_states(observation)       # update beliefs
    q_pi, G = agent.infer_policies()           # evaluate policies via EFE
    action = agent.sample_action()             # act
    print(f"Step {step}: obs={observation}, belief={qs[0].round(2)}, action={int(action[0])}")
    # In a real env: observation = env.step(action)
    observation = [int(action[0])]  # toy transition

This pattern scales to LLM-based agents too. The generative model becomes an LLM’s world model, beliefs are tracked in a structured context, and EFE guides which tool to call next.

Latest Developments & Research

Deep active inference (Çatal et al., 2020; Millidge et al., 2021) replaces the generative model with neural networks, enabling continuous high-dimensional state spaces. The key challenge is computing EFE efficiently with amortized inference networks.

Relationship to LLMs: Friston and colleagues (2023) proposed that LLMs can be interpreted as approximate inference engines, with next-token predictions as a form of free energy minimization over linguistic states. This opens the door to plugging active inference planning on top of language models.

RxInfer.jl (2023, TU Eindhoven) provides fast variational message passing that makes real-time active inference tractable for robotics. pymdp (Heins et al., 2022) is the go-to Python library and has seen significant adoption in cognitive science.

Open problems: scaling to long-horizon planning, handling non-stationary environments, and connecting EFE to standard RL benchmarks.

Cross-Disciplinary Insight

The Free Energy Principle is deeply rooted in thermodynamics and information theory. Free energy in physics (Helmholtz free energy) measures the work extractable from a system. Friston’s variational free energy is the information-theoretic analogue: the “work” available from a predictive model. Minimizing it is equivalent to maximizing model evidence (marginal likelihood), which is what Bayesian model fitting does.

This connects to self-organization in complex systems: a living system maintains its structure by resisting entropy, staying in a bounded set of states. The Free Energy Principle formalizes this as inference. Your body is constantly “guessing” what state it should be in (homeostasis) and acting to confirm those guesses.

For distributed AI: a multi-agent system where each agent minimizes local free energy could exhibit emergent coordination, similar to how ant colonies self-organize without a central planner.

Daily Challenge

Exercise: Build a Minimal Active Inference Agent

Using pymdp or pure NumPy, build an agent in a 4-cell grid:

  1. Define the A (likelihood), B (transition), C (preference) matrices manually.
  2. Run the active inference loop for 10 steps from cell_0.
  3. Plot the belief state over time. Does the agent correctly localize itself and navigate to the preferred cell?
  4. Bonus: Add a second state dimension (e.g., “holding object” yes/no) and see how the joint posterior updates.

Goal: witness how a single objective (minimize free energy) simultaneously drives belief updating and goal-directed navigation.

References & Further Reading

Foundational Papers

Recent Research

Tools & Code

Blog Posts