polis / README.md
AK
docs+build: HF Spaces Dockerfile, recruiter README, deploy guide, MIT license
89d6753
|
Raw
History Blame Contribute Delete
8.07 kB
---
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.