concept
Reactive Agents
Reactive Agents
An agent architecture where an agent declares which events it cares about and reacts to them as they arrive — rather than being stepped through a sequence by a controller. Events can be a human message, a function call from the agent’s own model, a reasoning trace from a peer agent, or a tool output from somewhere else. Introduced as the organizing concept of mozaik.
The Shift: Compose, Don’t Orchestrate
Most agent frameworks are orchestration-shaped: a controller (supervisor, graph, state machine) decides whose turn it is and routes between steps. Reactive agents flip this — there is no central controller. Each agent is an independent participant in a shared environment; system behavior emerges from participants reacting to each other’s events concurrently.
“a framework where intelligent behavior is something you compose, not something you orchestrate.”
| Orchestrated (langgraph-multi-agent-patterns) | Reactive (this) | |
|---|---|---|
| Who decides next step | Controller / graph edges | Each agent, independently |
| Coupling | Central routing logic | Just the event contract |
| Adding a role | Edit the controller/graph | Write one agent, join() it |
| Execution | Mostly sequential turns | Concurrent reactions |
Typed Event Handlers
The key ergonomic move: replace a single generic dispatch (onContextItem(source, item) + branching) with dedicated, default-no-op handlers, so an agent overrides only what’s relevant and reads as a flat “when X → do Y” list:
onMessage— plain conversational stringsonFunctionCall/onFunctionCallOutputonReasoningonModelMessage
Self vs. external split
Every handler has an external twin fired when a peer (not the agent itself) emits the event: onFunctionCall vs onExternalFunctionCall(source, item), etc. This removes manual if (source === me) checks and separates two distinct intents:
- self handlers → “act on my own outputs” (record context, run the next inference, execute my tool call)
- external handlers → “observe / react to others” (a reviewer, a logger, a handoff trigger)
A pure observer is just a participant that overrides only the onExternal* handlers and never runs inference.
Why It’s Interesting
- Less boilerplate, clearer intent — no branching dispatch; the handler list is the spec of what the agent does.
- Observers and safety reviewers are first-class — they just listen to peers’ streams (ties into streaming-as-runtime).
- Open composition — new behavior arrives by adding participants, not by growing a central switch. This is what makes agent-swarms cheap to extend.
Caveats
Emergent, controller-free systems trade explicit control flow for flexibility: reasoning about what will happen is harder than reading a graph, ordering/termination is less obvious, and debugging means tracing event cascades. For strictly sequential pipelines a graph (langgraph-multi-agent-patterns) is often clearer. Reactive shines when concurrency, live observation, and easy role-addition matter.
Related
- mozaik — the framework implementing this
- agent-swarms — many reactive agents in one environment
- streaming-as-runtime — streaming as the event substrate reactive agents react to
- ai-agent-architectures — where this sits relative to ReAct / multi-agent / graph orchestration
- langgraph-multi-agent-patterns — the orchestrated counterpoint
- pub-sub-vs-point-to-point — the event-bus messaging shape underneath