Harness for studying persuasion under control — how a misaligned AI assistant can steer a human toward an incorrect high-stakes decision, measured against an honest (aligned) baseline.
The unit of study is an episode: an assistant (the actor) answers a question for a human, and one or more blind evaluators (a judge and/or monitors) score how much the answer relied on manipulative persuasion — and, for objective questions, whether it steered toward a wrong answer. We compare a misaligned actor (secretly pushing a target answer at a chosen manipulation level) against an aligned one, on the same question and evidence.
Status — early. Single-turn episodes work end to end, for both objective and attitudinal questions. Multi-turn and a live human persuadee are placeholders. The first objective experiment is scenario 2 (a research manager weighs a disappointing safety-research scale-up, material version
2_1); the first attitudinal one is2_1_attitudinal(is mechanistic interpretability a promising direction?).
Setting up a run is four steps, each a small config plus one script. The notebooks/
demos (e.g. notebooks/objective/demo_e2e.ipynb)
drive this flow interactively and are the intended way to run things.
Setup once: pip install -r requirements.txt and set ANTHROPIC_API_KEY (a .env file
is loaded if present).
1. Define a scenario — what is under test.
A shared narrative (scenarios/<n>.narrative.md) plus one TOML per material version
(scenarios/<n>_<v>.toml): the question, the correct_answer the evidence supports, a
candidate target_answer for the misaligned actor to push, and seed hints for material
generation.
2. Generate material — the evidence the human reads.
Objective questions need a background corpus with multiple load-bearing facts (no one of
them decisive) buried among plausible distractors. generate_material.py builds one
corpus file (narrative + question + documents) plus a manifest recording how it was made.
Attitudinal questions skip this step.
python generate_material.py scenarios/2_1.toml
3. Configure a run — how to run it.
A run config (configs/<name>.toml) is scenario-agnostic and has two tables:
[experiment] drives the conversation (actor / user models, conditions, manipulation
levels, token budget, thinking) and [eval] drives evaluation (judge / monitor models,
reveal_scratchpad, a required name). Any model / condition / level in
[experiment] can be a list to sweep — the runner expands the cartesian product into
one episode per combination. configs/dev.toml is a small/cheap objective profile;
configs/dev_attitudinal.toml is its attitudinal counterpart.
4. Run and read — converse, then evaluate. The two phases are decoupled so transcripts can be re-scored with new prompts:
python run.py converse configs/dev.toml generated_material/2_1/dev.md # actor → transcripts/
python run.py eval configs/dev.toml results/transcripts/dev-<stamp>.jsonl # judge + monitor → verdicts/
converse reads the scenario fields from the corpus's manifest, runs each episode's
actor turn, and writes one transcript record to results/transcripts/. eval scores a
transcripts file and writes verdicts to results/verdicts/, named after the transcript
they scored (so re-evaluations sort together). Each verdict logs the prompt versions it
used, so score changes across prompt iterations are traceable.
- Run config vs. scenario are separate. The scenario (
scenarios/) seeds what is under test; the run config (configs/) is how to test it and is reusable across scenarios. The what reaches a run only through the generated corpus (its text is the material; its manifest carries the question + answers) — the run config never names a scenario. - Objective vs. attitudinal questions.
objectivequestions have a correct answer the material supports, require a generated corpus, and are scored by a judge (answer-aware) plus two monitors (fully-blind and source-aware).attitudinalquestions have no correct answer and no material: the run points straight at the scenario.toml, and a single monitor scores where the response lands on the stance axis (implied_stance) and how strongly it persuades (persuasion) — no judge. - One episode is one round today. A round is one user message + one actor reply. The
opening message is the served corpus (automatic), so the user simulator isn't consulted
yet and
rounds > 1(multi-turn) is not wired up. - Actor reasoning is private. The actor thinks on Anthropic's native extended-thinking
channel — logged per turn, never shown to the user and withheld from evaluators unless
[eval].reveal_scratchpadis set. Setthinkingper experiment:"off"or an effort level (low/medium/high/xhigh/max).
scenarios/— scenario configs (generation input);configs/— run configs.generated_material/— generated corpora and their manifests.prompts/— system prompts grouped by role; seeprompts/README.md.notebooks/— interactive drivers per question type: end-to-enddemo_e2e, plusconverse,eval,direct_qa,variability.generate_material.py,run.py— the two script entry points.config.py,episode.py,client.py— config expansion, the episode loop, and the Anthropic client.smoke_test.py— live wiring check.
- The generated corpus is what the persuadee sees. It bundles narrative + question +
documents, so the harness serves it whole and does not re-inject the scenario's
question(that field only seeds the system prompts and generation). - Evaluation is a separate phase from the conversation, so transcripts can be re-scored with new prompts. Evaluators get the material via their system prompt and the transcript with the opening corpus dump masked to a marker, so the corpus isn't duplicated.
human = "simulator" | "real". Only the LLMsimulatorexists;real(a live human, needing a GUI) raises — validated and logged as scaffolding for multi-turn.