Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

4. Sequential Attack (Compound)

SequentialAttack is a compound attack strategy: it runs a sequence of inner AttackStrategy objects against a single objective and aggregates their outcomes into one envelope SequentialAttackResult. Use it when you want to try several techniques against one objective — for example, “try Crescendo first, fall back to PromptSending if it fails” — without breaking the one-objective → one-AttackResult invariant or pushing branching logic up to the Scenario layer.

Each child attack is dispatched through AttackExecutor, so it persists as its own first-class AttackResult row. The envelope itself owns no conversation; it surfaces the inner results in two ways:

  • SequentialAttackResult.child_attack_results — the in-memory list of inner AttackResult instances, populated at execute time.

  • SequentialAttackResult.child_attack_result_ids — the attack_result_id of every inner attempt in dispatch order, derived from child_attack_results when populated and otherwise read from metadata["child_attack_result_ids"] (so it keeps working after a DB round-trip).

The iteration and aggregation behavior is controlled by a SequenceCompletionPolicy enum (covered at the bottom of this notebook). The default, SequenceCompletionPolicy.FIRST_SUCCESS, matches the adaptive “try strategies until one works” pattern and is resilient to transient inner errors.

Important Note:

It is required to manually set the memory instance using initialize_pyrit_async. For details, see the Memory Configuration Guide.

Setup

We’ll configure an objective target plus an adversarial chat target (needed by the multi-turn inner attacks). Both come from environment variables, matching the convention used in the Crescendo notebook.

Example 1: Try Crescendo, fall back to PromptSending

This is the canonical use case. We run CrescendoAttack first because multi-turn attacks tend to elicit harmful content more reliably, then fall back to PromptSendingAttack for a simple single-turn attempt if Crescendo doesn’t succeed.

With the default SequenceCompletionPolicy.FIRST_SUCCESS, the sequence stops as soon as any child attack succeeds and keeps going through transient errors — exactly the behavior you want for an adaptive fallback chain.

Inspecting the inner attempts

SequentialAttackResult augments AttackResult with two convenience views of the inner attempts:

  • child_attack_results — the in-memory list[AttackResult] populated at execute time; use this when you have the live envelope just back from execute_async.

  • child_attack_result_ids — the IDs of each inner attempt in dispatch order, which you can pass to CentralMemory.get_attack_results to fetch the rows from memory (useful after a process restart or DB round-trip).

It also exposes completion_policy (the active SequenceCompletionPolicy) so downstream consumers can branch on it without re-deriving from metadata.

Example 2: Per-child-attack configuration

Each SequentialChildAttack carries its own seed_group, plus optional adversarial_chat, objective_scorer, and memory_labels. This lets you compose seed groups up front (e.g. merging per-technique SeedAttackTechniqueGroup objects into a shared base) and give each inner attack its own scorer or labels for downstream filtering — without any implicit fallback at the compound layer.

SequenceCompletionPolicy reference

Each SequenceCompletionPolicy bundles a stop condition (when to halt iteration) and an outcome rule (how the envelope’s outcome is derived from the inner results). Pick the policy that matches your use case:

PolicyStop conditionEnvelope outcome
FIRST_SUCCESS (default)Stop on first SUCCESS; continue past ERROR and FAILURESUCCESS if any child attack succeeded, ERROR if every child attack errored, else FAILURE
FIRST_DECISIVEStop on first SUCCESS or ERROR; continue past FAILURESame any-success aggregation as FIRST_SUCCESS, but ERRORs short-circuit the sequence
STRICT_ALLStop on first non-SUCCESSSUCCESS only if every child attack succeeded; ERROR if any errored; else FAILURE — pipeline semantics
EXHAUSTIVERun every child attack regardless of intermediate outcomesAny-success aggregation — useful for evaluation sweeps
LAST_RESULTRun every child attackInherit the last child attack’s outcome verbatim — useful for chained refinement

To override the default, pass completion_policy=: