--- title: Polis β€” A Living Society of AI Agents emoji: πŸ›οΈ colorFrom: indigo colorTo: purple sdk: docker app_port: 7860 pinned: true license: mit --- # πŸ›οΈ Polis β€” A Living Society of AI Agents > 20+ generative agents that perceive, remember, reflect, form relationships, and build an emergent society β€” visualized in a scroll-driven 3D world you can reach into and change. **Live demo:** __ **Code:** __ Polis is a from-scratch implementation of the **Generative Agents** architecture (Park et al., 2023, *"Interactive Simulacra of Human Behavior"*) wrapped in a cinematic Three.js interface. Each agent runs its own memory-retrieval and reflection loop on top of the OpenAI API. Nothing about the story is scripted β€” relationships, rumors, and routines *emerge* from thousands of small LLM decisions. Drop an event into the town ("a stranger arrives with gold") and watch the society metabolize it. --- ## Why this project exists Most portfolio projects are a thin wrapper around a single API call. Polis is a **system**: retrieval, memory scoring, reflection, an agent scheduler, a budget guard, an offline-deterministic fallback, and a real-time 3D renderer β€” all wired together and deployed. It's meant to show the things frontier-lab and product teams actually screen for: - **Agentic architecture** β€” a perceive β†’ retrieve β†’ plan β†’ act β†’ reflect loop, not a chatbot. - **Retrieval that isn't naive RAG** β€” memories ranked by `recency Γ— importance Γ— relevance`. - **Systems discipline** β€” hard cost ceiling, caching, graceful degradation with zero key. - **Product & craft** β€” a 3D scrollytelling front-end that a non-technical person enjoys. - **Ship-it** β€” containerized, one-command deploy to Hugging Face Spaces. --- ## What you can do in the demo 1. **Scroll** the landing page β€” the camera flies through the agent cognitive loop rendered in 3D (perception radius, orbiting memory stream, the 5-stage pipeline, the reflection pulse). 2. **Launch the live town** β€” agents move between locations, talk, and think in real time; speech and reflections pop as bubbles. 3. **Click any agent** β€” the inspector opens its mind: personality, current goal, relationship graph (with signed sentiment bars), and its most recent memories. 4. **Inject a world event** β€” type "a storm floods the harbor" and watch it propagate into every agent's memory and change what they do next. Runs immediately in **mock mode** (deterministic, $0). Add an `OPENAI_API_KEY` secret to switch to the **live LLM engine**, bounded by a hard budget. --- ## How it works ### The memory stream Every observation, dialogue line, plan, and reflection an agent has is stored as a timestamped `Memory` with an **importance** score (the model rates each memory 1–10) and an embedding vector. When an agent needs to act, it doesn't stuff its whole history into the prompt β€” it **retrieves** the top-k memories by the composite score from the paper: ``` score(m) = Ξ±_recency Β· decay^(now βˆ’ last_access) + Ξ±_importance Β· (importance / 10) + Ξ±_relevance Β· cosine(query_embedding, m.embedding) ``` ### The reflection loop Once accumulated importance crosses a threshold, the agent **reflects**: it synthesizes its recent memories into a higher-level, first-person insight ("I value the people who show up for me"), which is written *back* into the memory stream and biases future retrieval. This feedback loop is what makes behavior compound into something that reads like a personality. ### The tick loop (`world.py`) ``` for each tick: broadcast any injected world events β†’ shared observations find agents standing close enough to talk for each agent: perceive location + neighbors retrieve relevant memories if a partner is near: generate a line of dialogue, update the bond else: choose an action + a destination, move toward it record what happened maybe reflect emit a compact event list β†’ the 3D front-end replays it ``` ### Safety & cost rails (`llm.py`) - **Budget guard** β€” a thread-safe ledger prices every call; once `POLIS_BUDGET_USD` is hit, the engine *refuses* to spend more (returns HTTP 402) instead of draining your account. - **Mock backend** β€” with no key, agents think via a deterministic, hash-seeded fallback and embeddings are computed locally, so the Space boots and demos at $0. - **Embedding cache** β€” identical strings are embedded once. --- ## Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ Browser ────────────────────────────┐ β”‚ Three.js (r128) scene Β· scroll-driven cinematic camera β”‚ β”‚ live playback engine Β· agent inspector Β· event injector β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ /api/demo, /api/step, /api/event β”‚ (JSON) β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ FastAPI (backend/main.py) β”‚ β”‚ World ── tick loop, locations, interactions (world.py) β”‚ β”‚ └─ Agent ── memory stream, retrieval, reflection (agents.py)β”‚ β”‚ └─ LLM ── OpenAI + budget guard + mock (llm.py) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` | Layer | Tech | |------------|------| | Front-end | Three.js (WebGL), vanilla JS, CSS scroll-driven storytelling | | Back-end | FastAPI + Uvicorn | | Intelligence | OpenAI `gpt-4o-mini` (chat) + `text-embedding-3-small` (retrieval) | | Deploy | Docker β†’ Hugging Face Spaces | --- ## Run it locally ```bash pip install -r requirements.txt # mock mode ($0, deterministic) β€” just run: python -m backend.main # β†’ open http://localhost:7860 # live mode (real agents): export OPENAI_API_KEY=sk-... export POLIS_BUDGET_USD=1.00 # hard ceiling python -m scripts.generate_demo --ticks 60 # optional: pre-record a richer demo python -m backend.main ``` ## API | Method | Route | Purpose | |--------|----------------|---------| | GET | `/api/health` | liveness + whether a real key is wired + budget | | GET | `/api/state` | current world snapshot | | POST | `/api/step` | advance N ticks, returns snapshots | | POST | `/api/event` | inject a world event | | GET | `/api/demo` | pre-recorded run (used by the Space at $0) | --- ## Project layout ``` polis/ β”œβ”€β”€ backend/ β”‚ β”œβ”€β”€ llm.py # OpenAI wrapper: budget guard, mock backend, embed cache β”‚ β”œβ”€β”€ agents.py # Memory stream, recencyΓ—importanceΓ—relevance retrieval, reflection β”‚ β”œβ”€β”€ world.py # Locations, tick loop, dialogue, relationships, event injection β”‚ └── main.py # FastAPI app + static serving β”œβ”€β”€ static/ β”‚ β”œβ”€β”€ index.html # 3D scrollytelling landing + live UI β”‚ └── app.js # Three.js scene, scroll camera, live playback, inspector β”œβ”€β”€ scripts/ β”‚ └── generate_demo.py # pre-records data/demo_run.json β”œβ”€β”€ data/demo_run.json # generated β”œβ”€β”€ Dockerfile # Hugging Face Spaces (port 7860) β”œβ”€β”€ requirements.txt └── README.md ``` --- ## Credits & references - J.S. Park et al., *Generative Agents: Interactive Simulacra of Human Behavior* (2023). - Built as a portfolio demonstration of agentic systems, retrieval, and 3D UX. MIT License.