Spaces:
Running
Running
| 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:** _<add your Hugging Face Space URL here after deploy>_ | |
| **Code:** _<add your GitHub URL here after push>_ | |
| 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. | |