Typed Agents
ABMForge's base Agent remains intentionally lightweight and dynamic. This is
useful for teaching, quick experiments, and compact research models. Typed-agent
support is therefore introduced as an additive protocol layer rather than as a
replacement base class.
What ABMForge provides
The abmforge.core.protocols module defines structural typing helpers:
AgentID;AgentLike;SteppableAgent;AdvanceableAgent;StatefulAgent.
These protocols are useful for type hints, helper functions, tests, and library
extensions that want stronger contracts without forcing every user-defined agent
to inherit from a separate TypedAgent class.
Basic example
from dataclasses import dataclass
from abmforge import Agent, Model, StatefulAgent
@dataclass
class ConsumerState:
adopted: bool
threshold: float
class Consumer(Agent):
state: ConsumerState
def step(self) -> None:
if not self.state.adopted and self.rng.random() >= self.state.threshold:
self.state.adopted = True
def adoption_status(agent: StatefulAgent[ConsumerState]) -> bool:
return agent.state.adopted
model = Model(seed=42)
consumer = model.agents.create(
Consumer,
n=1,
state=ConsumerState(adopted=False, threshold=0.5),
)[0]
assert adoption_status(consumer) in {False, True}
Collection typing
AgentCollection.create(...) and AgentCollection.by_type(...) preserve the
concrete agent class in their type signatures. This means typed subclasses can
be created and retrieved without losing their concrete type information in
static type checkers.
consumers = model.agents.create(Consumer, n=10, state=ConsumerState(False, 0.5))
selected = model.agents.by_type(Consumer)
Runtime checks
The protocols are marked with @runtime_checkable, so they can be used with
isinstance(...) for broad structural checks:
from abmforge import SteppableAgent
assert isinstance(consumer, SteppableAgent)
Runtime protocol checks only verify that required attributes or methods are present. They do not replace static type checking.
Design boundary
ABMForge does not yet provide a separate TypedAgent[StateT] base class. That
would be a stronger architectural commitment and may be considered later. The
current protocol layer is deliberately conservative and backward-compatible.