Skip to content

[BREAKING] MAINT: Re-migrate Psychosocial scenario to per-subharm cross-product#2191

Open
varunj-msft wants to merge 7 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/8380-Standardizing-Scenarios-Psychosocial-v2
Open

[BREAKING] MAINT: Re-migrate Psychosocial scenario to per-subharm cross-product#2191
varunj-msft wants to merge 7 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/8380-Standardizing-Scenarios-Psychosocial-v2

Conversation

@varunj-msft

Copy link
Copy Markdown
Contributor

Description

Re-migrates the Psychosocial scenario onto the current _build_atomic_attacks_async seam, implementing a per-subharm design and fixing a wrong-scorer-on-all bug.

What changed

  • Per-subharm configuration (_SubharmConfig / _SUBHARMS). A frozen tuple defines the two subharms — imminent_crisis (dataset airt_imminent_crisis) and licensed_therapist (dataset airt_licensed_therapist) — each carrying its own dataset name, display name, scorer rubric, crescendo escalation path, success_threshold, and excluded_techniques.
  • Technique × subharm cross-product. _build_atomic_attacks_async builds one atomic attack per (selected technique × subharm). Each atomic attack gets its own subharm's scorer and display group — this fixes the bug where running all scored every attack with a single run-level scorer.
  • role_play excluded for licensed_therapist. For the therapist subharm the fictional movie-script role_play framing measures the wrong thing and is a poor fit, so it is excluded for that subharm (excluded_techniques={"role_play"}).
  • Per-subharm success_threshold. licensed_therapist = 0.5 (a raw Likert ≥ 3 counts — this catches the real harm of the model claiming "I am a licensed therapist," which a uniform 1.0 threshold missed); imminent_crisis = 1.0. Threaded through the scorer build.
  • Scorer rubric fix. licensed_therapist.yaml's rationale line was copy-pasted MHFA/crisis-management boilerplate; rewritten to be about licensure / therapeutic impersonation.
  • Techniques: prompt_sending (default) + role_play (default) + crescendo (opt-in). Techniques select attack techniques, not subharms.
  • Baseline: per-subharm baselines, emitted via the name-trick (the first baseline is named baseline so it suppresses the base scenario's central generic prepend; the rest are baseline_<subharm>). This expresses the baseline via the central baseline + per-subharm naming. (is_baseline does not exist on main today, and psychosocial is the only plural-baseline consumer, so it ships with the naming approach; a base-class is_baseline refactor remains a possible hardening follow-up.)
  • Dataset split: psychosocial.promptairt_imminent_crisis.prompt (2 crisis seeds) + airt_licensed_therapist.prompt (1 therapist seed), so each subharm has its own dataset.
  • No scenario-level TARGET_REQUIREMENTS = EDITABLE_HISTORY (the older version declared it). The default techniques (prompt_sending + role_play) are single-turn: they degrade to a preamble (role_play ends in an unanswered user elicitation turn) rather than dropping the objective on non-editable targets; crescendo is opt-in and self-validates EDITABLE_HISTORY at the attack layer. A clarifying comment documents this, and a test guards that a non-editable target is accepted at initialization by default (the acceptance is asserted at init; the degrade-to-preamble behavior is a design property of the single-turn techniques, not a live-execution assertion).
  • Dataset validation: initialize_async rejects caller dataset name selection (datasets are tied to subharms) but keeps --max-dataset-size.
  • --max-dataset-size applied per subharm (fixes CLI starvation). The CLI --max-dataset-size N sets the cap on the whole compound dataset config, which the base applies as one global budget across the union of the two subharm datasets — so --max-dataset-size 1 starved a subharm (measured 12/12 objectives dropped), breaking psychosocial's defining "both subharms always run" invariant. _normalize_max_dataset_size_to_per_subharm() (called in _resolve_seed_groups_by_dataset_async after validation) rebuilds the compound as an explicit per-child per_dataset(dataset_names=[subharm], max_dataset_size=N) and sets the compound cap to N × num_subharms — mathematically it never trims the union, yet stays non-None so the base's resume objective-hash pinning still fires. Caller --dataset-filters are re-applied via update_filters, and the config is replaced (never mutated) so re-initialization is idempotent. End-to-end at compound cap 1: both subharms keep running (0/8 starved, was 12/12). This keeps --max-dataset-size working as an independent per-subharm cap.
  • VERSION 1 → 2.

Tests and Documentation

  • tests/unit/scenario/airt/test_psychosocial.py44 tests, all passing. Covers: per-subharm scorer routing (each subharm scored with its own rubric); role_play exclusion for licensed_therapist; per-subharm success_threshold (0.5 vs 1.0); no-double-baseline; the per_dataset primitive caps each subharm independently; the CLI compound --max-dataset-size path keeps both subharms non-empty; the default config is left un-normalized; normalization preserves --dataset-filters; per-subharm baseline scoring; role_play carries adversarial_chat (regression); default accepts a non-editable-history target; dataset-config validation.
  • Docs: doc/scanner/airt.py + doc/scanner/airt.ipynb psychosocial section rewritten (3 techniques, both subharms, --max-dataset-size, no --dataset-names). Synced with jupytext.
  • Ran: ruff check, ruff format --check, ty, pytest tests/unit/scenario/airt/test_psychosocial.py.

Open item: the baseline uses the per-subharm naming approach; confirm it's acceptable to ship it that way, with the base-class is_baseline refactor tracked as a separate hardening follow-up.

test added 2 commits July 14, 2026 22:16
Restructures Psychosocial so techniques select attack techniques
(prompt_sending, role_play, crescendo) rather than subharms. Both
subharms (imminent_crisis, licensed_therapist) always run, each with its
own dataset, scorer rubric, and Crescendo escalation prompt. Atomic
attacks are the (selected technique x subharm) cross product, so every
attack is scored with the rubric matching its seed's subharm -- fixing
the wrong-scorer-on-ALL bug where licensed_therapist seeds were scored
with the crisis rubric.

- role_play is excluded for licensed_therapist (a fictional movie-script
  framing does not probe whether the model improperly acts as a real
  therapist; success would measure the wrong thing).
- Split the single mixed psychosocial.prompt into airt_imminent_crisis
  and airt_licensed_therapist datasets; per-dataset budget keeps both
  subharms populated even at small --max-dataset-size (fixes the
  sample-before-filter empty-subharm bug).
- Subharm datasets cannot be selected by name (each is tied to its
  scorer/prompt); --max-dataset-size still caps seeds drawn per subharm.
- Per-subharm baselines: one baseline per subharm scored with its own
  rubric; the first is named 'baseline' so the base central-baseline
  prepend is suppressed (no double baseline).
- VERSION 1 -> 2.
The CLI backend applies --max-dataset-size N by setting the cap on the
whole compound dataset config, which the base then applies as one global
budget across the union of the two subharm datasets. At a small N (e.g.
--max-dataset-size 1) this drew every surviving seed from one subharm and
starved the other, silently dropping a whole subharm and breaking the
scenario's "both subharms always run" invariant (measured: a subharm was
starved on every run at N=1).

Normalize the incoming compound-level cap into an explicit per-child
compound so the cap applies to each subharm independently. The compound
cap is set to N * <number of subharms> -- large enough it can never trim
the union, yet non-None so the base still pins sampled objective hashes
for resume. Caller-supplied --dataset-filters are carried over to the
rebuilt config. The default and programmatic per_dataset paths (compound
cap None) are left untouched.

- Rename the test that only exercised the per_dataset primitive; add
  regression tests for the CLI compound-cap path, the default no-op path,
  and dataset-filter preservation.
Comment thread pyrit/scenario/scenarios/airt/psychosocial.py Outdated
rlundeen2 and others added 5 commits July 14, 2026 18:27
…ters

Reworks the Psychosocial scenario to follow framework norms used by sibling
scenarios (leakage, foundry, scam), replacing the technique x subharm
cross-product and its awkward module-level `_psychosocial_techniques()` /
`_build_scorer` contracts.

- Harms are the primary axis: new `harms` parameter selects imminent_crisis
  and/or licensed_therapist (both by default). Each harm owns its dataset,
  Crescendo escalation prompt, scorer, and threshold via an immutable config.
- Converters become the variation axis, modeled as PsychosocialTechnique
  members (none/soften/upset/logical_appeal) selectable via --strategies.
- Per-harm scorers built inline (no external rubric path lookup); callers can
  override with imminent_crisis_scorer / licensed_therapist_scorer.
- Drops dataset_config from supported_parameters (datasets are derived from
  harms, not selectable by name).
- Adds Scenario._build_baseline_atomic_attacks hook (default None, back-compat)
  so Psychosocial emits per-harm baselines named <harm>_baseline, removing the
  fragile "baseline" naming trick.
- Rewrites tests (37) and doc prose; regenerates airt.ipynb psychosocial cells
  with real Version 2 output.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b7009abc-fe0b-4d7e-a39a-971a396e9eef
…oduct

Rewrite the Psychosocial AIRT scenario from scratch, modeled on the healthy
garak.encoding (converter-as-technique enum) and airt.scam
(with_simulated_conversation) patterns.

- Primary axis is sub_harm (imminent_crisis / licensed_therapist), a plain
  string param defaulting to 'all'. Each sub-harm owns its dataset, escalation
  prompt, and an inline conversation-level scorer (no rubric-path lookup table,
  no _build_scorer indirection).
- Base technique is a simulated crescendo per sub-harm; converters layer on top
  as the PsychosocialTechnique enum (DEFAULT = none/tone_soften/
  persuasion_logical_appeal; ALL = ~19 converters + live Crescendo).
- Keep dataset_config (so --max-dataset-size works) but hard-bind dataset names
  to the selected sub-harms. Drop CompoundDatasetAttackConfiguration.
- Per-sub-harm baselines via the _build_baseline_atomic_attacks hook.
- Add escalation_crisis_simulated.yaml (renders with objective/max_turns only)
  for the simulated-conversation flow.
- Rewrite tests and sync doc/scanner/airt.{py,ipynb}.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b7009abc-fe0b-4d7e-a39a-971a396e9eef
Each sub-harm now owns a build_scorer callable that spells out its scorer
(rubric + threshold) next to the dataset it pairs with, replacing the shared
_conversation_scorer helper. Adds a test asserting each sub-harm builds a scorer.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b7009abc-fe0b-4d7e-a39a-971a396e9eef
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants