Skip to content

Concepts

Architecture

Architecture

Agents and users in Academy interact via handles to invoke actions asynchronously. An agent's behavior is defined by its actions, control loops, and state. Academy decouples the control and data planes through the exchange, used for user and agent communication, and launcher mechanisms that can remotely execute agents.

An Academy application includes one or more agents and zero or more users. An agent is a process defined by a local state, a set of actions, and a set of control loops. Agents can be executed remotely using a manager (previously referred to as a launcher). Once running, an agent concurrently executes all of its control loops and listens for messages from user programs or other agents.

A user program interacts with an agent through a handle, which acts like a reference to the remote agent and translates method calls into action request messages. Each entity (i.e., user or agent) has an associated mailbox that maintains a queue of messages sent to that entity by other entities. Mailboxes are maintained by an exchange such that any client with access to a given exchange can send messages to the mailbox of another agent in the exchange and receive a response through its own mailbox.

Agents

In Academy, the concept of an "agent" is intentionally simple. The agent primitive is, at its core, is an entity that:

  • Has state: Maintains information about its current situation, past history, or internal variables.
  • Performs actions: Execute specific operations or tasks.
  • Communicates: Exchanges messages or data with other users, agents, or the environment.

In essence, Academy agents can be thought of as building blocks for more complex or specialized agent-based systems.

An agent is implemented as a Python class that inherits from the base Agent type. This class-based approach is extensible through inheritance and polymorphism.

  • State is stored as instance attributes on the agent class instance. Instance attributes maintain the agent's state, and methods define the actions and control loops.
  • Actions can be performed in two ways: @action decorated methods allow other entities to invoke the method remotely and @loop decorated methods run non-terminating control loops that enable an agent to autonomously perform actions.
  • Communication between entities in managed via Handles which are client interfaces to remote agents used to invoke actions, ping, and shutdown.

Execution

The Runtime manager takes an Agent and executes the agent by: (1) listening for new messages in the agent's mailbox and dispatching them appropriately, (2) starting each @loop method, (3) calling the agent_on_startup() callback, and (4) waiting for the agent to be shut down. Each @action method is executed concurrently in the event loop when requested remotely so as to not block the handling of other messages.

Agents are designed to be long-running, but can be terminated by sending a shutdown request. Upon shutdown, the shutdown Event, passed to each @loop, is set; running tasks are cancelled and waited on; and the agent_on_shutdown() callback is invoked. Agents can terminate themselves by setting the shutdown event or calling Agent.agent_shutdown(); exceptions raised in @loop methods will shutdown the agent by default, and exceptions raised when executing @action methods are caught and returned to the remote caller.

Handles

Interacting with an agent is asynchronous; an entity sends a message to the agent's mailbox and waits to receive a response message in its own mailbox. A Handle is a client interface to a remote agent used to invoke actions, ping, and shutdown the agent. Handles translate method calls into a request messages sent via the exchange and returning a Futures. The handle also listens for response messages and accordingly sets the result on the appropriate Futures.

Exchanges and Mailboxes

Entities communicate by sending and receiving messages to and from mailboxes. Mailboxes are managed by an exchange, and the ExchangeClient protocol defines methods for interacting with the exchange and creating handles to other agents. An ExchangeFactory is used to register a new entity with the exchange and create a client that the entity can use for communicating with the exchange. Registering an entity involves creating a unique ID for the entity, which is also the address of its mailbox, and initializing that mailbox within the exchange.

A mailbox has two states: active and terminated. Active indicates that the entity's mailbox is accepting messages, even if, for example, an agent has not yet started or is temporarily offline. Terminated indicates permanent termination of the entity and will cause MailboxTerminatedError to be raised by subsequent send or receive operations to that mailbox.

Academy provides many exchange implementations for different scenarios, such as:

  • Local: Uses in-memory queues for single-process, multiple-agent scenarios. Useful for testing and development.
  • HTTP: Centralized service that maintains mailboxes and exposes a REST API. Lower performance but easy to extend with common authentication tools.
  • Redis: Stores state and mailboxes in a Redis server. Use of Redis enables optional replication and cloud-hosting for improved resilience and availability.
  • HybridExchange: Entities host their mailbox locally and message each other directly over TCP when possible. Redis is used to map mailbox IDs to address and port pairs, and to store messages for offline entities or when two entities cannot directly communicate (such as when behind NATs).

Manager

Agents can be run manually via Runtime.run(), but typically applications want to run many agents concurrently across parallel or distributed resources. The Manager provides a single interface for launching and managing agents across one or more Executors, such as a ProcessPoolExecutor, Parsl, or Globus Compute. A manager will handle common boilerplate, including registering agents, creating handles, and ensuring stateful resources are appropriately cleaned up.