II · Planning & Control FlowEmerging★

Stateless Reducer Agent

also known as Pure-Function Agent, Event-Sourced Agent, 12-Factor Stateless Agent

Design the agent as a pure function (state, event) → newState; entire execution history is held in an external event log; enables pause / resume / replay / time-travel without bespoke checkpointing.

Context

A team builds an agent. The default is to hold state in process memory (Python objects, in-memory dicts). Pausing, resuming, or replaying the agent requires custom checkpointing logic that is inevitably incomplete.

Problem

In-memory agent state cannot be paused, resumed across processes, or time-travelled. Each capability requires bespoke checkpointing that misses edge cases. Differs from durable-workflow-snapshot (which is a snapshot mechanism) by being a programming-model constraint — the agent is *designed* as a reducer, not made into one after the fact.

Forces

  • Stateless-reducer discipline constrains how agent code is structured.
  • External event log adds infrastructure dependency.
  • Some operations are naturally stateful (caches, connections) and need separate handling.

Example

A coding agent encounters a long-running test step. The runtime pauses by stopping event dispatch. A user requests resumption from a different worker. The new worker reads the agent's event log, replays the reducer from log start to current position, then resumes dispatch. Time-travel: the user later replays the log up to step 12 to debug a problem; the agent reconstructs its state at step 12 exactly.

Diagram

Solution

Therefore:

The agent's core is a pure function: takes (current state, next event) → (new state, side-effect descriptors). Side effects are descriptors, not executions — the runtime dispatches them. All events are appended to a durable log. Pause = stop dispatching. Resume = restart dispatching from current log position. Replay = re-run reducer against earlier log slice. Time-travel = re-run against any log slice. Pair with durable-workflow-snapshot, event-driven-agent, deterministic-control-flow-not-prompt, own-the team's-prompts.

What this pattern forbids. All agent state changes flow through the reducer; no hidden state in process memory; all events are persisted to the durable log.

And the patterns that stand alongside it, or against it —

  • complementsDurable Workflow Snapshot★— Capture workflow execution state as a snapshot in a pluggable storage provider so a paused run can resume across deployments, process restarts, and host crashes.
  • complementsEvent-Driven Agent★★— Trigger the agent on external events (webhooks, message queues, file changes) instead of user requests or schedules.
  • complementsDeterministic Control Flow, Not Prompt★— Branching decisions live in deterministic application code while the LLM is invoked at strategic points to produce structured signals that the code branches on.
  • complementsOwn Your Prompts (12-Factor Agents)★— Every prompt in a production agent is versioned, tested, and owned by the team in the application repo — never inherited as a framework default.
  • complementsAgent Resumption★★— Persist agent execution state so a long-running run survives restarts, deploys, or user disconnects.
  • complementsBlocking Sync Calls in Agent Loop✕— Anti-pattern: run synchronous, blocking I/O inside the agent loop or HTTP handler, capping concurrency at the number of OS threads.
  • complementsSubject-First Agent Architecture (ENA Stateful Core)·— Invert the LLM-centric pipeline: the agent is a stateful subject whose decision logic chooses whether to invoke the LLM at all, treating the model as one tool among many.
  • complementsOrchestrator as Bottleneck✕— Anti-pattern: route all agent runs through a single-process orchestrator that becomes the system-wide concurrency ceiling.
  • complementsHidden State Coupling✕— Anti-pattern: agent workflows read or write undeclared shared state (caches, env vars, process globals) instead of explicit inputs and outputs.
  • complementsExecution-State Ledger★— Maintain an execution state outside the model that records what the run has observed, changed and attempted, and consult it before each step so stale observations are refused and still-valid results are reused.

Neighbourhood

Click any neighbour to follow the language. Scroll to zoom, drag to pan.