A practical introduction to system architecture and logic
Summoner is a framework for building agent based systems in adversarial and asynchronous environments. It stands out by starting from constraints rather than convenience.
This guide introduces the core layers of Summoner not as features but as architectural responses. Each layer emerges from the limitations imposed by the previous one. If you are a developer, researcher, or technical lead, this guide provides a clear mental model for how Summoner works and why.
Summoner servers are intentionally untrusted. Imagine a vast castle of interconnected rooms where agents roam freely, encountering others without any guarantee of trust, privacy, or message delivery.
Within this castle, messages travel from room to room without validation, sequencing, or access control. Servers can observe, reorder, or drop traffic entirely.
This constraint is fundamental. It means:
- All correctness must be enforced at the edges (client side)
- All coordination and verification must be explicit
- Security and behavior must be defined by agents, not enforced by servers
You will find these ideas expanded in Getting Started with Summoner Servers
If servers cannot be trusted, clients must handle the responsibilities your application depends on: validate structure, decide whether to verify identity, optionally decrypt payloads, and react. Summoner clients are programmable, reactive runtimes. They define how to respond to incoming messages and when to send new messages.
Agents are enhanced clients that organize message handling via handshake logic, relying on client route definitions (e.g. ready --> action) to build reactive surfaces. These components are not passive listeners but stateful, self-contained automata.
As a result, each agent becomes a secure boundary, a validator, and a state machine.
More about this model is available in Getting Started with Summoner Clients and Agents
Agents scale through composition. Rather than hardwiring every condition, branch, and outcome, you declare logic in a structured way.
Summoner lets you define agent behavior as flows: graphs of reactions based on triggers, actions, and events.
@agent.receive("ready --> action")
async def handle_action(ctx):
...
if ctx["status"] == "done":
return Move(Trigger.done) # send an event
# returning None results in no event
@agent.receive("action --> finish")
async def handle_finish(ctx):
...
if ctx["status"] == "in_progress":
return Stay(Trigger.in_progress) # send an event
else:
return Move(Trigger.OK) # send an event| Concept | Description | In the example |
|---|---|---|
| Signal | Plain outcome name wrapped into a python object | done, in_progress, OK |
| Trigger | Signal-typed attribute bound to a Trigger instance |
Trigger.OK, Trigger.done |
| Action | Transition intent declared in an event | Move, Stay, Test |
| Event | Action-Trigger emission from a handler | Move(Trigger.OK) |
You can think of each route (@receive(...)) as a node in a finite state machine. Flows define how events move between nodes, providing structure, traceability, and composability.
Learn more in Orchestrating Agent Behavior Using Flows
In Summoner, asynchrony is not just a performance feature but a key part of system semantics.
- Handlers are coroutines: they can
awaitlong-running tasks. - Incoming messages do not block other activity.
- Agents can pause on triggers without stalling the system.
This design is critical in adversarial distributed environments. Agents remain ready to respond or wait without causing global stalls. It allows them to continue functioning when messages are delayed, reordered, or dropped.
If you are used to linear scripts or request response logic, this approach will feel different. But it maps more naturally to real world decentralized coordination.
You can dive deeper in Working with Asynchronous Programming in Summoner
« Previous: Client (Basics) | Next: Getting Started with Servers »

