Agent Types
2.3 Agent Types
The SDK provides five agent types that span a decision-making spectrum from deterministic rules to probabilistic LLM reasoning. The choice of agent type is a modelling decision: it determines how the agent makes decisions, what infrastructure it requires, and what its computational cost is.
Rule-Based Agent (Agent)
The base Agent class. Decisions are implemented as pure Python methods. Deterministic given the same seed, fastest to execute (microseconds per agent per step), and fully transparent --- every decision is traceable through the code. Used for 90%+ of agents in most models.
LLM Agent (LLMAgent)
Agents that delegate decisions to a large language model. Every LLMAgent subclass must implement rule_decide() as a mandatory fallback --- this ensures the model can still run if the LLM API is unavailable, over budget, or in testing mode.
class LLMAgent(Agent):
__behavior_type__ = BehaviorType.LLM_BASED
def __init__(self, agent_id: str, model: Optional[ABMModel] = None,
llm_config: Optional[LLMConfig] = None,
response_cache: Optional[ResponseCache] = None) -> None
def llm_decide(self, prompt: str, context: Dict[str, Any]) -> Dict[str, Any]
@abstractmethod
def rule_decide(self, context: Dict[str, Any]) -> Dict[str, Any]
@classmethod
def set_backend(cls, backend: LLMBackend) -> None
@classmethod
def clear_backend(cls) -> None
@property
def llm_call_count(self) -> int
@property
def llm_cost_usd(self) -> float
@property
def llm_fallback_count(self) -> int
The LLMConfig dataclass controls LLM behaviour:
| Field | Type | Default | Description |
|---|---|---|---|
model | str | "claude-sonnet-4-6" | LLM model identifier |
max_tokens | int | 200 | Maximum response tokens |
temperature | float | 0.0 | Sampling temperature (0 = deterministic) |
cache_identical_prompts | bool | True | Enable response caching |
cost_budget_per_step | float | 0.01 | Max USD spend per step |
cost_budget_total | float | 1.0 | Max USD spend total |
Three backend implementations are provided: MockLLMBackend (deterministic, no API calls --- for testing), DebugLLMBackend (real API calls with logging --- for development), and LLMBackendPool (load-balanced multi-endpoint --- for production).
Hybrid Agent (HybridAgent)
A subclass of LLMAgent that probabilistically routes decisions between the LLM and the rule-based fallback. The llm_probability attribute (default 1.0) controls the fraction of decisions routed to the LLM, using the agent's deterministic PRNG for the coin flip. This enables cost control: setting llm_probability=0.3 means 70% of decisions use cheap rules and 30% use the expensive LLM.
class HybridAgent(LLMAgent):
__behavior_type__ = BehaviorType.HYBRID
def __init__(self, agent_id: str, model: Optional[ABMModel] = None,
llm_config: Optional[LLMConfig] = None,
llm_probability: float = 1.0) -> None
RL Agent (RLAgent)
Agents that learn optimal policies through reinforcement learning. The RLAgent base class defines three abstract methods that subclasses must implement:
class RLAgent(Agent):
def __init__(self, agent_id: str, model: Optional[Any] = None,
policy: Optional[Any] = None) -> None
@abstractmethod
def observe(self) -> np.ndarray # Current observation vector
@abstractmethod
def compute_reward(self) -> float # Reward for last action
@abstractmethod
def apply_action(self, action: np.ndarray) -> None # Execute action
def decide(self) -> np.ndarray # Policy inference
Two built-in policies are provided: RandomPolicy (uniform random actions --- for baselines) and TabularQPolicy (Q-learning with epsilon-greedy exploration --- for discrete action spaces).
External Agent Proxy (ExternalAgentProxy)
Agents whose decisions come from an external system via HTTP or MCP. The proxy sends an ExternalDecideRequest (containing agent state, inbox, globals, and neighbour list) to the endpoint and receives an ExternalDecideResponse (containing the action and outgoing messages). If the endpoint is unreachable, the optional fallback callable is invoked instead.
class ExternalAgentProxy(Agent):
def __init__(self, agent_id: str, model: Optional[ABMModel] = None,
endpoint: Optional[str] = None, timeout: float = 30.0,
fallback: Optional[Callable] = None) -> None
def set_fallback(self, fallback: Callable[[], Dict[str, Any]]) -> None
def set_mock_server(self, mock_server: MockExternalServer) -> None
def decide(self) -> Dict[str, Any]