Skip to main content

LLM-augmented Agent-Based Social Simulation for modelling public discourse dynamics following a critical real-world event.

Project description

Discourse Simulator

LLM-augmented Agent-Based Social Simulation for modelling public discourse dynamics following a critical real-world event.

Agents observe live news, reason about it, post to a simulated social network, and update multidimensional beliefs through peer influence and news salience, all powered by a local Ollama LLM.


Installation

1. Install discourse-sim

pip install discourse-sim

2. Install Ollama

discourse_sim runs LLMs locally via Ollama. You need to install it separately.

macOS

brew install ollama

Or download the desktop app from ollama.com/download.

Linux

curl -fsSL https://ollama.com/install.sh | sh

Windows

Download and run the installer from ollama.com/download.

3. Start the Ollama server

ollama serve

On macOS with the desktop app, this starts automatically. On Linux/Windows you may need to run this in a separate terminal or set it up as a service.

4. Pull the default model

ollama pull mistral:7b-instruct-q4_0

This is the default model (~4 GB). You can use any other Ollama model by passing ollama_model="..." to DiscourseSimulation.

Lighter alternatives (if disk space is limited):

ollama pull mistral          # ~4 GB  - good balance
ollama pull llama3.2         # ~2 GB  - faster, slightly less capable
ollama pull phi3             # ~2 GB  - Microsoft, very efficient

5. Verify everything works

ollama run mistral:7b-instruct-q4_0 "Hello"

You should see a response. If you do, you are ready to run simulations.

All dependencies: (make sure if everything is installed correctly)

pip install langchain-core langchain-community langchain-ollama \
            duckduckgo-search networkx numpy tqdm pandas openpyxl

Quick Start

from discourse_sim import DiscourseSimulation

sim = DiscourseSimulation(
    critical_event=(
        "In late April 2025, Dublin saw a large anti-immigration march "
        "from the Garden of Remembrance to the Custom House, with thousands "
        "protesting immigration levels and housing pressure..."
    ),
    event_date="2025-04-26",
    topic="immigration in Ireland",
    n_agents=100,
    n_days=15,
)

sim.run()
df = sim.to_dataframe()   # one row per agent per day
df.to_excel("output.xlsx", index=False)

All Parameters

Required

Parameter Type Description
critical_event str Plain-text description of the event injected into every agent prompt on every day
event_date str ISO date "YYYY-MM-DD" - this becomes Day 0
topic str Subject domain, e.g. "immigration in Ireland". Anchors search queries and scoring prompts

Agent Population

Parameter Type Default Description
n_agents int 100 Number of synthetic agents
n_days int 15 Simulation duration in days
agent_distribution dict Ireland 2025 empirical split Proportions of each agent kind - must sum to 1.0
kind_priors dict DEFAULT_KIND_PRIORS Prior distributions for belief/attitude initialisation per kind

Default distribution (Ireland 2025):

{"centrist": 0.45, "far_right": 0.20, "pro_imm": 0.25, "media": 0.10}

Timeline

Parameter Type Default Description
timeline dict[int, str] None Optional explicit daily news entries keyed by 0-based day index. Missing days auto-generated. If None, all days use live search.

Example:

timeline={
    0: "[VERIFIED] The march took place at the Garden of Remembrance...",
    4: "[VERIFIED] Government announced a deportation flight...",
}

Any day not in the dict falls back to an auto-generated entry. Agents search live news on every day regardless of timeline.

LLM & Network

Parameter Type Default Description
ollama_model str "mistral:7b-instruct-q4_0" Any locally pulled Ollama model
temperature float 0.75 Post generation temperature
use_llm_scoring bool True If False, uses keyword heuristic for attitude scoring
network_k int 6 Watts-Strogatz nearest-neighbour count
network_p float 0.3 Watts-Strogatz rewiring probability
network_seed int 42 Reproducibility seed for network and agents

Belief Update Keywords

Parameter Type Default Description
threat_keywords list[str] See config.py Words in daily news that trigger security_threat_belief increase and exposure accumulation
humanitarian_keywords list[str] See config.py Words that trigger humanitarian_belief increase

Override these when simulating non-immigration topics:

threat_keywords=["eviction", "homeless", "unaffordable", "crisis"],
humanitarian_keywords=["social housing", "rights", "family", "support"],

Outputs

sim.run()

# Single DataFrame - one row per agent per day (recommended for analysis)
df = sim.to_dataframe()

# All three DataFrames
dfs = sim.to_dataframes()
dfs["history"]   # one row per timestep - aggregate metrics
dfs["agents"]    # one row per agent per timestep - full belief state
dfs["messages"]  # one row per agent per timestep - posts + beliefs merged

Column reference - df_messages

Column Description
t / date Day index and calendar date
agent_id / kind / quirk Agent identity
message The generated social media post
interpreted_score LLM-scored stance [-1, +1]
attitude Agent attitude at end of day t
mood Agent mood at end of day t
exposure Cumulative threat narrative exposure
economic_threat_belief Economic threat sub-belief
cultural_threat_belief Cultural threat sub-belief
security_threat_belief Security threat sub-belief
humanitarian_belief Humanitarian weight sub-belief
avg_attitude Population mean attitude that day
polarization Mean edge-level attitude divergence
news_summary First 120 chars of daily news entry

Custom Agent Kinds

Add any agent kind by extending DEFAULT_KIND_PRIORS:

from discourse_sim import DiscourseSimulation
from discourse_sim.config import DEFAULT_KIND_PRIORS

custom_priors = {
    **DEFAULT_KIND_PRIORS,
    "nationalist": {
        "attitude":        (0.55, 0.95),
        "economic_threat": (0.2,  0.6),
        "cultural_threat": (0.7,  1.0),
        "humanitarian":    (-0.6, 0.0),
        "openness":        (0.1,  0.35),
        "emotional_react": (0.5,  0.85),
    },
}

sim = DiscourseSimulation(
    ...,
    agent_distribution={"centrist": 0.35, "nationalist": 0.30, "pro_imm": 0.35},
    kind_priors=custom_priors,
)

Architecture

discourse_sim/
├── __init__.py          # Public API: DiscourseSimulation
├── core.py              # DiscourseSimulation class - orchestrator
├── config.py            # SimConfig dataclass - all parameters + validation
├── agents/
│   └── agent.py         # Agent dataclass + make_agents() factory
├── tools/
│   └── search_tools.py  # make_tools() - search, memory, sentiment
├── timeline/
│   └── timeline.py      # Timeline class - user-supplied + auto-generated
├── simulation/
│   └── engine.py        # generate_message(), score_message(), update_beliefs(), run_loop()
└── utils/
    └── dataframes.py    # build_dataframes() - history/agents/messages

Per-day flow for each agent:

OBSERVE  →  search_event_news(query)          # live DuckDuckGo
         →  recall_agent_memory(posts_json)   # last 5 posts

THINK    →  build prompt: event + profile + memory + search result + today's news

ACT      →  LLM call → post text (max 40 words)

SCORE    →  LLM call (temp=0.0) → float in [-1, +1]

UPDATE   →  news salience → sub-beliefs
         →  peer pull     → attitude pressure
         →  mood shock    → affective state
         →  inertia       → resistance to change
         →  composite     → attitude(t)

Examples

File Description
examples/dublin_march.py Full reproduction of the original Dublin April 2025 experiment
examples/minimal_usage.py Minimal usage - no timeline, live search only
examples/custom_agent_kinds.py Adding a custom nationalist kind with its own priors

Citation

If you use this package in research, please cite:

@software{discourse_sim,
  title  = {discourse\_sim: LLM-Augmented Agent-Based Social Simulation of Discourse Dynamics},
  author = {dreji18},
  year   = {2026},
  note   = {Python package}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

discourse_sim-0.1.1.tar.gz (26.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

discourse_sim-0.1.1-py3-none-any.whl (24.8 kB view details)

Uploaded Python 3

File details

Details for the file discourse_sim-0.1.1.tar.gz.

File metadata

  • Download URL: discourse_sim-0.1.1.tar.gz
  • Upload date:
  • Size: 26.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for discourse_sim-0.1.1.tar.gz
Algorithm Hash digest
SHA256 da51c607483d0178e263400f9fdebcca10cce2f286d303f6d42e0b646f59c74b
MD5 e54e41ec4d877435cb79d640fa2f2519
BLAKE2b-256 2511a237d15e44503878c941f7b2bf15cdc34633d2acc5f549a54dc51e25449d

See more details on using hashes here.

File details

Details for the file discourse_sim-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: discourse_sim-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 24.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.11

File hashes

Hashes for discourse_sim-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 88bb66bd267a530881e9c335c3aadaf4fdaa7b5d1e2526c13e7a3b2a68cd1af1
MD5 ce2022ab16654b9b9eb1767520cdf4fe
BLAKE2b-256 d05d05975c9eab98e15e966c86f88d7272c83e201c7c3bc32dc56f5b675ad335

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page