Agentropolis is a real-time, multi-multi-agent urban simulation grounded in Toronto geography.
The system runs thousands of AI-driven followers, streams tick updates over WebSockets, and renders live behavior on a 3D Mapbox scene.
Most city simulations are either purely statistical or purely visual.
Agentropolis combines both:
- Real map topology (Toronto neighborhoods + work districts).
- LLM-driven intent
- User-generated situations (arbitrary events)
- Python 3.12+
- FastAPI
- SQLAlchemy async +
asyncpg - PostgreSQL (Neon)
- Alembic migrations
- Railtracks agent orchestration
Core modules:
backend/src/api/*: REST endpoints for sessions, ticks, events, followers, posts, archetypes, zones.backend/src/simulation/tick_orchestrator.py: per-tick orchestration and broadcast flow.backend/src/agents/*: archetype decision agent, event designer, tweet generation, fallback logic.backend/src/db/*: ORM models, query helpers, async engine lifecycle.backend/src/ws/*: WebSocket endpoint + connection manager heartbeat.
- Next.js 15 + React 19 + TypeScript
- Zustand state store
- Mapbox GL JS 3D rendering
Core modules:
frontend/src/store/simulationStore.ts: session lifecycle, tick loop, WS event handling.frontend/src/api/client.ts: typed REST and WS client wrappers.frontend/src/world/toronto-mapbox.ts: terrain/buildings/follower layer + flyover behavior.frontend/src/components/*: HUD, toolkit, welcome, map container.frontend/src/avatar/*: deterministic avatar resolution + creator schema.
- One LLM call per large demographic
- Input includes:
- recent memories
- follower aggregate stats
- relationship summaries
- active events/effects
- neighborhood/work location context
- Output: structured action list (
work,commute,eat,sleep,shop,exercise,socialize,post,attend_event,visit_family).
- Rule-based updates for position and happiness to keep runtime stable and deterministic.
- Event effects can override normal movement (industry stay-home, global stay-home, gathering-zone pull).
- Optional tweet generation for sampled followers (rate scaled by event multipliers).
Event injection endpoint:
POST /api/sessions/{session_id}/events
Flow:
- User submits narrative prompt (for example, "Transit strike across downtown").
- Event-designer agent maps narrative -> structured mechanical levers.
- Effects are stored with optional
duration_ticksand computedend_time. - Tick pipeline applies active effects each hour until expiration.
Session lifecycle:
POST /api/sessionsGET /api/sessions/{session_id}DELETE /api/sessions/{session_id}POST /api/sessions/{session_id}/resumePOST /api/sessions/{session_id}/pause
Simulation + data:
POST /api/sessions/{session_id}/tickPOST /api/sessions/{session_id}/eventsGET /api/sessions/{session_id}/followersPOST /api/sessions/{session_id}/followers(custom avatar join)GET /api/sessions/{session_id}/postsGET /api/sessions/{session_id}/archetypesGET /api/zonesGET /api/zones/residentialGET /api/zones/work-districts
OpenAPI docs:
/docs/redoc
Endpoint:
ws://<host>/ws/{session_id}
Client -> server:
{"type":"subscribe"}{"type":"pong"}(heartbeat ack)
Server -> client:
subscribedpingtick_starttick_archetype_decisiontick_archetype_updatetick_completeerror
Behavior note: when the last client disconnects, the session is auto-paused.
- Python 3.12+
uv(recommended)- Node.js 20+
- PostgreSQL (Neon URL expected)
- OpenAI API key
- Mapbox token
Create .env at repository root:
GOOGLE_CLOUD_PROJECT=your-project-id
OPENAI_API_KEY=sk-...
NEON_DB=postgresql://user:password@host/dbname?sslmode=require
YOUR_NEON_API_KEY=napi_... # optionalcd backend
uv pip install -e .
uv run alembic upgrade head
uv run uvicorn src.main:app --reload --host 0.0.0.0 --port 8000Create frontend/.env.local:
NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN=your_mapbox_token
NEXT_PUBLIC_API_URL=http://localhost:8000cd frontend
npm install
npm run devThen open http://localhost:3000.
Backend tests:
cd backend
uv run pytest