Agents and Container¶
Container¶
In mango, every agent lives inside a container. The container owns the network layer: it sends and receives messages, routes them to the correct agent, and handles serialisation/deserialisation with the chosen codec. When two agents share the same container their messages stay in-process (no network round-trip), which speeds up local communication significantly.
Container types¶
mango ships three container types, each suited to a different deployment scenario:
Type |
Factory |
When to use |
|---|---|---|
TCP |
Default choice. Fast, point-to-point TCP sockets for local and distributed simulations. |
|
MQTT |
When a message broker is already present (e.g. IoT deployments) or when you need topic-based pub/sub routing. |
|
External Coupling |
Co-simulation scenarios where an external tool (e.g. a power-flow solver) drives the time loop and injects messages. |
All factory methods are synchronous — you can create containers before
starting the asyncio event loop. The default codec is JSON (see
Codecs for details). You can supply a custom ExternalClock
to decouple simulation time from wall time (see Scheduling and Clock).
import asyncio
from mango import create_tcp_container
container = create_tcp_container(addr=('127.0.0.1', 5555))
print(container.addr)
('127.0.0.1', 5555)
Starting and stopping¶
Container creation is separate from container starting. Before a container
can exchange messages its network server must be started. Use the
activate() context manager — it starts all containers, runs your
code, and shuts everything down on exit (even on exceptions):
import asyncio
from mango import create_tcp_container, activate
async def start_container():
container = create_tcp_container(addr=('127.0.0.1', 5555))
async with activate(container) as c:
print("Container is running!")
await asyncio.sleep(0)
print("Container shut down automatically.")
asyncio.run(start_container())
Container is running!
Container shut down automatically.
Note
Shutdown also cancels all running agent tasks and calls
on_stop() on every registered agent.
Agents¶
Agents are created by subclassing Agent. Every subclass
must call super().__init__() in its constructor.
An agent is registered with a container via register().
Registration assigns the agent its agent ID (aid) and enables
scheduling. You can suggest a preferred AID with suggested_aid; if the
name conflicts with an existing agent or with the default agentN pattern
the framework generates one automatically.
from mango import Agent, create_tcp_container
import asyncio
class MyAgent(Agent):
pass
async def create_and_register():
container = create_tcp_container(addr=('127.0.0.1', 5555))
agent = container.register(MyAgent(), suggested_aid="my_agent")
return agent
print(asyncio.run(create_and_register()).aid)
my_agent
Lifecycle callbacks¶
Implement these methods to hook into the agent’s lifecycle:
Method |
When it is called |
|---|---|
Immediately after the agent is registered. The scheduler and agent address are available; no messages can be sent yet. |
|
When the container is started (inside |
|
After all containers passed to |
|
When the container shuts down or the agent is deregistered. Use it for cleanup and final messages. |
For handling incoming messages override handle_message().
See Message exchange for the full messaging API.
Express setup¶
The run_with_tcp() (and run_with_mqtt()) helpers
wrap container creation, agent registration, activation, and shutdown into a
single context manager. Agents are distributed evenly across the requested
number of containers.
Pass plain agent instances or (agent, {"aid": "preferred_id"}) tuples:
import asyncio
from mango import PrintingAgent, run_with_tcp
async def run_with_tcp_example():
agent_tuple = (PrintingAgent(), dict(aid="MyAgent"))
single_agent = PrintingAgent()
async with run_with_tcp(2, agent_tuple, single_agent) as cl:
await agent_tuple[0].send_message("Hello, print me!", single_agent.addr)
await asyncio.sleep(0.1)
asyncio.run(run_with_tcp_example())
Received: Hello, print me! with {'sender_id': 'MyAgent', 'sender_addr': ['127.0.0.1', 5555], 'receiver_id': 'agent0', 'network_protocol': 'tcp', 'priority': 0}
See also
run_with_simulation() for the equivalent helper for the
simulation world.
Agent processes¶
Python’s GIL limits true parallelism within a single process. For CPU-intensive tasks mango lets you run individual agents in a dedicated subprocess, coordinated automatically through a mirror container.
# Register an agent in a new subprocess
process_handle = await main_container.as_agent_process(
agent_creator=lambda sub_container: sub_container.register(
MyAgent(), suggested_aid="process_agent"
)
)
# Wait until the subprocess is ready
await process_handle
print(f"Agent running in PID {process_handle.pid}")
The agent in the subprocess communicates with other agents exactly like any other mango agent — through the normal messaging API.
Note
Once an agent is running in a subprocess you cannot access it directly
from the main process. Use
dispatch_to_agent_process() to
schedule a function inside the subprocess:
await main_container.dispatch_to_agent_process(
process_handle.pid,
my_function, # called as my_function(sub_container, *args)
*args,
)
If you need to set up process agents before an asyncio loop is available,
use as_agent_process_lazy() (no
process handle is returned; the subprocess is created when
activate() is called).
See also
Scheduling and Clock — clock types and the scheduling API