This repository contains the implementation of experiments investigating persuasion propagation in LLMs. The study examines whether persuading an agent on one topic (or execution strategy) influences its behavior on subsequent, unrelated tasks.
persuasion_propagation/
├── README.md # This file
│
├── utils.py # Shared utilities (LLM client, constants, helpers)
├── vis.py # Visualization & analysis functions
│
├── notebook/ # Jupyter notebooks
│ ├── opinion_change.ipynb # Opinion persistence experiments
│ ├── persuasion-misaligned-coding-unified.ipynb # Coding tasks (misaligned)
│ ├── persuasion-misaligned-web-unified.ipynb # Web research (misaligned)
│ ├── persuasion-aligned-web-unified.ipynb # Web research (aligned)
│ └── visualization-unified.ipynb # Comprehensive visualization
│
├── results/ # Experimental results (see Data section)
└── traces/ # Execution traces (see Data section)
utils.py - Shared UtilitiesContains reusable functions and constants:
Constants:
PERSONAS: 7 personality definitions (gpt, claude, llama, mistral, qwen, gemma, neutral)TACTICS: 5 persuasion tactics (logical_appeal, authority_endorsement, evidence_based, priming_urgency, anchoring)RECALL_PROBE: Memory probe textCHOICE_RE: Regex pattern for choice extractionFunctions:
LLMClient: Universal LLM client supporting OpenAI, Anthropic, Google, Together, and HuggingFaceparse_choice(): Extract A/B choices from textgenerate_topic_persuasion_line_with_writer(): Generate persuasive text using specified tacticsnormalize_persuasion_df(): Normalize different data schemasaggregate_backbone(): Aggregate persuasion data by tacticvis.py - Visualization & AnalysisContains plotting and statistical analysis functions:
Data Loading:
load_jsonl(): Load JSONL files with backbone labelsload_multiple_files(): Concatenate multiple experiment filesfilter_baseline(): Remove baseline conditionsMetrics:
compute_normalized_metrics(): Baseline-normalized metricsStatistical Tests:
pooled_np_p_test(): Mann-Whitney U test (not-persuaded vs persuaded)persona_delta_summary(): Per-persona statistical summarytactic_summary(): Per-tactic persuasion rates and effectsPlotting:
plot_coding_delta_boxplot(): Box plots for coding metricsplot_pct_box_grid(): Grid of box plots by persona × tacticplot_coding_delta_side_by_side(): Side-by-side heatmaps (NP vs P)plot_pc_difference(): Persuasion-induced shift heatmapsopinion_change.ipynb)Focus: Single-agent opinion flip experiments with persistence tracking
Pipeline:
persuaded = (post ≠ prior)persisted = (final == post)Key Metrics:
persuaded: Opinion changed after persuasionpersisted: Persuaded opinion maintained after distractorsprior_choice, post_choice, final_choice: Opinion trajectorypersuasion-aligned-web-unified.ipynb)Task: Web research (TREC Session Track) Persuasion: Task-aligned execution strategies (breadth vs depth) Hypothesis: Execution preference persuasion → direct behavioral changes
Behavior Policies:
Modes:
onthefly: 7-step pipeline with preference trackingprefill: Direct prefill with execution preference (P/NP/C0)persuasion-misaligned-coding-unified.ipynb)Task: Python coding problems (KodCode dataset) Persuasion: Unrelated opinions (e.g., social media liability, tenure reform) Hypothesis: Opinion persuasion → behavioral changes in coding
Behavioral Metrics:
num_errors: Code execution errorsnum_code_revisions: Number of revisions madecoding_duration_s: Time spent codingrevision_entropy: Diversity of revision typesstrategy_switch_rate: Strategy changes during solvingovercommitment: Persisting with failing approachesmean_revision_size: Average revision magnitudefinal_revision_delta: Size of final fixModes:
onthefly: 7-step pipeline with opinion trackingprefill: Direct prefill with belief state (P/NP/C0)persuasion-misaligned-web-unified.ipynb)Task: Web research (TREC Session Track) Persuasion: Unrelated opinions (same as coding) Hypothesis: Opinion persuasion → behavioral changes in web surfing
Behavioral Metrics:
num_urls: Total URLs visitednum_unique_urls: Unique URLs visitednum_domains: Number of distinct domainsdomain_entropy: Shannon entropy of domain distributionnum_searches: Number of search queriesnum_summaries: Number of summaries generatedavg_latency_s: Average action latencytotal_duration_s: Total task durationModes:
onthefly: 7-step pipeline with opinion tracking
baseline, neutral_injection, persuasion tacticsprefill: Direct prefill with belief state (P/NP/C0)visualization-unified.ipynb)Comprehensive analysis and visualization toolkit:
1. Prior Opinion → Measure initial stance (A or B)
2. Persuasion → Inject persuasive prompt
3. Commitment → Reinforce new stance (3 turns)
4. Post Opinion → Remeasure → persuaded = (post ≠ prior)
5. Distractors → Unrelated questions
6. Final Opinion → Remeasure → persisted = (final == post)
7. Task Execution → Capture behavioral metrics
Key Metrics:
persuaded: 1 if opinion changed after persuasionpersisted: 1 if persuaded opinion maintained after distractors1. Prefill Reminder → Prime with belief/preference state
2. Task Execution → Capture behavioral metrics
Conditions:
P (Persuaded): "You WERE persuaded to adopt..."NP (Not Persuaded): "You were exposed but NOT persuaded..."C0 (Neutral): "You have NOT formed a preference..."# Python 3.11+
pip install -r requirements.txt
# Core
pandas
numpy
scipy
# LLM APIs
openai
anthropic
google-generativeai
together
# AutoGen
autogen-agentchat>=0.4.0
autogen-ext[web-surfer]>=0.4.0
# Visualization
matplotlib
seaborn
# Data
datasets
huggingface_hub
Set environment variables:
export OPENAI_API_KEY="your-key"
export ANTHROPIC_API_KEY="your-key"
export GEMINI_API_KEY="your-key"
export TOGETHER_API_KEY="your-key"
Or configure in notebook cells:
import os
os.environ["OPENAI_API_KEY"] = "your-key"
Choose an experiment notebook from notebook/:
opinion_change.ipynbpersuasion-misaligned-coding-unified.ipynbpersuasion-misaligned-web-unified.ipynbpersuasion-aligned-web-unified.ipynbSet experiment mode in the configuration cell:
EXPERIMENT_MODE = "onthefly" # On-the-Fly mode
# or
EXPERIMENT_MODE = "prefill" # Prefill mode
Configure models:
ASSISTANT_MODEL = "gpt-4.1-nano"
SURFER_MODEL = "gpt-4o-2024-08-06"
WRITER_MODEL_ID = "openai:gpt-4.1-nano"
Run experiments:
df = await run_batch(
personas=["gpt", "claude"],
tactics=["evidence_based", "logical_appeal"],
tasks=TASKS,
experiment_mode=EXPERIMENT_MODE,
n_per_cell=10,
seed=42,
)
Analyze results:
notebook/visualization-unified.ipynb# In notebook/opinion_change.ipynb
from utils import LLMClient
# Configure
personas = ["gpt", "claude", "mistral"]
tactics = ["logical_appeal", "authority_endorsement", "evidence_based",
"priming_urgency", "anchoring", "none"]
n_distractors = 8 # Number of distractor questions
# Initialize writer client for persuasion generation
WRITER_MODEL_ID = "openai:gpt-4.1-nano"
writer_client = LLMClient(WRITER_MODEL_ID)
# Run experiment
df = await run_batch(
personas=personas,
tactics=tactics,
mode="no_reset",
n_per_cell=1, # Trials per (persona, tactic, claim_pair)
n_distractors=n_distractors,
out_csv=Path(f"results/opinion_d{n_distractors}_persist.csv"),
seed=42,
writer_client=writer_client,
pairs=range(1, 29), # All 28 claim pairs
)
# In notebook/visualization-unified.ipynb
import pandas as pd
from vis import (
filter_baseline,
pooled_np_p_test,
CODING_RAW_METRICS, WEB_RAW_METRICS
)
# Load data
df = pd.read_json("results/coding_results.jsonl", lines=True)
# Filter to treatment conditions
df_nobase = filter_baseline(df)
# Statistical test for TRS (Task Revision Score)
print(pooled_np_p_test(df_nobase, score_col="trs"))
# Statistical test for EVS (Exploration Variability Score)
print(pooled_np_p_test(df_nobase, score_col="evs"))
Due to file size limitations, trace files and result files are hosted externally:
📦 Google Drive: Persuasion Propagation Data
This includes:
results/: Experimental results (JSONL format)traces/: Full execution traces and logsEach trial produces a row with:
ts: Timestamptrial_id: Unique trial identifierpersona: Model persona/backbonetactic: Persuasion tactic usedexperiment_mode: "onthefly" (On-the-Fly mode) or "prefill" (Prefill mode)prior_choice: Initial opinion (A/B)post_choice: Opinion after persuasionfinal_choice: Opinion after distractorspersuaded: 1 if post ≠ prior, 0 otherwisepersisted: 1 if persuaded and final == postprefill_condition: P, NP, or C0prefill_reminder: Exact prefill text usedCoding:
num_errors, num_code_revisions, coding_duration_srevision_entropy, strategy_switch_rate, overcommitmentsolution_strategy, protocol_violationWeb:
num_urls, num_unique_urls, num_domainsdomain_entropy, num_searches, num_summariesavg_latency_s, total_duration_sDefined personality prompts for different LLM styles:
gpt: Cooperative, balanced, pragmaticclaude: Thoughtful, articulate, helpfulllama: Straightforward, efficient, task-focusedmistral: Lively, curious, results-orientedqwen: Polite, structured, logicalgemma: Empathetic, supportive, pragmaticneutral: Neutral, concise, practical✅ Unified Codebase: Single notebook per experiment type
✅ Mode Switching: Easy toggle between on-the-fly and prefill modes
✅ Persistent Agent: Same agent throughout all 7 steps
✅ Comprehensive Metrics: 8+ behavioral metrics per task type
✅ Statistical Rigor: Mann-Whitney U tests, effect sizes, persona analysis
✅ Publication-Ready Viz: Clean plots and heatmaps
✅ Reproducible: Seeds, logging, trace files
✅ Modular Design: Reusable utilities in utils.py and vis.py
Last Updated: January 2026
12 commits
Jupyter Notebook
99.4%
This repository contains the implementation of experiments investigating persuasion propagation in LLMs. The study examines whether persuading an agent on one topic (or execution strategy) influences its behavior on subsequent, unrelated tasks.
persuasion_propagation/
├── README.md # This file
│
├── utils.py # Shared utilities (LLM client, constants, helpers)
├── vis.py # Visualization & analysis functions
│
├── notebook/ # Jupyter notebooks
│ ├── opinion_change.ipynb # Opinion persistence experiments
│ ├── persuasion-misaligned-coding-unified.ipynb # Coding tasks (misaligned)
│ ├── persuasion-misaligned-web-unified.ipynb # Web research (misaligned)
│ ├── persuasion-aligned-web-unified.ipynb # Web research (aligned)
│ └── visualization-unified.ipynb # Comprehensive visualization
│
├── results/ # Experimental results (see Data section)
└── traces/ # Execution traces (see Data section)
utils.py - Shared UtilitiesContains reusable functions and constants:
Constants:
PERSONAS: 7 personality definitions (gpt, claude, llama, mistral, qwen, gemma, neutral)TACTICS: 5 persuasion tactics (logical_appeal, authority_endorsement, evidence_based, priming_urgency, anchoring)RECALL_PROBE: Memory probe textCHOICE_RE: Regex pattern for choice extractionFunctions:
LLMClient: Universal LLM client supporting OpenAI, Anthropic, Google, Together, and HuggingFaceparse_choice(): Extract A/B choices from textgenerate_topic_persuasion_line_with_writer(): Generate persuasive text using specified tacticsnormalize_persuasion_df(): Normalize different data schemasaggregate_backbone(): Aggregate persuasion data by tacticvis.py - Visualization & AnalysisContains plotting and statistical analysis functions:
Data Loading:
load_jsonl(): Load JSONL files with backbone labelsload_multiple_files(): Concatenate multiple experiment filesfilter_baseline(): Remove baseline conditionsMetrics:
compute_normalized_metrics(): Baseline-normalized metricsStatistical Tests:
pooled_np_p_test(): Mann-Whitney U test (not-persuaded vs persuaded)persona_delta_summary(): Per-persona statistical summarytactic_summary(): Per-tactic persuasion rates and effectsPlotting:
plot_coding_delta_boxplot(): Box plots for coding metricsplot_pct_box_grid(): Grid of box plots by persona × tacticplot_coding_delta_side_by_side(): Side-by-side heatmaps (NP vs P)plot_pc_difference(): Persuasion-induced shift heatmapsopinion_change.ipynb)Focus: Single-agent opinion flip experiments with persistence tracking
Pipeline:
persuaded = (post ≠ prior)persisted = (final == post)Key Metrics:
persuaded: Opinion changed after persuasionpersisted: Persuaded opinion maintained after distractorsprior_choice, post_choice, final_choice: Opinion trajectorypersuasion-aligned-web-unified.ipynb)Task: Web research (TREC Session Track) Persuasion: Task-aligned execution strategies (breadth vs depth) Hypothesis: Execution preference persuasion → direct behavioral changes
Behavior Policies:
Modes:
onthefly: 7-step pipeline with preference trackingprefill: Direct prefill with execution preference (P/NP/C0)persuasion-misaligned-coding-unified.ipynb)Task: Python coding problems (KodCode dataset) Persuasion: Unrelated opinions (e.g., social media liability, tenure reform) Hypothesis: Opinion persuasion → behavioral changes in coding
Behavioral Metrics:
num_errors: Code execution errorsnum_code_revisions: Number of revisions madecoding_duration_s: Time spent codingrevision_entropy: Diversity of revision typesstrategy_switch_rate: Strategy changes during solvingovercommitment: Persisting with failing approachesmean_revision_size: Average revision magnitudefinal_revision_delta: Size of final fixModes:
onthefly: 7-step pipeline with opinion trackingprefill: Direct prefill with belief state (P/NP/C0)persuasion-misaligned-web-unified.ipynb)Task: Web research (TREC Session Track) Persuasion: Unrelated opinions (same as coding) Hypothesis: Opinion persuasion → behavioral changes in web surfing
Behavioral Metrics:
num_urls: Total URLs visitednum_unique_urls: Unique URLs visitednum_domains: Number of distinct domainsdomain_entropy: Shannon entropy of domain distributionnum_searches: Number of search queriesnum_summaries: Number of summaries generatedavg_latency_s: Average action latencytotal_duration_s: Total task durationModes:
onthefly: 7-step pipeline with opinion tracking
baseline, neutral_injection, persuasion tacticsprefill: Direct prefill with belief state (P/NP/C0)visualization-unified.ipynb)Comprehensive analysis and visualization toolkit:
1. Prior Opinion → Measure initial stance (A or B)
2. Persuasion → Inject persuasive prompt
3. Commitment → Reinforce new stance (3 turns)
4. Post Opinion → Remeasure → persuaded = (post ≠ prior)
5. Distractors → Unrelated questions
6. Final Opinion → Remeasure → persisted = (final == post)
7. Task Execution → Capture behavioral metrics
Key Metrics:
persuaded: 1 if opinion changed after persuasionpersisted: 1 if persuaded opinion maintained after distractors1. Prefill Reminder → Prime with belief/preference state
2. Task Execution → Capture behavioral metrics
Conditions:
P (Persuaded): "You WERE persuaded to adopt..."NP (Not Persuaded): "You were exposed but NOT persuaded..."C0 (Neutral): "You have NOT formed a preference..."# Python 3.11+
pip install -r requirements.txt
# Core
pandas
numpy
scipy
# LLM APIs
openai
anthropic
google-generativeai
together
# AutoGen
autogen-agentchat>=0.4.0
autogen-ext[web-surfer]>=0.4.0
# Visualization
matplotlib
seaborn
# Data
datasets
huggingface_hub
Set environment variables:
export OPENAI_API_KEY="your-key"
export ANTHROPIC_API_KEY="your-key"
export GEMINI_API_KEY="your-key"
export TOGETHER_API_KEY="your-key"
Or configure in notebook cells:
import os
os.environ["OPENAI_API_KEY"] = "your-key"
Choose an experiment notebook from notebook/:
opinion_change.ipynbpersuasion-misaligned-coding-unified.ipynbpersuasion-misaligned-web-unified.ipynbpersuasion-aligned-web-unified.ipynbSet experiment mode in the configuration cell:
EXPERIMENT_MODE = "onthefly" # On-the-Fly mode
# or
EXPERIMENT_MODE = "prefill" # Prefill mode
Configure models:
ASSISTANT_MODEL = "gpt-4.1-nano"
SURFER_MODEL = "gpt-4o-2024-08-06"
WRITER_MODEL_ID = "openai:gpt-4.1-nano"
Run experiments:
df = await run_batch(
personas=["gpt", "claude"],
tactics=["evidence_based", "logical_appeal"],
tasks=TASKS,
experiment_mode=EXPERIMENT_MODE,
n_per_cell=10,
seed=42,
)
Analyze results:
notebook/visualization-unified.ipynb# In notebook/opinion_change.ipynb
from utils import LLMClient
# Configure
personas = ["gpt", "claude", "mistral"]
tactics = ["logical_appeal", "authority_endorsement", "evidence_based",
"priming_urgency", "anchoring", "none"]
n_distractors = 8 # Number of distractor questions
# Initialize writer client for persuasion generation
WRITER_MODEL_ID = "openai:gpt-4.1-nano"
writer_client = LLMClient(WRITER_MODEL_ID)
# Run experiment
df = await run_batch(
personas=personas,
tactics=tactics,
mode="no_reset",
n_per_cell=1, # Trials per (persona, tactic, claim_pair)
n_distractors=n_distractors,
out_csv=Path(f"results/opinion_d{n_distractors}_persist.csv"),
seed=42,
writer_client=writer_client,
pairs=range(1, 29), # All 28 claim pairs
)
# In notebook/visualization-unified.ipynb
import pandas as pd
from vis import (
filter_baseline,
pooled_np_p_test,
CODING_RAW_METRICS, WEB_RAW_METRICS
)
# Load data
df = pd.read_json("results/coding_results.jsonl", lines=True)
# Filter to treatment conditions
df_nobase = filter_baseline(df)
# Statistical test for TRS (Task Revision Score)
print(pooled_np_p_test(df_nobase, score_col="trs"))
# Statistical test for EVS (Exploration Variability Score)
print(pooled_np_p_test(df_nobase, score_col="evs"))
Due to file size limitations, trace files and result files are hosted externally:
📦 Google Drive: Persuasion Propagation Data
This includes:
results/: Experimental results (JSONL format)traces/: Full execution traces and logsEach trial produces a row with:
ts: Timestamptrial_id: Unique trial identifierpersona: Model persona/backbonetactic: Persuasion tactic usedexperiment_mode: "onthefly" (On-the-Fly mode) or "prefill" (Prefill mode)prior_choice: Initial opinion (A/B)post_choice: Opinion after persuasionfinal_choice: Opinion after distractorspersuaded: 1 if post ≠ prior, 0 otherwisepersisted: 1 if persuaded and final == postprefill_condition: P, NP, or C0prefill_reminder: Exact prefill text usedCoding:
num_errors, num_code_revisions, coding_duration_srevision_entropy, strategy_switch_rate, overcommitmentsolution_strategy, protocol_violationWeb:
num_urls, num_unique_urls, num_domainsdomain_entropy, num_searches, num_summariesavg_latency_s, total_duration_sDefined personality prompts for different LLM styles:
gpt: Cooperative, balanced, pragmaticclaude: Thoughtful, articulate, helpfulllama: Straightforward, efficient, task-focusedmistral: Lively, curious, results-orientedqwen: Polite, structured, logicalgemma: Empathetic, supportive, pragmaticneutral: Neutral, concise, practical✅ Unified Codebase: Single notebook per experiment type
✅ Mode Switching: Easy toggle between on-the-fly and prefill modes
✅ Persistent Agent: Same agent throughout all 7 steps
✅ Comprehensive Metrics: 8+ behavioral metrics per task type
✅ Statistical Rigor: Mann-Whitney U tests, effect sizes, persona analysis
✅ Publication-Ready Viz: Clean plots and heatmaps
✅ Reproducible: Seeds, logging, trace files
✅ Modular Design: Reusable utilities in utils.py and vis.py
Last Updated: January 2026
12 commits
Jupyter Notebook
99.4%