Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

38 Commits

Repository files navigation

AgentStan

AI-native agent-based modeling framework.

Simulate. Test. Analyze. Narrate.

pip install agentstan

Quick Start

A 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.

Rule language in one minute

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"}]}
]

Packs — save, share, export your work

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 entry

A 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.

Python behaviors (escape hatch)

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.

The STAN Framework

Simulate — Run agent-based models

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()

Test — Batch runs and parameter sweeps

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}")

Analyze — Understand what happened

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}, ...}}

Narrate — AI explains the results

# 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")

CLI

# 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 300

Architecture

agentstan/
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)

DeFi stress testing

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.

License

BSD 3-Clause

About

Agent-Based Modeling Library for LLMs

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages