Anatomy of a Reactive Agentic AI System: The Life of a Request in MOTHER
In my previous post, “Agentic AI, Built on Functional and Reactive Principles,” I outlined the philosophy behind our MOTHER architecture - the why behind choosing functional programming and reactive, event-driven design as our foundation.
Now, let’s explore the what - how those ideas look in a live system. Unlike orchestrators that direct workers, MOTHER lets types and events direct themselves. We’ll follow a realistic example: analyzing student sentiment from course feedback surveys. I’ll stay high-level enough to not prescribe specific storage or messaging technologies, while hopefully giving enough details to be able to envision it with technologies you know.
The MOTHER Vocabulary
MOTHER is the entire system - Model Orchestrated Transparent Hermetic Event Reactors.
Within that system we these components:
- The Brain is the orchestrator. It receives user objectives, uses an LLM to analyze intent and select tools, and monitors completion. Importantly, the Brain has zero hardcoded knowledge of any specific tools - its control flow only knows how to validate messages against types.
- Reactors are specialized, stateless workers (serverless functions) that consume specific event types and produce immutable outputs. Each reactor listens only for the events it understands.
- Lore is the immutable artifact that a reactor produces - the structured data or analysis it discovered. Lore is always persisted to durable storage and referenced by events.
- Lore Envelopes are event wrappers that carry metadata about lore: which reactor created it, when, for which session, and where it’s stored.
- Session Metadata tracks the overall progress and state of each user request in the database on append-only tables. Each session has a unique correlation ID that ties together all events, lore, and state changes throughout the workflow.
The entire system runs on serverless infrastructure - ephemeral functions triggered by message events. There are no long-running processes, and no services waiting for requests. Each component scales independently and costs nothing when idle.
One Message Type to Rule Them All
Here’s what makes MOTHER different from microservices you may be used to: there is exactly one message type flowing through the entire system.
In the F# code, this entire system hinges on a single type definition. The elegance of the architecture begins here:
type MotherMessage = {
CorrelationID: Guid
MessageID: Guid
Timestamp: DateTime
Event: MotherEvent // <-- This is the discriminated union
}
and MotherEvent =
| SessionCreated of MotherSessionRequest
| ExtractSurveyData of SurveyExtractionRequest
| AnalyzeSentiment of SentimentAnalysisRequest
| LoreComplete of LoreEnvelope
// ... additional event types as tools are added
This is the entire routing logic. There are no message queues per service, no service registries, no API gateways routing to specific endpoints. Just one event stream, and each serverless function pattern-matches on the MotherEvent discriminated union to decide if it cares about a message.
The event stream is pub/sub rather than a message queue. Every published event is broadcast to all subscribers simultaneously (multicast). The Brain publishes an event and immediately terminates - it has no idea which reactors (if any) will respond. Each reactor independently decides “is this for me?” through pattern matching.
These architectural choices contribute to the true reactivity of the system. Were any part of the system calling specific services, it would be a proactive architecture.
As such, Brain doesn’t “call” reactors. Again, it doesn’t even know specific reactors exist. The Brain simply:
- Validates that an output from the LLM’s decision making deserializes to a valid
MotherEventvariant - Publishes that message to the event stream
- Updates session metadata in the database with what it just did
- Terminates
Despite the relative rarity of its patterns - reactivity, immutability, type-driven design - MOTHER achieves systemic simplicity. The single-stream architecture keeps the system easy to reason about for a small, agile team.
The User’s Objective
Each session begins with a natural language prompt:
User Request: “Analyze student sentiment for Course 12345 over the last 3 months”
This objective contains implicit structure, but it arrives as unstructured text. The system must parse intent, extract parameters, select appropriate tools, and orchestrate their execution. Let’s explore how MOTHER handles this.
1. Session Creation and Tool Selection
When a session begins, the first event flows into the system: SessionCreated. The Brain - which subscribes to all events - receives this and pattern-matches.
The Brain makes its first decision: which tool should handle this objective?
The Brain delegates this decision to an LLM. It provides the model with the user’s objective, a registry of available tools with their F# type schemas, and instructions to construct a valid execution graph of what reactors will accomplish the request and a valid payload for the initial reactor(s) (in this case, providing a JSON schema to the LLM‘s toolset to enforce a valid structured response - mileage may vary with different vendors).
The model determines what could happen; the type system ensures only valid things do happen.
The LLM analyzes the request and responds with structured JSON that must deserialize to a valid MotherEvent. The Brain validates this payload by attempting to deserialize it into the MotherEvent discriminated union. If deserialization succeeds, the type system has proven the message is valid. The Brain updates the session metadata, then publishes the event.
This is the beauty of type-driven development where the F# type system is the contract, the validator, and the router, all in one.
2. Survey Extraction Reactor
Let’s say for instance this newly published message is of the type ExtractSurveyData. This message goes to every subscriber. The SurveyExtractionReactor receives it and reacts.
This reactor is a hermetic function - it has no knowledge of what came before or what comes after. It receives pure data, transforms it deterministically, and emits lore. Specifically, it:
- Queries the database for survey responses matching the parameters
- Generates structured anonymous survey data
- Stores the data as lore in durable object storage
This output becomes lore - an immutable artifact of what the reactor discovered. Note that in this example the reactor does not use an LLM itself. A reactor can be anything that writes lore: Traditional, precise code; a specific, isolated task for an LLM; or even a multimodal MCP agent.
3. Lore Handoff and Sentiment Analysis
With its work complete, the SurveyExtractionReactor constructs a Lore Envelope and wraps it in the LoreComplete event variant. It publishes this back to the event stream.
The LoreComplete event, carrying the Lore Envelope, is the hand-off mechanism between reactors. By publishing the storage key of the immutable Lore artifact, the first reactor provides the necessary contextual input for the next.
The Brain receives this LoreComplete event. It recognizes the session is not yet complete and consults the LLM again (“Does the body of existing Lore provide everything needed to fulfill the original request?”). Using the storage key from the LoreEnvelope, it retrieves the newly collected lore and provides it to the LLM along with the original objective and the tool registry.
The LLM determines the next step is AnalyzeSentiment. The Brain validates and publishes the next event, referencing the storage key of the survey data.
It should be noted here that the architecture supports parallel executions of reactors. So say if the request was to compare sentiment of multiple courses, the sentiment reactor may have multiple instances at once.
4. Completion and Transparency
The SentimentAnalysisReactor receives the AnalyzeSentiment event and pattern-matches. It is another hermetic function. It retrieves the survey data from storage, calls the LLM for analysis, stores the markdown report as lore, and publishes its own LoreComplete event.
The Brain receives this second LoreComplete event. This time, it recognizes the session is complete - all required lore has been collected. It retrieves the final analysis from storage, synthesizes a final response based on the original request and collected lore, persists that response, and updates the session metadata to mark the session as completed. The Brain function then shuts down.
The entire workflow unfolds across multiple serverless invocations, with no process ever running longer than necessary and no component aware of the others’ existence.
Extensibility through Data and Composition
Let’s emphasize what didn’t happen here: the Brain did not contain control flow logic deciding when to call the Sentiment Reactor.
The discriminated union is the control flow. The LLM determines sequencing based on the objective and available lore. The type system ensures messages are valid. The reactive model ensures loose coupling.
When you add a new reactor:
- Add a new variant to
MotherEventwith the required parameters - Create a new serverless function that pattern-matches on that variant
- Subscribe that function to the event stream
- Add the tool to the registry with its schema
The Brain requires zero changes.
By adhering to functional principles, we achieve extensibility through data and composition. The Brain’s core logic is “closed” for modification because it never explicitly routes to a reactor; it only operates on the generic MotherMessage type. When a new reactor is added, we simply extend the MotherEvent discriminated union - an additive change to the data structure.
The Brain immediately gains the ability to dispatch to this new tool because its job is merely to validate the LLM’s response against any valid variant of the union, and then publish. This allows us to compose new workflows by adding new, hermetic functions without ever modifying the central orchestration logic.
In MOTHER, logic lives in types and data, not code.
Transparency and Trust
Throughout this process, the Brain maintains a progress log that clients can poll for real-time updates. Each phase appends an entry to the log:
[
{
"timestamp": "2025-10-25T14:32:00Z",
"phase": "SessionCreated",
"message": "Session received: Analyze student sentiment for Course 12345"
},
{
"timestamp": "2025-10-25T14:32:05Z",
"phase": "AnalyzingObjective",
"message": "Analyzing objective to determine appropriate tools..."
},
{
"timestamp": "2025-10-25T14:32:10Z",
"phase": "DispatchingTool",
"message": "Dispatching survey-extraction tool..."
},
{
"timestamp": "2025-10-25T14:32:45Z",
"phase": "LoreReceived",
"message": "Survey data extracted, analyzing next steps..."
},
{
"timestamp": "2025-10-25T14:32:50Z",
"phase": "DispatchingTool",
"message": "Dispatching sentiment-analysis tool..."
},
{
"timestamp": "2025-10-25T14:33:15Z",
"phase": "LoreReceived",
"message": "Received sentiment analysis results"
},
{
"timestamp": "2025-10-25T14:33:16Z",
"phase": "Complete",
"message": "Session completed successfully"
}
]
This tracks the state transitions and provides a clear, queryable record of system health - transparency by design.
The Immutable Event Log
Every event and lore artifact is stored immutably:
sessions/{correlationID}/
├── request.json
├── brain-events/
│ ├── 001-tool-selection.json
│ └── 002-tool-selection.json
├── lore/
│ ├── survey-data
│ └── sentiment-analysis
├── response.json
└── progress.json
This ensures:
- Debuggability: Session failures can be replayed exactly
- Auditability: Every AI decision has a paper trail
- Testability: Deterministic inputs produce deterministic outputs
- Evolvability: New reactors can process historical events
The Principles in Practice
This example demonstrates how MOTHER’s architectural principles manifest in production:
- Model-Orchestrated: The Brain uses an LLM to make intelligent routing decisions without hardcoded conditionals. The model determines workflow sequencing based on available lore. The model output must conform to F# types - marrying AI flexibility with type safety.
- Transparent: Every decision, data transformation, and state transition is visible in the event log and database. The system is debuggable by design.
- Hermetic: Each reactor is a pure function - same input always produces the same output, stored as immutable lore. No side effects beyond durable storage and completion events.
- Event Reactors: Components react to events they recognize through pattern matching. There is no orchestrator directing traffic, no service mesh routing requests - just independent functions reacting to data shapes.
- Type-Driven: The discriminated union is the API contract, the validator, and the router. The type system enforces correctness; the reactive model enables loose coupling.
By adhering to functional and reactive principles, we built an AI system that’s as debuggable, testable, and maintainable as any other system on our platform. Rather than the “magic” of AI requiring the sacrifice of engineering rigor, we’re finding it as an opportunity to increase rigor.