Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/salesforce/ai-economist/llms.txt

Use this file to discover all available pages before exploring further.

An agent represents a single decision-maker in the simulation. Foundation ships with two built-in agent types that cover the most common research patterns. This page explains their design and shows when and how to create your own agent type.

The two built-in agent types

BasicMobileAgent

Represents an embodied individual actor. Has a loc (2D grid position), inventory, escrow, and endogenous quantities. Can navigate the world map and interact with resources.

BasicPlanner

Represents a social planner. Has no physical location—loc is removed from its state. Its index is always "p" regardless of the idx argument passed at construction. Intended to be unique (one per environment).
Looking at their full source code illustrates how thin agent subclasses are in practice:
from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry

@agent_registry.add
class BasicMobileAgent(BaseAgent):
    """
    A basic mobile agent represents an individual actor in the economic
    simulation.

    "Mobile" refers to agents of this type being able to move around in
    the 2D world.
    """
    name = "BasicMobileAgent"

When to create a custom agent type

The built-in agents cover most use cases. Create a new agent type only when you need structural differences that cannot be expressed through component state fields, such as:
  • A second class of mobile agent with different physical rules (e.g. a firm agent versus a household agent)
  • An agent that should never appear in world.agents (Foundation currently places all non-planner agents there)
  • An agent where loc semantics differ from the 2D grid (e.g. a multi-dimensional position)
If you only need agents to carry extra data, use BaseComponent.get_additional_state_fields() instead of creating a new agent type. Those fields are merged directly into agent.state without any subclassing.

Agent state layout

BaseAgent.__init__ initialises agent.state as:
self.state = dict(loc=[0, 0], inventory={}, escrow={}, endogenous={})
Each field is populated during environment construction:
FieldTypePopulated byDescription
loc[row, col]World constructor2D grid position. Removed by BasicPlanner.
inventory{resource: float}agent.register_inventory(resources)Quantities the agent directly holds.
escrow{resource: float}agent.register_inventory(resources)Reserved quantities (e.g. pending trade orders).
endogenous{name: float}agent.register_endogenous(endogenous)Non-inventory quantities such as Labor.
Extra keys added by components (via get_additional_state_fields) are merged directly into agent.state by agent.register_components().

Agent indexing conventions

Mobile agents are indexed with integers starting from 0. The planner is always indexed as the string "p".
for agent in env.world.agents:
    print(agent.idx)  # 0, 1, 2, ..., n_agents-1

print(env.world.planner.idx)  # "p"
Keys in observation and reward dictionaries use str(agent.idx), so mobile agent keys are "0", "1", etc., and the planner key is "p". The idx passed to the constructor is stored as an int for numeric values and as-is for strings:
# From BaseAgent.__init__
if isinstance(idx, str):
    self._idx = idx
else:
    self._idx = int(idx)

Step-by-step: implementing a custom agent type

1

Subclass BaseAgent and set the name attribute

The name attribute is the registry key. It must be a non-empty string containing no . characters.
from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry

@agent_registry.add
class FirmAgent(BaseAgent):
    """
    A firm agent accumulates capital and employs mobile agents.
    Unlike BasicMobileAgent, it does not move on the grid.
    """
    name = "FirmAgent"
If no further customisation is needed—just a new named type—this is the complete definition, mirroring BasicMobileAgent.
2

Override __init__ to adjust state structure

Modify agent.state in __init__ to add, remove, or rename fields. Always call super().__init__() first.
def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    # Remove location — firms have a fixed address, not a grid position
    del self.state["loc"]
    # Add firm-specific state
    self.state["capital"] = 0.0
    self.state["employees"] = []
Removing loc from state means the agent cannot be placed on the world map. Make sure no component or scenario tries to call world.set_agent_loc() on this agent type.
3

Override the loc property if needed

BaseAgent.loc reads from self.state["loc"]. If you deleted that key, override the property to prevent confusing KeyError messages—following the pattern in BasicPlanner:
@property
def loc(self):
    raise AttributeError("FirmAgent does not occupy a grid location.")
4

Register the agent type

The @agent_registry.add decorator does all the work. After it runs, the agent is retrievable via:
import ai_economist.foundation as foundation
FirmAgentClass = foundation.agents.get("FirmAgent")
The module defining your agent must be imported before foundation.agents.get() is called. Add it to ai_economist/foundation/agents/__init__.py or import it explicitly in your script.
5

Reference the agent type in scenarios and components

Use the name string wherever agent types are listed:
# In a scenario
class MyScenario(BaseEnvironment):
    name = "my-scenario"
    agent_subclasses = ["BasicMobileAgent", "FirmAgent", "BasicPlanner"]
    required_entities = ["Coin"]

# In a component
class FirmProduction(BaseComponent):
    name = "FirmProduction"
    agent_subclasses = ["FirmAgent"]
    required_entities = ["Coin"]

    def get_n_actions(self, agent_cls_name):
        if agent_cls_name == "FirmAgent":
            return 3  # produce low / medium / high
        return None
When agent_subclasses contains more than one name, none of the listed agent classes may be a subclass of another. This is enforced at construction time by both BaseEnvironment and BaseComponent.

Complete minimal example

firm_agent.py
from ai_economist.foundation.base.base_agent import BaseAgent, agent_registry


@agent_registry.add
class FirmAgent(BaseAgent):
    """
    A stationary firm agent that accumulates capital.
    Does not occupy a location on the world grid.
    """

    name = "FirmAgent"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        del self.state["loc"]
        self.state["capital"] = 0.0

    @property
    def loc(self):
        raise AttributeError("FirmAgent does not occupy a grid location.")
Referencing FirmAgent in a scenario and component:
import firm_agent  # registers FirmAgent
import ai_economist.foundation as foundation

# Confirm registration
assert foundation.agents.has("FirmAgent")
assert foundation.agents.get("FirmAgent") is firm_agent.FirmAgent

Key BaseAgent API reference

Once constructed, agent objects expose the following API used by components and scenarios:
Method / PropertyDescription
agent.idxUnique index. Integer for mobile agents, "p" for the planner.
agent.loc[row, col] position. Raises AttributeError if removed.
agent.inventoryDict of {resource_name: float} quantities held.
agent.escrowDict of reserved resource quantities.
agent.endogenousDict of endogenous quantities (e.g. {"Labor": 3.5}).
agent.total_endowment(resource)Sum of inventory[resource] and escrow[resource].
agent.inventory_to_escrow(resource, amount)Move quantity from inventory to escrow.
agent.escrow_to_inventory(resource, amount)Move quantity from escrow to inventory.
agent.get_component_action(component_name)Return the current action index for a component.
agent.reset_actions()Reset all action buffers to NO-OP (0).
agent.action_spacesTotal action count (single-action mode) or array of per-subspace counts (multi-action mode).