Factor 12: Make Agent a Stateless Reducer
Principle: Design agents as stateless reducers for predictable, reproducible behavior.
How Vel Implements This
Vel implements the reducer pattern for non-streaming execution:
# The reducer is a pure function: (State, Event) -> (State, Effects)
from vel.core import State, reduce
# Initial state
state = State(run_id='run-123')
# Event sequence
events = [
{'kind': 'start'},
{'kind': 'llm_step', 'step': {'tool': 'get_weather', 'args': {'city': 'NYC'}}},
{'kind': 'tool_result', 'result': {'temp_f': 65, 'condition': 'cloudy'}},
{'kind': 'llm_step', 'step': {'done': True, 'answer': 'The weather is cloudy, 65°F'}},
]
# Deterministic state transitions
for event in events:
state, effects = reduce(state, event)
# State is immutable, effects are generated
for effect in effects:
if effect.kind == 'call_llm':
# Execute LLM call
pass
elif effect.kind == 'call_tool':
# Execute tool
pass
elif effect.kind == 'halt':
# Agent completed
break
# Same inputs always produce same outputs
# Easy to test, debug, and reason about
Stateless Execution
# Agent execution is stateless with external state storage
agent = Agent(
id='my-agent',
model={'provider': 'openai', 'model': 'gpt-4o'},
session_storage='database' # State stored externally
)
# Each call is independent
# State loaded from database
# New state saved after completion
# Agent itself is stateless
Benefits
- ✓ Predictable behavior: same input → same output
- ✓ Easy to test with pure functions
- ✓ Reproducible debugging
- ✓ State stored externally (database)
- ✓ Horizontally scalable (agents are stateless)