How We Build Agent Populations for Social Media Simulations
#atlas#devlog#feature#simulation#building-in-public
David OlssonOne of the harder problems in social media simulation is making the agents feel real. Not just demographically diverse, but behaviorally distinct — agents that have stakes in the outcome, triggers that provoke them, and communication styles that differ in ways that matter to how a conversation unfolds.
Here is how we generate those populations in Atlas.
Where Agents Come From
Atlas simulations are grounded in a knowledge graph built from uploaded documents. Every named entity extracted from that graph — a university, a student activist, a government agency, a journalist — becomes a candidate simulation agent.
The persona generator takes those entity nodes and converts them into structured OASIS agent profiles. The conversion is not a template fill; it is a full LLM generation pass using several hundred tokens of entity context pulled from the graph.
The Pipeline
The context builder pulls from three sources before the prompt is sent: entity attributes from the graph, relationship edges (facts), and a semantic search over the graph store for related nodes. That context is trimmed to 3,000 characters and injected directly into the prompt.
The 12-Field Schema
Each agent gets these fields generated by the LLM:
| Field | Purpose |
|---|---|
bio | ~200-word public profile bio |
persona | ~2,000-word internal behavioral description |
mbti | Personality type, distributed to reflect realistic population proportions |
prior_stance | The agent's pre-existing view on the topic before the event |
personal_stakes | What this agent personally stands to gain or lose |
reaction_triggers | 3-5 content types that provoke a strong response |
communication_style | How the agent writes online |
age, gender, country, profession | Demographics |
interested_topics | Topic affinities used by the recommendation layer |
The persona field is what gets injected into the OASIS agent's LLM system prompt at runtime. It is the longest field — and the most consequential. Getting it to encode behavioral specificity consistently across 500 agents is the main challenge the prompt has to solve.
Individual entities (students, journalists, professors) and group entities (universities, government agencies, media outlets) use separate prompt templates. Institutional accounts get framing that covers communication register, forbidden topics, and how they handle controversy rather than personal memory.
The four behavioral fields — prior_stance, personal_stakes, reaction_triggers, communication_style — are appended to the persona string at runtime via _enriched_persona() before the agent is initialized. This keeps them queryable as structured data while also surfacing them to the LLM that drives each agent.
Power-Law Distributions for Social Structure
Real social networks are not uniformly distributed. A small number of accounts have enormous reach; most have almost none. We model that with Python's random.paretovariate(), applying different shape parameters and range clamps by entity type:
# From oasis_profile_generator.py
if et_lower in ("mediaoutlet", "media"):
follower_count = int(random.paretovariate(1.5) * 50_000)
follower_count = max(10_000, min(follower_count, 2_000_000))
karma = random.randint(10_000, 100_000)
elif et_lower in ("student", "alumni"):
follower_count = int(random.paretovariate(3.0) * 100)
follower_count = max(30, min(follower_count, 2_000))
karma = random.randint(100, 1_500)
else:
# Person, Organization, default
follower_count = int(random.paretovariate(2.5) * 200)
follower_count = max(50, min(follower_count, 10_000))
karma = random.randint(300, 5_000)
A lower Pareto shape parameter (1.5 for media outlets) produces heavier tails — occasionally generating accounts in the millions. A higher parameter (3.0 for students) concentrates values much lower. The same draw populates both platforms: Twitter gets follower_count, Reddit gets karma, mapped to platform-appropriate ranges.
Bootstrapping the Conversation
Generating agent profiles is only part of the setup. The simulation also needs an initial conversation to react to.
The SimulationConfigGenerator uses a separate LLM pass to produce 4-6 diverse seed posts that kick off the simulation. The prompt requires at minimum: one authoritative institutional source, one grassroots personal voice, one expert analytical perspective, and one emotionally charged post. Each seed post gets a poster_type that the system uses to match it to the right agent at initialization time.
Two scheduled turning-point events are also generated — narrative twists injected at approximately simulated hours 24 and 48. These are designed to introduce new evidence, official responses, or viral revelations that shift the conversation's trajectory.
Scaling to 500 Agents
Profile generation runs in parallel using a ThreadPoolExecutor. The default concurrency is 5 simultaneous LLM calls, and profiles are written to disk incrementally as they complete so partial results survive interruption.
Agent configs — activity level, active hours, posting frequency, response delay, influence weight, stance — are generated in batches of 15 via a second LLM pass. The prompt gives the LLM entity type guidance (universities post slowly during work hours at high influence weight; students post fast in the evenings at low influence weight) and asks it to produce per-agent numeric parameters that reflect the entity's expected behavior.
Both passes have fallback paths. If the LLM returns malformed JSON, the system attempts truncation repair, regex extraction, and finally a rule-based generator that produces reasonable defaults by entity type.
What This Buys Us
The combination of graph-grounded personas, power-law social structure, and behavioral fields that differ by agent type produces simulations where agents do not all respond the same way to the same event. A leaked document triggers the journalists immediately, with short response delays and high posting frequency. The university account waits, responds formally during work hours, and avoids emotional framing. The affected student posts first on instinct, then reacts to what the institution says.
Whether that produces useful signal about how real discourse unfolds is still an empirical question — and one we are actively working to answer.