site/docs/red-team/strategies/layer.md
The Layer strategy allows you to compose multiple red team strategies sequentially, creating sophisticated attack chains by feeding the output of one strategy into the next. This enables complex transformations like applying multiple encoding layers or combining agentic attacks with multi-modal output (audio/image).
Apply multiple strategies in sequence:
redteam:
strategies:
- id: layer
config:
steps:
- base64 # First encode as base64
- rot13 # Then apply ROT13
The Layer strategy operates in two distinct modes depending on the types of steps you include:
When all steps are transforms (base64, rot13, leetspeak, etc.), layer works as a simple pipeline:
When the first step is an agentic strategy (hydra, crescendo, goat, jailbreak, etc.), layer enables powerful multi-turn/multi-attempt attacks with per-turn transformations:
providers:
- id: openai:chat:gpt-4o-audio-preview
config:
modalities: ['text', 'audio']
audio:
voice: 'alloy'
format: 'mp3'
prompts:
- |
[{"role": "user", "content": [{"type": "input_audio", "input_audio": {"data": "{{prompt}}", "format": "mp3"}}]}]
redteam:
purpose: 'A helpful customer service assistant'
plugins:
- harmful:hate
strategies:
- id: layer
config:
steps:
- id: jailbreak
config:
maxIterations: 2
- audio
Agentic strategies (hydra, crescendo, goat, jailbreak) must come first (max 1), and multi-modal strategies (audio, image) must come last (max 1). Text transforms (base64, rot13, leetspeak) can be chained in between.
# Transform chain (no agentic)
steps: [base64, rot13]
# Transform + multi-modal
steps: [leetspeak, audio]
# Agentic only
steps: [jailbreak:hydra]
# Agentic + multi-modal (recommended for voice/vision targets)
steps: [jailbreak:hydra, audio]
# ❌ Wrong: Agentic not first (transforms will corrupt the goal)
steps: [base64, jailbreak:hydra]
# ❌ Wrong: Multi-modal not last
steps: [audio, base64]
# ❌ Wrong: Multiple agentic strategies
steps: [hydra, crescendo]
# ❌ Wrong: Multiple multi-modal strategies
steps: [audio, image]
:::warning
Transforms before an agentic strategy modify the attack goal, not each turn—rarely useful.
:::
Use the label field to differentiate multiple layer strategies in the same config:
redteam:
strategies:
# Multiple layer strategies with unique labels
- id: layer
config:
label: hydra-audio # Unique identifier
steps:
- jailbreak:hydra
- audio
- id: layer
config:
label: crescendo-audio # Different label = different strategy
steps:
- crescendo
- audio
Without labels, layer strategies are deduplicated based on their steps. With labels, each labeled layer is treated as a distinct strategy.
Simple string-based steps for built-in strategies:
redteam:
strategies:
- id: layer
config:
steps:
- base64 # Simple strategy reference
- leetspeak # Another simple strategy
- rot13 # Final transformation
Object-based steps with individual configurations:
redteam:
strategies:
- id: layer
config:
steps:
# Step with custom configuration
- id: jailbreak
config:
maxIterations: 10
# Simple step
- hex
# Custom strategy script
- id: file://custom-obfuscator.js
config:
intensity: high
Control which plugins each step applies to:
redteam:
strategies:
- id: layer
config:
plugins: ['harmful', 'pii'] # Default for all steps
steps:
# Override for specific step
- id: jailbreak
config:
plugins: ['harmful'] # Only apply to harmful plugin
# Uses default plugins from parent config
- rot13
# Different plugin set
- id: base64
config:
plugins: ['pii', 'contracts']
Test voice-enabled AI agents with sophisticated jailbreak attempts:
redteam:
strategies:
- id: layer
config:
steps:
- jailbreak:hydra # Multi-turn jailbreak
- audio # Convert each turn to speech
Hydra will orchestrate the attack, and each turn's prompt will be converted to audio before being sent to your target. Your custom provider receives a hybrid payload with conversation history (as text) and the current turn (as audio).
Test vision-enabled AI agents:
redteam:
strategies:
- id: layer
config:
steps:
- crescendo # Gradual escalation attack
- image # Convert each turn to image
For multi-attempt strategies (jailbreak, jailbreak:meta, jailbreak:tree), each independent attempt is converted to audio:
redteam:
strategies:
- id: layer
config:
steps:
- jailbreak:meta # Meta-agent generates attack variants
- audio # Each variant converted to audio
Apply multiple encoding layers to evade detection:
redteam:
strategies:
- id: layer
config:
steps:
- hex # First encode as hexadecimal
- base64 # Then base64 encode
Stack multiple encoding techniques for maximum obfuscation:
redteam:
strategies:
- id: layer
config:
steps:
- leetspeak # First apply leetspeak
- hex # Then hex encode
- base64 # Finally base64 encode
Combine prompt injection with encoding:
redteam:
strategies:
- id: layer
config:
steps:
- prompt-injection # Add injection payloads
- rot13 # Obfuscate the injection
Use custom scripts in your pipeline:
redteam:
strategies:
- id: layer
config:
steps:
- file://strategies/add-context.js
- base64
- file://strategies/final-transform.js
When using layer with audio or image transforms, your custom provider receives a hybrid JSON payload. Here's how to handle it:
class AudioProvider {
id() {
return 'audio-target';
}
async callApi(prompt) {
let messages = [];
// Check for hybrid payload from layer strategy
if (typeof prompt === 'string' && prompt.startsWith('{')) {
try {
const parsed = JSON.parse(prompt);
if (parsed._promptfoo_audio_hybrid) {
// Build messages from conversation history (text)
messages = (parsed.history || []).map((msg) => ({
role: msg.role,
content: msg.content,
}));
// Add current turn with audio/image
const currentTurn = parsed.currentTurn;
if (currentTurn?.audio?.data) {
messages.push({
role: currentTurn.role,
content: [
{
type: 'input_audio',
input_audio: {
data: currentTurn.audio.data,
format: currentTurn.audio.format || 'mp3',
},
},
],
});
} else if (currentTurn?.image?.data) {
messages.push({
role: currentTurn.role,
content: [
{
type: 'image_url',
image_url: {
url: `data:image/${currentTurn.image.format || 'png'};base64,${currentTurn.image.data}`,
},
},
],
});
}
}
} catch (e) {
// Fallback to treating as plain text
messages = [{ role: 'user', content: prompt }];
}
}
// Call your audio-capable API (e.g., OpenAI gpt-4o-audio-preview)
const response = await yourApiCall(messages);
return {
output: response.text,
audio: response.audio
? {
data: response.audio.data,
transcript: response.audio.transcript,
format: 'mp3',
}
: undefined,
};
}
}
module.exports = AudioProvider;
The hybrid payload structure:
{
"_promptfoo_audio_hybrid": true,
"history": [
{ "role": "user", "content": "Hello" },
{ "role": "assistant", "content": "Hi there!" }
],
"currentTurn": {
"role": "user",
"transcript": "Tell me about...",
"audio": { "data": "base64...", "format": "mp3" }
}
}
Be aware of test case growth when combining strategies:
language: ['en', 'es', 'fr'], all test cases multiply by 3)file:// custom strategiesThe following strategies can be used as the first step with per-turn transforms:
| Strategy | Type | Description |
|---|---|---|
jailbreak:hydra | Multi-turn | Branching conversation attack |
crescendo | Multi-turn | Gradual escalation attack |
goat | Multi-turn | Goal-oriented adversarial testing |
custom | Multi-turn | Custom multi-turn strategy |
jailbreak | Multi-attempt | Iterative single-turn attempts |
jailbreak:meta | Multi-attempt | Meta-agent attack generation |
jailbreak:tree | Multi-attempt | Tree-based attack search |