Sixty seconds in
You open the stream mid-scene. A pixel town at dusk, rain coming in. Click on Tom and the broadcast camera glides over — "CAM 02" — and follows him down the street while a news chyron crawls underneath. Sooner or later one of the eight residents posts something anxious to the town feed. You type a reply. If it clears moderation, that character's next decision carries a flag on the broadcast: acting on your message.
That's the whole thesis. The chat isn't commentary — it's memory the characters act on.
Inspiration
When Stanford's Generative Agents paper (Park et al., UIST 2023) came out, I did what everyone did — watched the Smallville replays. And I kept catching myself wanting to reply. Smallville is a closed replay: 25 agents with memory, reflection, and planning, but nothing you feel about them matters. I wanted the inverse — a live, perturbable society where an outside message becomes a real memory and the causal effect is measurable. Qwen City is that experiment dressed as a 24/7 soap opera, built for the Agent Society track on Qwen.
One function, three jobs
Audience-Coupled Salience Memory. A single importance function scores every event:
$$I(m) = 0.5 \cdot \text{recency} + 3.0 \cdot \text{relevance} + 2.0 \cdot \text{importance}$$
Recency is exponential decay ($0.995^{\text{hours}}$), relevance is embedding cosine similarity, importance is a 1–10 poignancy score — each min-max normalized, matching the released Generative Agents weighting. $I(m)$ ranks the top-8 memories an agent pulls into every decision (retrieval); the day's top-importance memories are the recap, no separate editor model (highlight editing); and the same score nominates the day's single highest-salience beat for an expensive Wan video render — a documented design; the renderer is the next hook-in (render budgeting). One score, three subsystems.
Audience replies enter this same memory economy with a +2 importance bias, then get force-surfaced to the agent's very next decision, so the causal loop is a guarantee, not a ranking accident. Measured result: one moderated reply changed 25% of the society's next actions versus 0% in the deterministic no-audience control. A closed sandbox structurally cannot report that number.
How I built it
Offline-first. Every model call goes through one ModelAdapter seam, so the entire cognitive loop — perceive → store → retrieve → reflect → plan → act — was built against a deterministic mock while the API key was pending. Going live was one env var (MODEL_BACKEND=dashscope); the first real smoke test passed. 117 tests run in under a second, and a docs-consistency test re-derives the ablation numbers (rumor diffusion 4/4 agents with dialogue vs 1/4 without) so the README can't drift from the code.
Qwen routing is tiered by cognitive load — cheap models do the high-frequency noticing, and the expensive model only thinks deep thoughts a few times a day:
| Task | Model |
|---|---|
| Per-tick actions, importance scoring | qwen-flash |
| Dialogue, social posts | qwen-plus |
| Daily plans, reflection synthesis | qwen3-max |
| Memory embeddings (1024-dim) | text-embedding-v4 |
One instrumented 6-hour live window logged ~8,676 calls and ~1,034K tokens — an average of 119 tokens per request. Tiny requests at high frequency is what a salience engine looks like on the wire. The world runs in fast-forward — a sim half-hour every five real minutes — with cached snapshots keeping the stream lit; a buffer-plus-playhead component (built and tested) is the design for batch-priced generation. It runs on a single Alibaba Cloud ECS instance at qwensociety.andresio.com.
Challenges
Bugs only the simulation could show. The mock's 64-dim embeddings hash-collided so badly that a seeded rumor scored 0.0 relevance while the weather scored 1.0; gossip degraded like telephone because attribution nesting plus truncation clipped the fact off the end. Neither was visible by reading the code.
Reflection produced zero insights for an entire iteration. The parser required insights shaped like insight (because of 1, 5) — anchored to end-of-line. Every model emits a trailing period. Un-anchoring one regex took the town from 0 to 9 reflections.
The first audience injection quoted the wrong memory. The reply surfaced in retrieval but ranked #2, and the agent answered someone else's thought. The fix — force-surfacing the pending injection — is what turned the causal loop from probable into guaranteed.
32 overlaps I couldn't see. After "several buildings are in the middle of the road," a collision script computing real sprite footprints against road bands found 32 overlaps my eyes had missed at zoomed-out scale. The last one was only fixable by admitting a road couldn't exist: uptown became a pedestrian plaza.
Deploys looked like outages. The server binds its port only after ~3 minutes of world init, so every deploy reads as a 502 until you learn to poll /health. And an uncapped memory store would have grown the heap unbounded across the judging window — importance-based forgetting now caps it at 6,000 nodes, so the town forgets its most boring moments first.
What I learned
Run the sim, don't just read the code — my worst bugs were invisible statically. Calibrate thresholds against the real model, not the mock (live Qwen scores importance 6–9 where the mock compressed to 5/1). A deterministic offline harness isn't a compromise; it's what let every claim in the README be a measurement. And a deterministic moderation gate can't be prompt-injected — but it also waved through a hand-written abusive reply containing no profanity, a limit I documented instead of pretending the floor is a ceiling.
What's next
Layer a Qwen VL moderation pass over the deterministic gate, grow the cast beyond eight on the pgvector + OSS scale-out path already wired in deploy/alicloud.ts, and open the perturbation API so researchers can run their own information-diffusion experiments on a live society.
Also a future plan I have is to render scenes so people can follow their characters and they will generate a video of themselves in scenes talking with others using Wan model so people can watch it as casual series on real time. I am trying to make a "The Truman show" series
Built With
- ai-agents
- alibaba-cloud
- canvas
- dashscope
- ecs
- embeddings
- generative-agents
- llm
- model-studio
- multi-agent-systems
- nginx
- node.js
- qwen
- qwen3
- simulation
- typescript
Log in or sign up for Devpost to join the conversation.