| | --- |
| | title: Soci |
| | emoji: ποΈ |
| | colorFrom: blue |
| | colorTo: purple |
| | sdk: docker |
| | app_port: 7860 |
| | pinned: false |
| | --- |
| | |
| | # Soci β LLM-Powered City Population Simulator |
| |
|
| | Simulates a diverse population of AI people living in a city using an LLM as the reasoning engine. Each agent has a unique persona, memory stream, needs, and relationships. |
| |
|
| | Inspired by [Stanford Generative Agents (Joon Park et al.)](https://arxiv.org/abs/2304.03442), CitySim, AgentSociety, and a16z ai-town. |
| |
|
| | **Live demo:** https://huggingface.co/spaces/RayMelius/soci |
| |
|
| | --- |
| |
|
| | ## Features |
| |
|
| | - AI agents with unique personas, goals, and memories |
| | - Maslow-inspired needs system (hunger, energy, social, purpose, comfort, fun) |
| | - Relationship graph with familiarity, trust, sentiment, and romance |
| | - Agent cognition loop: **OBSERVE β REFLECT β PLAN β ACT β REMEMBER** |
| | - Web UI with animated city map, zoom, pan, and agent inspector |
| | - Road-based movement with L-shaped routing (agents walk along streets) |
| | - Agent animations: walking (profile/back view), sleeping on bed |
| | - Speed controls (1x β 50x) and real-time WebSocket sync across browsers |
| | - **LLM probability slider** β tune AI usage from 0β100% to stay within free-tier quotas |
| | - **Player login** β register an account, get your own agent on the map, chat with NPCs |
| | - Multi-LLM support: Gemini (free tier), Groq (free tier), Anthropic Claude, Ollama (local) |
| | - GitHub-based state persistence (survives server reboots and redeploys) |
| | - Cost-efficient model routing (Haiku for routine, Sonnet for novel situations) |
| | - Daily quota circuit-breaker with warnings at 50 / 70 / 90 / 99% usage |
| |
|
| | --- |
| |
|
| | ## System Architecture |
| |
|
| | ``` |
| | βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| | β Browser (web/index.html β single-file Vue-less UI) β |
| | β β’ Canvas city map β’ Agent inspector β’ Chat panel β |
| | β β’ Speed / LLM-probability sliders β’ Login modal β |
| | ββββββββββ¬βββββββββββββββββββββββββββββββ¬ββββββββββββββββββ |
| | β REST GET /api/* β WebSocket /ws |
| | β POST /api/controls/* β push events |
| | βΌ βΌ |
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| | β FastAPI Server (soci/api/server.py) β |
| | β β’ lifespan: load state β start sim loop β |
| | β β’ routes.py β REST endpoints β |
| | β β’ websocket.py β broadcast tick events β |
| | ββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ |
| | β asyncio.create_task |
| | βΌ |
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| | β Simulation Loop (background task) β |
| | β tick every N sec β sim.tick() β sleep β |
| | β respects: _sim_paused / _sim_speed / llm_call_prob β |
| | ββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ |
| | β |
| | βΌ |
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| | β Simulation.tick() (engine/simulation.py) β |
| | β β |
| | β 1. Entropy / world events β |
| | β 2. Daily plan generation βββΊ LLM (if prob gate β) β |
| | β 3. Agent needs + routine actions (no LLM) β |
| | β 4. LLM action decisions ββββββΊ LLM (if prob gate β) β |
| | β 5. Conversation turns ββββββββΊ LLM (if prob gate β) β |
| | β 6. New conversation starts βββΊ LLM (if prob gate β) β |
| | β 7. Reflections βββββββββββββββΊ LLM (if prob gate β) β |
| | β 8. Romance / relationship updates (no LLM) β |
| | β 9. Clock advance β |
| | ββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ |
| | β await llm.complete_json() |
| | βΌ |
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| | β LLM Client (engine/llm.py) β |
| | β β’ Rate limiter (asyncio.Lock, min interval per RPM) β |
| | β β’ Daily usage counter β warns at 50/70/90/99% β |
| | β β’ Quota circuit-breaker (expires midnight Pacific) β |
| | β β’ Providers: GeminiClient / GroqClient / β |
| | β ClaudeClient / HFInferenceClient / β |
| | β OllamaClient β |
| | ββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ |
| | β HTTP (httpx async) |
| | βΌ |
| | External LLM API (Gemini / Groq / β¦) |
| | ``` |
| |
|
| | --- |
| |
|
| | ## Message Flow β One Simulation Tick |
| |
|
| | ``` |
| | Browser poll (3s) Simulation background loop |
| | β β |
| | β GET /api/city tick_delay (4s Gemini, 0.5s Ollama) |
| | βββββββββββββββββββββββββββββββ€ |
| | β β sim.tick() |
| | β β |
| | β βββββββββββ΄βββββββββββ |
| | β β For each agent: β |
| | β β tick_needs() β |
| | β β check routine ββββΊ execute routine action (no LLM) |
| | β β roll prob gate β |
| | β β _decide_action() ββββΊ LLM call βββΊ AgentAction JSON |
| | β β _execute_action() β |
| | β βββββββββββ¬βββββββββββ |
| | β β |
| | β βββββββββββ΄βββββββββββ |
| | β β Conversations: β |
| | β β continue_conv() ββββΊ LLM call βββΊ dialogue turn |
| | β β new conv start ββββΊ LLM call βββΊ opening line |
| | β βββββββββββ¬βββββββββββ |
| | β β |
| | β βββββββββββ΄βββββββββββ |
| | β β Reflections: β |
| | β β should_reflect()? ββββΊ LLM call βββΊ memory insight |
| | β βββββββββββ¬βββββββββββ |
| | β β |
| | β clock.tick() |
| | β β |
| | β WebSocket push β |
| | ββββ events/state βββββββββββββ |
| | β |
| | [browser updates map, event log, agent inspector] |
| | ``` |
| |
|
| | --- |
| |
|
| | ## Agent Cognition Loop |
| |
|
| | ``` |
| | Every tick, each NPC agent runs: |
| | |
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| | β β |
| | β OBSERVE βββΊ perceive nearby agents, events, β |
| | β location, time of day β |
| | β β β |
| | β βΌ β |
| | β REFLECT βββΊ check memory.should_reflect() β |
| | β LLM synthesises insight from recent β |
| | β memories β stored as reflection β |
| | β β β |
| | β βΌ β |
| | β PLAN ββββΊ if no daily plan: LLM generates β |
| | β ordered list of goals for the day β |
| | β (or routine fills the plan β no LLM) β |
| | β β β |
| | β βΌ β |
| | β ACT βββββΊ routine slot? β execute directly β |
| | β no slot? β LLM picks action β |
| | β action types: move / work / eat / β |
| | β sleep / socialise / leisure / rest β |
| | β β β |
| | β βΌ β |
| | β REMEMBER βΊ add_observation() to memory stream β |
| | β importance 1β10, recency decay, β |
| | β retrieved by relevance score β |
| | β β |
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| | |
| | LLM budget per tick (rate-limited providers): |
| | max_llm_calls_this_tick = 1 (Gemini/Groq/HF) |
| | llm_call_probability = 0.45 (Gemini default β ~10h/day) |
| | ``` |
| |
|
| | --- |
| |
|
| | ## Tech Stack |
| |
|
| | | Layer | Technology | |
| | |-------|-----------| |
| | | Language | Python 3.10+ | |
| | | API server | FastAPI + Uvicorn | |
| | | Real-time | WebSocket (FastAPI) | |
| | | Database | SQLite via aiosqlite | |
| | | LLM providers | Gemini Β· Groq Β· Anthropic Claude Β· HF Inference Β· Ollama | |
| | | Config | YAML (city layout, agent personas) | |
| | | State persistence | GitHub API (simulation-state branch) | |
| | | Container | Docker (HF Spaces / Render) | |
| |
|
| | --- |
| |
|
| | ## Quick Start (Local) |
| |
|
| | ### Prerequisites |
| | - Python 3.10+ |
| | - At least one LLM API key β or [Ollama](https://ollama.ai) installed locally (free, no key needed) |
| |
|
| | ### Install |
| |
|
| | ```bash |
| | git clone https://github.com/Bonum/Soci.git |
| | cd Soci |
| | pip install -r requirements.txt |
| | ``` |
| |
|
| | ### Configure |
| |
|
| | ```bash |
| | # Pick ONE provider (Gemini recommended β free tier is generous): |
| | export GEMINI_API_KEY=AIza... # https://aistudio.google.com/apikey |
| | # or |
| | export GROQ_API_KEY=gsk_... # https://console.groq.com |
| | # or |
| | export ANTHROPIC_API_KEY=sk-ant-... |
| | # or install Ollama and pull a model β no key needed |
| | ``` |
| |
|
| | ### Run |
| |
|
| | ```bash |
| | # Web UI (recommended) |
| | python -m uvicorn soci.api.server:app --host 0.0.0.0 --port 8000 |
| | # Open http://localhost:8000 |
| | |
| | # Terminal only |
| | python main.py --ticks 20 --agents 5 |
| | ``` |
| |
|
| | --- |
| |
|
| | ## Deploying to the Internet |
| |
|
| | ### Option 1 β Hugging Face Spaces (free, recommended) |
| |
|
| | HF Spaces runs the Docker container for free with automatic HTTPS. |
| |
|
| | 1. **Create a Space** at https://huggingface.co/new-space |
| | - SDK: **Docker** |
| | - Visibility: Public |
| |
|
| | 2. **Add the HF remote** and push: |
| | ```bash |
| | git remote add hf https://YOUR_HF_USERNAME:YOUR_HF_TOKEN@huggingface.co/spaces/YOUR_HF_USERNAME/soci |
| | git push hf master:main |
| | ``` |
| | Get a write token at https://huggingface.co/settings/tokens (select *Write* + *Inference Providers* permissions). |
| |
|
| | 3. **Add Space secrets** (Settings β Variables and Secrets): |
| |
|
| | | Secret | Value | |
| | |--------|-------| |
| | | `SOCI_PROVIDER` | `gemini` | |
| | | `GEMINI_API_KEY` | your AI Studio key | |
| | | `GITHUB_TOKEN` | GitHub PAT (repo read/write) | |
| | | `GITHUB_OWNER` | your GitHub username | |
| | | `GITHUB_REPO_NAME` | `Soci` | |
| |
|
| | 4. Your Space rebuilds automatically on every push. Visit |
| | `https://YOUR_HF_USERNAME-soci.hf.space` |
| |
|
| | > **Free-tier tip:** Gemini free tier = 5 RPM, ~1500 requests/day (resets midnight Pacific). |
| | > The default LLM probability is set to **45%** which gives ~10 hours of AI-driven simulation per day. |
| | > Use the π§ slider in the toolbar to adjust at runtime. |
| |
|
| | --- |
| |
|
| | ### Option 2 β Render (free tier) |
| |
|
| | 1. Connect your GitHub repo at https://render.com/new |
| | 2. Choose **Web Service** β Docker |
| | 3. Set **Start Command**: |
| | ``` |
| | python -m uvicorn soci.api.server:app --host 0.0.0.0 --port $PORT |
| | ``` |
| | 4. Set environment variables in the Render dashboard: |
| |
|
| | | Variable | Value | |
| | |----------|-------| |
| | | `SOCI_PROVIDER` | `gemini` or `groq` | |
| | | `GEMINI_API_KEY` | your key | |
| | | `GITHUB_TOKEN` | GitHub PAT | |
| | | `GITHUB_OWNER` | your GitHub username | |
| | | `GITHUB_REPO_NAME` | `Soci` | |
| |
|
| | 5. To prevent state-file commits from triggering redeploys, set **Ignore Command**: |
| | ``` |
| | [ "$(git diff --name-only HEAD~1 HEAD | grep -v '^state/' | wc -l)" = "0" ] |
| | ``` |
| |
|
| | > **Note:** Render free tier spins down after 15 min of inactivity. Simulation state is saved to GitHub on shutdown and restored on the next boot β no data is lost. |
| |
|
| | --- |
| |
|
| | ### Option 3 β Railway |
| |
|
| | 1. Go to https://railway.app β **New Project** β **Deploy from GitHub repo** |
| | 2. Railway auto-detects the Dockerfile |
| | 3. Add environment variables in the Railway dashboard (same as Render above) |
| | 4. Railway assigns a public URL automatically |
| |
|
| | --- |
| |
|
| | ### Option 4 β Local + Ngrok (quick public URL for testing) |
| |
|
| | ```bash |
| | # Start the server |
| | python -m uvicorn soci.api.server:app --host 0.0.0.0 --port 8000 & |
| | |
| | # Expose it publicly (install ngrok first: https://ngrok.com) |
| | ngrok http 8000 |
| | # Copy the https://xxxx.ngrok.io URL and share it |
| | ``` |
| |
|
| | --- |
| |
|
| | ## Environment Variables |
| |
|
| | | Variable | Default | Description | |
| | |----------|---------|-------------| |
| | | `SOCI_PROVIDER` | auto-detect | LLM provider: `gemini` Β· `groq` Β· `claude` Β· `hf` Β· `ollama` | |
| | | `GEMINI_API_KEY` | β | Google AI Studio key (free tier: 5 RPM, ~1500 RPD) | |
| | | `GROQ_API_KEY` | β | Groq API key (free tier: 30 RPM) | |
| | | `ANTHROPIC_API_KEY` | β | Anthropic Claude API key | |
| | | `SOCI_LLM_PROB` | per-provider | LLM call probability 0β1 (`0.45` Gemini Β· `0.7` Groq Β· `1.0` Ollama) | |
| | | `GEMINI_DAILY_LIMIT` | `1500` | Override Gemini daily request quota for warning thresholds | |
| | | `SOCI_AGENTS` | `50` | Starting agent count | |
| | | `SOCI_TICK_DELAY` | `0.5` | Seconds between simulation ticks (overridden to 4.0 for rate-limited providers) | |
| | | `SOCI_DATA_DIR` | `data` | Directory for SQLite DB and snapshots | |
| | | `GITHUB_TOKEN` | β | GitHub PAT for state persistence across deploys | |
| | | `GITHUB_OWNER` | β | GitHub repo owner (e.g. `alice`) | |
| | | `GITHUB_REPO_NAME` | β | GitHub repo name (e.g. `Soci`) | |
| | | `GITHUB_STATE_BRANCH` | `simulation-state` | Branch used for state snapshots (never touches main) | |
| | | `GITHUB_STATE_FILE` | `state/autosave.json` | Path inside repo for state file | |
| | | `PORT` | `8000` | HTTP port (set to `7860` on HF Spaces automatically) | |
| |
|
| | --- |
| |
|
| | ## Web UI Controls |
| |
|
| | | Control | How | |
| | |---------|-----| |
| | | Zoom | Scroll wheel or **οΌ / οΌ** buttons | |
| | | Fit view | **Fit** button | |
| | | Pan | Drag canvas or use sliders | |
| | | Rectangle zoom | Click **β¬**, then drag | |
| | | Inspect agent | Click agent on map or in sidebar list | |
| | | Speed | **π’ 1x 2x 5x 10x 50x** buttons | |
| | | LLM usage | **π§ ** slider (0β100%) β tune AI call frequency | |
| | | Switch LLM | Click the provider badge (e.g. **β¦ Gemini 2.0 Flash**) | |
| | | **Login / play** | Register β your agent appears with a gold ring | |
| | | **Talk to NPC** | Select agent β **Talk to [Name]** button | |
| | | **Move** | Player panel β location dropdown β **Go** | |
| | | **Edit profile** | Player panel β **Edit Profile** | |
| | | **Add plans** | Player panel β **My Plans** | |
| |
|
| | --- |
| |
|
| | ## LLM Provider Comparison |
| |
|
| | | Provider | Free tier | RPM | Daily limit | Best for | |
| | |----------|-----------|-----|-------------|----------| |
| | | **Gemini 2.0 Flash** | β
Yes | 5 | ~1500 req | Cloud demos (default) | |
| | | **Groq Llama 3.1 8B** | β
Yes | 30 | ~14k tokens/min | Fast responses | |
| | | **Ollama** | β
Local | β | β | Local dev, no quota | |
| | | **Anthropic Claude** | β Paid | β | β | Highest quality | |
| | | **HF Inference** | β οΈ PRO only | 5 | varies | Experimenting | |
| |
|
| | --- |
| |
|
| | ## Project Structure |
| |
|
| | ``` |
| | Soci/ |
| | βββ src/soci/ |
| | β βββ world/ City map, simulation clock, world events |
| | β βββ agents/ Agent cognition: persona, memory, needs, relationships |
| | β βββ actions/ Movement, activities, conversation, social actions |
| | β βββ engine/ Simulation loop, scheduler, entropy, LLM clients |
| | β βββ persistence/ SQLite database, save/load snapshots |
| | β βββ api/ FastAPI REST + WebSocket server |
| | βββ config/ |
| | β βββ city.yaml City layout, building positions, zones |
| | β βββ personas.yaml Named character definitions (20 hand-crafted agents) |
| | βββ web/ |
| | β βββ index.html Single-file web UI (no framework) |
| | βββ Dockerfile For HF Spaces / Render / Railway deployment |
| | βββ render.yaml Render deployment config |
| | βββ main.py Terminal runner (no UI) |
| | ``` |
| |
|
| | --- |
| |
|
| | ## License |
| |
|
| | MIT |
| |
|