AI-native agent-based modeling framework.
Simulate. Test. Analyze. Narrate.
pip install agentstanA model is pure JSON — environment, agents, and behaviors are all data. No code in the spec means it can be stored, diffed, generated by an LLM, validated, and executed by any engine that implements the spec.
fromagentstanimportSimulationspec= {
"seed": 42, # same spec + same seed = same results, every time"environment": {"type": "grid_2d", "dimensions": {"width": 40, "height": 40, "topology": "torus"}},
"agent_types": {
"rabbit": {
"initial_count": 80,
"initial_state": {"energy": 25, "perception_radius": 5},
"behavior": {
"rules": [
# Graze when hungry
{"when": {"<": ["$energy", 20]},
"do": [{"type": "modify_state", "attribute": "energy", "delta": 2}]},
# Flee the nearest wolf
{"when": {">": [{"count": {"type": "wolf"}}, 0]},
"do": [{"type": "move_away", "from": {"nearest": {"type": "wolf"}}}]},
# Otherwise wander
{"when": {"==": [{"count": {"type": "wolf"}}, 0]},
"do": [{"type": "move", "direction": [{"choice": [-1, 0, 1]}, {"choice": [-1, 0, 1]}]}]},
# Reproduce when well-fed
{"when": {">": ["$energy", 30]}, "prob": 0.08,
"do": [{"type": "reproduce", "energy_cost": 15}]},
# Metabolism and starvation
{"do": [{"type": "modify_state", "attribute": "energy", "delta": -0.8}]},
{"when": {"<=": ["$energy", 0]}, "do": [{"type": "die", "cause": "starvation"}]},
]
}
}
}
}
sim=Simulation(spec)
results=sim.run(200)
print(results["summary"]["final_counts"])See examples/predator_prey_rules.py for the full two-species model, and
agentstan/core/rules.py for the complete expression/action reference.
Each rule is {"when": <condition>, "prob": <chance>, "do": [<actions>]} —
every rule whose condition holds fires, in order. Expressions are single-key
dicts: comparisons (<, >=, == …), arithmetic (+, * …), logic
(and, or, not), randomness (random, choice, uniform), and
neighbor queries (count, nearest_distance). "$energy" reads the agent's
own state; selectors like {"nearest": {"type": "wolf"}} pick a target agent
for move_toward, move_away, and interact.
The kernel is domain-blind: interactions are generic effects
(kill_target, transfer, self_delta, target_delta), reproduction
costs any attribute you choose, and world laws are spec data too — e.g.
death at zero energy is a top-level "global_rules" entry, not engine code:
"global_rules": [
{"when": {"<=": ["$energy", 0]}, "do": [{"type": "die", "cause": "energy_depleted"}]}
]A pack is a single JSON file bundling models (runnable specs) and named scenarios (parameter variations) plus metadata. Packs are 100% data — they can be stored anywhere, shared, versioned, and run by anyone with the library:
fromagentstan.packimportloadpack=load("goblin-economy.pack.json")
print(pack.models, pack.scenarios) # ['base'] ['gold-rush', 'crash']results=pack.run("gold-rush") # resolves overrides, seeds, runspack.validate(deep=True) # full engine validation of every entryA scenario is just dot-path overrides on a model:
"scenarios": {
"gold-rush": {"model": "base", "steps": 500, "seed": 7,
"overrides": {"agent_types.miner.initial_count": 40}}
}The format is versioned (schema_version), so packs you export today keep
loading tomorrow.
For local power users, an agent type may instead define behavior_code — a
Python function as a string (see examples/predator_prey.py). This is not
recommended for anything that stores or transmits specs: it is not
sandbox-safe and not portable. Prefer rules.
fromagentstanimportSimulation, StagedScheduler, DataCollectorsim=Simulation(spec, scheduler=StagedScheduler(["prey", "predator"]))
collector=DataCollector(
model_metrics={"avg_energy": lambdas: sum(a["energy"] forains.agent_manager.get_living_agents()) /max(s.agent_manager.get_total_count(), 1)},
)
sim.add_collector(collector)
results=sim.run(200)
time_series=collector.get_model_data()fromagentstan.experimentimportbatch_run, sweep# Run 50 times to get statistical confidence (seed makes the batch reproducible)results=batch_run(spec, n_runs=50, steps=200, seed=1000)
# Sweep a parameterresults=sweep(spec, param="agent_types.wolf.initial_count", values=range(5, 50, 5), n_runs=10)
forval, runsinresults.items():
avg=sum(r["summary"]["final_counts"].get("wolf", 0) forrinruns) /len(runs)
print(f"wolves={val}: avg final = {avg:.1f}")fromagentstan.analysisimportanalyze_population, analyze_eventspop_report=analyze_population(results)
# {'agent_types': {'rabbit': {'stability': 'oscillating', 'period': 34, ...}}}event_report=analyze_events(results)
# {'deaths': {'by_cause': {'starvation': 42, 'predation': 18}, ...}}# pip install agentstan[ai]fromagentstan.aiimportgenerate, interpret, validate# Generate a model from natural languagespec=generate("simulate wolves hunting rabbits in a forest")
# Run itsim=Simulation(spec)
results=sim.run(200)
# AI explains what happenedexplanation=interpret(results)
# "Rabbits peaked at step 34 then crashed due to overgrazing..."# Validate the model matches the descriptionissues=validate(spec, "wolves should hunt rabbits")# Run from spec file
agentstan --from-spec ecosystem.json --steps 200
# Batch run
agentstan --from-spec ecosystem.json --batch 50 --analyze
# Generate from natural language (requires agentstan[ai])
agentstan "simulate ants foraging for food" --steps 300agentstan/
core/ # Simulation engine, agents, environments, schedulers
experiment/ # Batch runs, parameter sweeps
analysis/ # Population dynamics, event analysis
ai/ # LLM-powered generation, interpretation, validation
defi/ # DeFi lending stress-testing (see below)
agentstan.defi is a self-serve simulation lab for DeFi lending protocols —
stress-test your protocol before it breaks. Describe a market in plain
language, run crash / depeg / whale-panic scenarios against it, sweep parameters
to find the safe frontier, and export a governance-ready report.
fromagentstan.defiimportauthor_config, LendingMarketcfg=author_config("USDC market, 75% max LTV, stress a 40% ETH crash with a slow oracle")
print(LendingMarket(cfg).run(120)["summary"]["bad_debt"])Deterministic protocol mechanics (interest, oracle, liquidation, slippage) +
constrained LLM behavioral agents, validated against real events (MakerDAO Black
Thursday, the Aave CRV squeeze). See agentstan/defi/README.md.
BSD 3-Clause