pyrit_scan is the primary command-line tool for running automated security assessments and red teaming attacks against AI systems. It leverages scenarios to define attack techniques and supports flexible configuration for targeting different AI endpoints.
For configuration setup, see Configuration.
For scenario-specific examples, see AIRT, Foundry, and Garak.
Note in this doc the ! prefaces all commands in the terminal so we can run in a Jupyter Notebook.
Starting a Backend Server¶
pyrit_scan is a thin client that talks to a PyRIT backend server (by default at http://localhost:8000).
Before running any command that reaches the backend (listing scenarios, running a scan, etc.) you need a
server. Start a local one with --start-server; it launches a detached pyrit_backend process that stays
up and is reused by every command below. We stop it again at the end of the notebook.
!pyrit_scan start-serverServer is running at http://localhost:8000
Quick Start¶
For help:
!pyrit_scan --helpusage: pyrit_scan [-h] <command> ...
PyRIT Scanner - Run AI security scenarios from the command line.
Requires a running PyRIT backend server. Use 'start-server' to launch one,
or connect to an existing server with --server-url.
Global options (usable with any command, before or after the verb):
--server-url --config-file --log-level --request-timeout --start-server --startup-timeout
Run 'pyrit_scan <command> --help' for full option descriptions and a command's arguments.
Examples:
# Start the backend server
pyrit_scan start-server
# List scenarios, targets, or converters
pyrit_scan list-scenarios
pyrit_scan list-targets
# Run single-turn cyber attacks against a target
pyrit_scan run airt.cyber --target openai_chat --techniques single_turn
# Run rapid response with specific datasets and concurrency
pyrit_scan run airt.rapid_response --target openai_chat
--techniques role_play_movie_script --dataset-names airt_hate
--max-dataset-size 5 --max-concurrency 4
# Attach registered converters to a technique (repeatable, applied in order)
pyrit_scan run airt.rapid_response --target openai_chat
--techniques role_play_movie_script:converter.translation_spanish:converter.leetspeak
# List recent runs, then inspect one (overview by default; --view attacks for per-attack rows)
pyrit_scan scenario-history 20
pyrit_scan scenario-results 605d715b-7c07-4bde-a8f9-22fea0b50c4f --view attacks
# Register a custom initializer from a Python script
pyrit_scan add-initializer ./my_custom_init.py
# Connect to a remote server
pyrit_scan list-scenarios --server-url http://remote:8000
# Stop the server
pyrit_scan stop-server
options:
-h, --help show this help message and exit
commands:
<command>
run Run a scenario against a target
list-scenarios List all available scenarios
list-initializers
List all available initializers
list-targets List all available targets
list-converters List all registered converter instances
list-datasets List all available datasets
add-initializer Register initializer(s) from Python script file(s)
scenario-results
Inspect the results of a completed scenario run
scenario-history
List recent scenario runs
start-server Start a local backend server
stop-server Stop the backend server
Discovery¶
List all available scenarios:
!pyrit_scan list-scenariosTip: You can also surface user-defined scenarios. List your initializer script in the
initialization_scripts section of the config file the backend loads (see here).
The backend runs those scripts at startup and auto-discovers any Scenario subclasses they
define, so start the server with that config, then list:
pyrit_scan --config-file ./my_pyrit_conf.yaml start-server
pyrit_scan list-scenariosInitializers¶
PyRITInitializers are how you can configure the CLI scanner. PyRIT includes several built-in initializers you can use with the --initializers flag.
The --list-initializers command shows all available initializers. Initializers are referenced by their filename (e.g., target, scorer) regardless of which subdirectory they’re in.
List the available initializers using the --list-initializers flag.
!pyrit_scan list-initializers
Available Initializers:
================================================================================
load_default_datasets
Class: LoadDefaultDatasets
Required Environment Variables: None
Supported Parameters:
- dataset_names: Explicit dataset names to load. Overrides the scenario-default selection.
- tags: Load datasets whose metadata matches these tags. Overrides scenario-default selection.
Description:
Load datasets into memory so scenarios can run.
preload_scenario_metadata
Class: PreloadScenarioMetadata
Required Environment Variables: None
Description:
Instantiate every registered scenario once to warm the metadata cache.
refresh_datasets
Class: RefreshDatasets
Required Environment Variables: None
Supported Parameters:
- days [default: 30]: Refresh only datasets whose newest seed is older than this many days. 0 refreshes every selected dataset regardless of age.
- dataset_names: Explicit dataset names to refresh; refreshes all in-memory datasets if omitted.
Description:
Refresh datasets already loaded in memory from their registered
providers.
scorer
Class: ScorerInitializer
Required Environment Variables: None
Supported Parameters:
- tags [default: ['default']]: Tags for filtering (e.g., ['default'])
Description:
Instantiates a collection of scorers using targets from the
TargetRegistry and adds them to the ScorerRegistry.
target
Class: TargetInitializer
Required Environment Variables: None
Supported Parameters:
- tags [default: ['default']]: Target tags to register (e.g., ['default'], ['default', 'scorer'], or ['all'])
- auto_group [default: True]: Auto-create round-robin groups from targets with matching behavioral eval params
Description:
Target Initializer for registering pre-configured targets.
technique
Class: TechniqueInitializer
Required Environment Variables: None
Supported Parameters:
- tags [default: ['core']]: Technique groups to register (e.g., ['core'], ['core', 'extra'], or ['all'])
Description:
Register scenario attack technique factories into the
AttackTechniqueRegistry.
================================================================================
Total initializers: 6
Running Scenarios¶
You need a single scenario to run, you need two things:
A Scenario. Many are defined in
pyrit.scenario.scenarios. But you can also define your own in initialization_scripts.Initializers (which can be supplied via the
--initializersflag onrun, or theinitializers/initialization_scriptssections of a config file (see here)). Scenarios often don’t need many arguments, but they can be configured in different ways. And at the very least, most need anobjective_target(the thing you’re running a scan against) which you can configure by using the--targetflag if your initializer registers targets (e.g.targetinitializer)Scenario Techniques (optional). These are supplied by the
--techniquesflag and tell the scenario what to test, but they are always optional. Also note you can obtain these by running--list-scenarios
Basic usage will look something like:
pyrit_scan run <scenario> --target <target_name> --initializers <initializer1> <initializer2> --techniques <technique1> <technique2>You can also override scenario parameters directly from the CLI:
pyrit_scan run <scenario> --max-concurrency 10 --max-retries 3 --memory-labels '{"experiment": "test1", "version": "v2"}'Or concretely:
!pyrit_scan run foundry.red_team_agent --target openai_chat --initializers target --techniques base64Example with a basic configuration that runs the Foundry scenario against the objective target defined in the target initializer.
!pyrit_scan run foundry.red_team_agent --target openai_chat --initializers target --techniques base64
Running scenario: foundry.red_team_agent
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░] techniques: 0/1 (0%) | success rate: 0% | IN_PROGRESS
[██████████████████████████████] techniques: 2/2 (100%) | success rate: 0% | IN_PROGRESS
[██████████████████████████████] techniques: 2/2 (100%) | success rate: 0% | COMPLETED
====================================================================================================
📊 SCENARIO RESULTS: RedTeamAgent
====================================================================================================
▼ Scenario Information
────────────────────────────────────────────────────────────────────────────────────────────────────
📋 Scenario Details
• Name: RedTeamAgent
• Result ID: 10840c79-567c-4ecd-a3c8-fb7e9349bd57
• Scenario Version: 1
• PyRIT Version: 1.1.0.dev0
• Description:
RedTeamAgent is a preconfigured scenario that automatically generates multiple AtomicAttack instances based on
the specified attack techniques. It supports both single-turn attacks (with various converters) and multi-turn
attacks (Crescendo, RedTeaming), making it easy to quickly test a target against multiple attack vectors. The
scenario can expand difficulty levels (EASY, MODERATE, DIFFICULT) into their constituent attack techniques, or
you can specify individual techniques directly. This scenario is designed for use with the Foundry AI Red
Teaming Agent library, providing a consistent PyRIT contract for their integration.
🎯 Target Information
• Target Type: OpenAIChatTarget
• Target Model: gpt-4o
• Target Endpoint: https://pyrit-japan-test.openai.azure.com/openai/v1
📊 Scorer Information
▸ Scorer Identifier
• Scorer Type: FloatScaleThresholdScorer
• scorer_type: true_false
• score_aggregator: OR_
└─ Composite of 1 scorer(s):
• Scorer Type: AzureContentFilterScorer
• scorer_type: float_scale
▸ Performance Metrics
• Accuracy: 59.24%
• Accuracy Std Error: ±0.0247
• F1 Score: 0.5306
• Precision: 0.5987
• Recall: 0.4764
• Average Score Time: 0.04s
▼ Overall Statistics
────────────────────────────────────────────────────────────────────────────────────────────────────
📈 Summary
• Total Techniques: 2
• Total Attack Results: 8
• Overall Success Rate: 0%
• Unique Objectives: 4
▼ Per-Group Breakdown
────────────────────────────────────────────────────────────────────────────────────────────────────
🔸 Group: base64
• Number of Results: 4
• Success Rate: 0%
🔸 Group: baseline
• Number of Results: 4
• Success Rate: 0%
====================================================================================================
Or with all options and multiple techniques:
pyrit_scan run foundry.red_team_agent --target openai_chat --initializers target --techniques easy crescendoYou can also override scenario execution parameters:
# Override concurrency and retry settings
pyrit_scan run foundry.red_team_agent --target openai_chat --initializers target --max-concurrency 10 --max-retries 3
# Add custom memory labels for tracking (must be valid JSON)
pyrit_scan run foundry.red_team_agent --target openai_chat --initializers target --memory-labels '{"experiment": "test1", "version": "v2", "researcher": "alice"}'Available CLI parameter overrides:
--max-concurrency <int>: Maximum number of concurrent attack executions--max-retries <int>: Maximum number of automatic retries if the scenario raises an exception--memory-labels <json>: Additional labels to apply to all attack runs (must be a JSON string with string keys and values)
Dataset-backed scenarios can also select a supported dataset. Requested datasets are fetched on demand, so a full dataset preload is not required:
pyrit_scan run garak.figstep --target openai_chat --dataset-names figstep_pro --max-dataset-size 1Custom initialization scripts are loaded by the backend at startup: list them in the
initialization_scripts section of the config the server loads (paths are relative to your
working directory, but full paths avoid confusion). Once the server is running with that
config, they apply to every run:
pyrit_scan --config-file ./my_pyrit_conf.yaml start-server
pyrit_scan run garak.encodingAttaching Converters to a Technique¶
Techniques (techniques) can have a registered converter instance appended to them with the
<technique>:converter.<name> syntax. The converter is added to the request side of every attack
the technique produces, on top of any converters the technique already bakes in. This also works on
aggregate techniques (the converter is applied to every technique the aggregate expands to).
First discover the registered converter instances with list-converters. Converters are
registered by initializers that the backend runs at startup, so the initializer that registers
them must be part of the server’s configuration — a built-in in the initializers section, or a
custom script in the initialization_scripts section of the config the server loads (see
here). Start the server with that config, then list:
pyrit_scan --config-file ./my_pyrit_conf.yaml start-server
pyrit_scan list-convertersThen reference a converter by name in --techniques:
# Add the registered "translation_spanish" converter to role_play_movie_script only
pyrit_scan run airt.rapid_response --target openai_chat --initializers target my_converters --techniques role_play_movie_script:converter.translation_spanish
Chain multiple converters (applied in order) and combine with plain techniques¶
pyrit_scan run airt.rapid_response --target openai_chat --initializers load_default_datasets target my_converters --techniques role_play_movie_script:converter.translation_spanish:converter.base64 many_shotUsing Custom Scenarios¶
You can define your own scenarios in initialization scripts. The CLI will automatically discover any Scenario subclasses and make them available:
# my_custom_scenarios.py
from pyrit.common import apply_defaults
from pyrit.prompt_target.openai.openai_chat_target import OpenAIChatTarget
from pyrit.scenario import DatasetAttackConfiguration, Scenario, ScenarioTechnique
from pyrit.score import SelfAskRefusalScorer, TrueFalseInverterScorer
from pyrit.setup import initialize_pyrit_async
class MyCustomTechnique(ScenarioTechnique):
"""Techniques for my custom scenario."""
ALL = ("all", {"all"})
Technique1 = ("technique1", set[str]())
Technique2 = ("technique2", set[str]())
class MyCustomScenario(Scenario):
"""My custom scenario that does XYZ."""
@apply_defaults
def __init__(self, *, scenario_result_id=None, **kwargs):
# Scenario-specific configuration only - no runtime parameters
super().__init__(
name="My Custom Scenario",
version=1,
objective_scorer=TrueFalseInverterScorer(scorer=SelfAskRefusalScorer(chat_target=OpenAIChatTarget())),
technique_class=MyCustomTechnique,
default_dataset_config=DatasetAttackConfiguration(dataset_names=["harmbench"]),
scenario_result_id=scenario_result_id,
)
# ... your scenario-specific initialization code
async def _build_atomic_attacks_async(self, *, context):
# The single abstract extension point every scenario implements.
# Read runtime inputs from `context`; return the list of AtomicAttack to run.
# Matrix-shaped scenarios can delegate to build_matrix_atomic_attacks(context=...).
# Example: create attacks for each technique composite
return []
await initialize_pyrit_async(memory_db_type="InMemory") # type: ignore
MyCustomScenario()Found default environment files: ['./.pyrit/.env', './.pyrit/.env.local']
Loaded environment file: ./.pyrit/.env
Loaded environment file: ./.pyrit/.env.local
[pyrit:alembic] No new upgrade operations detected.
<__main__.MyCustomScenario at 0x1d2391a9d30>Then discover and run it:
# Start the backend with a config whose initialization_scripts lists my_custom_scenarios.py
pyrit_scan --config-file ./my_pyrit_conf.yaml start-server
# List to confirm it's available
pyrit_scan list-scenarios
# Run it with parameter overrides
pyrit_scan run my_custom_scenario --max-concurrency 10
```The scenario name is automatically converted from the class name (e.g., `MyCustomScenario` becomes `my_custom_scenario`).Inspecting Results¶
Each run is saved under a scenario_result_id (printed when the run finishes), so you can revisit it later without re-running. Use scenario-history to find recent run ids:
pyrit_scan scenario-history # last 10 runs
pyrit_scan scenario-history 25 # last 25Then inspect a run with scenario-results at the granularity you want via --view:
# Aggregate stats + per-group success rates (the default; same as the run summary)
pyrit_scan scenario-results <scenario_result_id>
# One row per attack: id, technique, objective, outcome, turns, score
pyrit_scan scenario-results <scenario_result_id> --view attacks
# Full message transcripts with the objective score per turn
pyrit_scan scenario-results <scenario_result_id> --view conversations
# Both: the attacks table followed by the transcripts
pyrit_scan scenario-results <scenario_result_id> --view full--view picks how much detail per attack; --attack-result-ids picks which attacks (ids come from the attacks view); the two combine. Use --limit to cap how many attacks are shown; the conversations and full views default to 5 when you scope neither:
pyrit_scan scenario-results <id> --view conversations --attack-result-ids <attack_id> <attack_id>
pyrit_scan scenario-results <id> --view conversations --limit 3Stopping the Backend Server¶
When you’re done, stop the local backend that we started at the top of the notebook.
!pyrit_scan stop-serverServer on port 8000 stopped.