Spaces:
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: 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
- 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).
- Launch the live town — agents move between locations, talk, and think in real time; speech and reflections pop as bubbles.
- Click any agent — the inspector opens its mind: personality, current goal, relationship graph (with signed sentiment bars), and its most recent memories.
- 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_USDis 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
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.