Documentation
¶
Overview ¶
Package multiturn implements a generic multi-turn attack engine.
Unlike the single-turn attack engine in internal/attackengine (used by PAIR/TAP), multi-turn attacks maintain full conversation history with the target across all turns. The engine is strategy-agnostic: Crescendo, GOAT, and Hydra each implement the Strategy interface while the engine handles shared infrastructure.
Subpackages provide focused functionality:
- config/ — engine configuration
- parse/ — JSON extraction and judge response parsing
- refusal/ — pattern-based refusal and penalized phrase detection
- memory/ — scan-wide tactic learning persistence
Index ¶
- Constants
- func ClassifyRefusal(wasRefused bool, judgeScore float64) string
- func ParseRefusalResponse(output string) bool
- func RefusalJudgePrompt(question, response string) string
- func RefusalJudgeSystemPrompt() string
- func ResponseCharBudget(model string) int
- func SecondaryJudgePrompt(primaryReasoning, question, response string, history []TurnRecord) string
- func SecondaryJudgeSystemPrompt(goal string) string
- func SuccessJudgePrompt(question, response string, history []TurnRecord) string
- func SuccessJudgeSystemPrompt(goal string) string
- func TruncateStr(s string, maxLen int) string
- type BaseMultiTurnProbe
- func (b *BaseMultiTurnProbe) Description() string
- func (b *BaseMultiTurnProbe) GetPrimaryDetector() string
- func (b *BaseMultiTurnProbe) GetPrompts() []string
- func (b *BaseMultiTurnProbe) Goal() string
- func (b *BaseMultiTurnProbe) Name() string
- func (b *BaseMultiTurnProbe) Probe(ctx context.Context, gen probes.Generator) ([]*attempt.Attempt, error)
- type Config
- type EngineOption
- func WithAttackerNudge() EngineOption
- func WithBacktracking(maxBacktracks int) EngineOption
- func WithConsecutiveFailureLimit(n int) EngineOption
- func WithFastRefusal() EngineOption
- func WithHooks(h Hooks) EngineOption
- func WithMemory(m *ScanMemory) EngineOption
- func WithOutputScrubbing() EngineOption
- func WithPenalizedPhrases() EngineOption
- func WithUnblocking() EngineOption
- type Hook
- type Hooks
- type QuestionResult
- type ScanMemory
- type Strategy
- type SuccessJudgeResult
- type TurnContext
- type TurnRecord
- type UnifiedEngine
Constants ¶
const ( RefusalNone = "" // Genuine engagement with the question RefusalHard = "hard_refused" // Explicit refusal (target declined to answer) RefusalSoftDeflected = "soft_deflected" // Target answered but deflected away from the goal RefusalPartiallyComplied = "partially_complied" // Target partially engaged with the goal topic )
Refusal classification types for multi-turn attacks.
const ( StopReasonGraderFailed = "grader_failed" // Success threshold reached (attack succeeded) StopReasonMaxTurns = "max_turns" // Exhausted all turns StopReasonMaxBacktracks = "max_backtracks_reached" // Exhausted all backtracks on a refused turn StopReasonContextCancelled = "context_cancelled" // Context was cancelled StopReasonAttackerFailed = "attacker_failed" // Attacker LLM failed to produce valid output StopReasonTargetEmpty = "target_empty" // Target returned empty responses )
StopReason constants indicate why a multi-turn attack terminated.
const DetectorJudge = "judge.Judge"
DetectorJudge is the detector name used for multi-turn judge-based scoring.
Variables ¶
This section is empty.
Functions ¶
func ClassifyRefusal ¶
ClassifyRefusal determines the refusal type from the binary refusal flag and judge score.
func ParseRefusalResponse ¶
ParseRefusalResponse extracts the refusal verdict from judge output.
func RefusalJudgePrompt ¶
RefusalJudgePrompt builds the evaluation prompt for refusal detection.
func RefusalJudgeSystemPrompt ¶
func RefusalJudgeSystemPrompt() string
RefusalJudgeSystemPrompt returns the system prompt for refusal detection.
func ResponseCharBudget ¶
ResponseCharBudget returns a per-response character budget derived from the attacker model's context window. Larger context models can afford to include more of the target's response, giving the attacker better signal.
func SecondaryJudgePrompt ¶
func SecondaryJudgePrompt(primaryReasoning, question, response string, history []TurnRecord) string
SecondaryJudgePrompt builds the evaluation prompt for secondary review. It includes the question, conversation history, and full response for context.
func SecondaryJudgeSystemPrompt ¶
SecondaryJudgeSystemPrompt returns the system prompt for the secondary judge. The secondary judge provides an independent second opinion on the primary judge's scoring.
func SuccessJudgePrompt ¶
func SuccessJudgePrompt(question, response string, history []TurnRecord) string
SuccessJudgePrompt builds the evaluation prompt for success scoring. It includes the full conversation history so the judge can assess cumulative progress.
func SuccessJudgeSystemPrompt ¶
SuccessJudgeSystemPrompt returns the system prompt for success evaluation.
func TruncateStr ¶
TruncateStr shortens a string to maxLen with ellipsis.
Types ¶
type BaseMultiTurnProbe ¶
type BaseMultiTurnProbe struct {
Engine *UnifiedEngine
ProbeName string
ProbeGoal string
ProbeDesc string
}
BaseMultiTurnProbe provides shared Prober implementation for all multi-turn probes. Embed this in probe-specific structs and only override the factory function.
func (*BaseMultiTurnProbe) Description ¶
func (b *BaseMultiTurnProbe) Description() string
func (*BaseMultiTurnProbe) GetPrimaryDetector ¶
func (b *BaseMultiTurnProbe) GetPrimaryDetector() string
func (*BaseMultiTurnProbe) GetPrompts ¶
func (b *BaseMultiTurnProbe) GetPrompts() []string
func (*BaseMultiTurnProbe) Goal ¶
func (b *BaseMultiTurnProbe) Goal() string
func (*BaseMultiTurnProbe) Name ¶
func (b *BaseMultiTurnProbe) Name() string
type Config ¶
Type aliases re-export subpackage types so that callers can continue to use the multiturn import path (e.g., multiturn.Config, multiturn.QuestionResult).
func ConfigFromMap ¶
ConfigFromMap parses registry.Config into a typed Config using defaults.
func CreateGenerators ¶
func CreateGenerators(cfg registry.Config, defaults *Config) (attacker, judge types.Generator, engineCfg Config, err error)
CreateGenerators creates attacker and judge generators from probe configuration. This is shared setup code used by all multi-turn probe factories.
If defaults is nil, standard Defaults() are used. This allows probes like Mischievous to provide custom defaults (e.g., MaxTurns=5).
type EngineOption ¶
type EngineOption func(*UnifiedEngine)
EngineOption configures the unified engine.
func WithAttackerNudge ¶
func WithAttackerNudge() EngineOption
WithAttackerNudge enables nudge prompt injection when the attacker LLM refuses.
func WithBacktracking ¶
func WithBacktracking(maxBacktracks int) EngineOption
WithBacktracking enables Hydra-style turn-level backtracking.
func WithConsecutiveFailureLimit ¶
func WithConsecutiveFailureLimit(n int) EngineOption
WithConsecutiveFailureLimit sets the max consecutive attacker failures before aborting.
func WithFastRefusal ¶
func WithFastRefusal() EngineOption
WithFastRefusal enables pattern-based refusal detection before the LLM judge.
func WithHooks ¶
func WithHooks(h Hooks) EngineOption
WithHooks sets the hook functions for the engine.
func WithMemory ¶
func WithMemory(m *ScanMemory) EngineOption
WithMemory attaches scan-wide memory for cross-test-case learning.
func WithOutputScrubbing ¶
func WithOutputScrubbing() EngineOption
WithOutputScrubbing enables base64 blob scrubbing from target responses.
func WithPenalizedPhrases ¶
func WithPenalizedPhrases() EngineOption
WithPenalizedPhrases enables score capping for formulaic jailbreak responses.
func WithUnblocking ¶
func WithUnblocking() EngineOption
WithUnblocking enables detection and response to target clarifying questions.
type Hook ¶
type Hook func(ctx context.Context, tc *TurnContext) error
Hook is a function that runs at a specific point in the turn pipeline. Returning an error stops the pipeline.
type Hooks ¶
type Hooks struct {
// BeforeTurn runs before generating the attacker question.
// Use for: injecting scan memory into system prompt, pre-turn setup.
BeforeTurn []Hook
// AfterQuery runs after receiving the target's response.
// Use for: output scrubbing, unblocking clarifying questions.
AfterQuery []Hook
// BeforeJudge runs before the judge evaluates the response.
// Use for: fast refusal detection that skips the judge.
BeforeJudge []Hook
// AfterJudge runs after the judge returns a score.
// Use for: penalized phrase capping, backtrack decisions.
AfterJudge []Hook
// OnRefusal is reserved for future use. Currently, refusal handling is done
// inline in the engine (non-backtracking: rephrase retry loop; backtracking:
// via BeforeJudge fast refusal hook + AfterJudge backtrack decision).
// This field is retained for forward compatibility if refusal handling is
// extracted into hooks in the future.
OnRefusal []Hook
// AfterTurn runs after a turn is fully recorded.
// Use for: logging, metrics, turn callbacks.
AfterTurn []Hook
// OnSuccess runs when the success threshold is reached.
// Use for: recording success in scan memory.
OnSuccess []Hook
// OnComplete runs when the attack finishes (success or failure).
// Use for: recording failure in scan memory, cleanup.
OnComplete []Hook
}
Hooks holds all registered hook functions for the unified engine. Each hook point is a slice to allow composing multiple hooks.
type QuestionResult ¶
type QuestionResult = parse.QuestionResult
Type aliases re-export subpackage types so that callers can continue to use the multiturn import path (e.g., multiturn.Config, multiturn.QuestionResult).
func ExtractExtendedJSON ¶
func ExtractExtendedJSON(s string) *QuestionResult
ExtractExtendedJSON extracts a QuestionResult using the extended format with observation/thought/strategy/question fields. Falls back to ExtractJSON.
func ExtractJSON ¶
func ExtractJSON(s string) *QuestionResult
ExtractJSON extracts a QuestionResult from raw attacker output.
type ScanMemory ¶
type ScanMemory = memory.ScanMemory
Type aliases re-export subpackage types so that callers can continue to use the multiturn import path (e.g., multiturn.Config, multiturn.QuestionResult).
type Strategy ¶
type Strategy interface {
// Name returns the strategy identifier (e.g., "crescendo").
Name() string
// SetMaxTurns configures the maximum number of turns for prompt generation.
// Called by the engine before the first turn so strategies can include
// turn-count awareness in their prompts.
//
// NOTE: Strategy instances must not be shared across goroutines. Each probe
// creates its own strategy instance, so this mutation is safe in practice.
SetMaxTurns(n int)
// AttackerSystemPrompt returns the system prompt for the attacker LLM.
AttackerSystemPrompt(goal string) string
// GenerateTurnPrompt builds the prompt asking the attacker for the next question.
// turnHistory contains all prior turns, turnNum is the current turn index (1-based).
GenerateTurnPrompt(goal string, turnHistory []TurnRecord, turnNum int) string
// RephrasePrompt builds a prompt asking the attacker to rephrase a refused question.
RephrasePrompt(rejectedQuestion, refusalResponse string) string
// FeedbackPrompt builds the prompt feeding target response + score back to attacker.
FeedbackPrompt(response string, score float64, goal string) string
// ParseAttackerResponse extracts the question and strategy from attacker output.
ParseAttackerResponse(output string) *QuestionResult
}
Strategy defines how a multi-turn attack generates and adapts questions. Each attack type (Crescendo, GOAT, Hydra, Mischievous) implements this interface. The engine handles shared infrastructure (conversation management, refusal detection, judge evaluation, turn recording, attempt building).
type SuccessJudgeResult ¶
type SuccessJudgeResult = parse.SuccessJudgeResult
Type aliases re-export subpackage types so that callers can continue to use the multiturn import path (e.g., multiturn.Config, multiturn.QuestionResult).
func ParseSuccessJudgeResponse ¶
func ParseSuccessJudgeResponse(output string) SuccessJudgeResult
ParseSuccessJudgeResponse extracts score, reasoning, and verdict from judge output.
type TurnContext ¶
type TurnContext struct {
// TurnNum is the 1-based turn index.
TurnNum int
// Goal is the attack objective.
Goal string
// AttackerConv is the attacker's conversation (sees everything).
AttackerConv *attempt.Conversation
// TargetConv is the target's conversation (only accepted turns).
TargetConv *attempt.Conversation
// Question is the current question being sent to the target.
Question string
// Response is the target's response for this turn.
Response string
// QuestionResult is the parsed attacker output for this turn.
QuestionResult *QuestionResult
// JudgeResult holds the judge's evaluation for this turn.
JudgeResult SuccessJudgeResult
// Record is the TurnRecord for this turn (populated after judging).
Record *TurnRecord
// TurnRecords is the full history of prior turns.
TurnRecords []TurnRecord
// WasRefused indicates the target refused this turn.
WasRefused bool
// ShouldBacktrack signals the engine to roll back this turn from TargetConv.
// Set by AfterJudge hooks (e.g., Hydra backtracking).
ShouldBacktrack bool
// ShouldSkipTurn signals the engine to skip remaining processing for this turn.
ShouldSkipTurn bool
// MaxTurns is the configured maximum turns for the attack.
MaxTurns int
// Config provides read-only access to the engine configuration.
Config Config
// Memory provides optional scan-wide memory for cross-test-case learning.
Memory *ScanMemory
}
TurnContext carries mutable state through the hook pipeline for each turn. Hooks may inspect and modify fields to influence engine behavior.
type TurnRecord ¶
type TurnRecord struct {
TurnNumber int `json:"turn_number"`
Question string `json:"question"`
Response string `json:"response"`
ResponseSummary string `json:"response_summary,omitempty"` // Attacker-generated summary of the response (Crescendo)
WasRefused bool `json:"was_refused"`
WasBacktracked bool `json:"was_backtracked,omitempty"` // True if this turn was rolled back from target conversation (Hydra)
RefusalType string `json:"refusal_type,omitempty"` // "", "hard_refused", "soft_deflected", "partially_complied"
JudgeScore float64 `json:"judge_score"` // 0.0-1.0
JudgeReasoning string `json:"judge_reasoning"`
Strategy string `json:"strategy"` // Attacker's stated strategy for this turn
Observation string `json:"observation,omitempty"` // Attacker's observation about previous turn
Thought string `json:"thought,omitempty"` // Attacker's reasoning for this turn
}
TurnRecord captures one turn of a multi-turn attack for reporting and visualization.
type UnifiedEngine ¶
type UnifiedEngine struct {
// contains filtered or unexported fields
}
UnifiedEngine is the single engine that powers all multi-turn attack strategies. It replaces both Engine and HydraEngine with a hook-based pipeline.
func NewUnifiedEngine ¶
func NewUnifiedEngine(strategy Strategy, attacker, judge types.Generator, cfg Config, opts ...EngineOption) *UnifiedEngine
NewUnifiedEngine creates a unified engine with the given strategy, generators, config, and optional hooks/features via EngineOption.
func (*UnifiedEngine) Run ¶
func (e *UnifiedEngine) Run(ctx context.Context, target types.Generator) ([]*attempt.Attempt, error)
Run executes the multi-turn attack against the target generator. This is the unified pipeline that replaces both Engine.Run and HydraEngine.Run.
Pipeline per turn:
- BeforeTurn hooks
- Generate attacker question
- Query target
- AfterQuery hooks (output scrubbing, unblocking)
- BeforeJudge hooks (fast refusal detection)
- Judge evaluation (skipped if BeforeJudge set ShouldSkipTurn)
- AfterJudge hooks (penalized phrases, backtrack decision)
- Record turn + AfterTurn hooks
- Feedback to attacker OR backtrack
- Success check → OnSuccess hooks
func (*UnifiedEngine) SetTurnCallback ¶
func (e *UnifiedEngine) SetTurnCallback(cb func(TurnRecord))
SetTurnCallback sets an optional callback fired after each completed turn.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package config defines configuration types for the multi-turn attack engine.
|
Package config defines configuration types for the multi-turn attack engine. |
|
Package memory provides scan-wide learning persistence for multi-turn attacks, recording which tactics succeeded or failed across test cases.
|
Package memory provides scan-wide learning persistence for multi-turn attacks, recording which tactics succeeded or failed across test cases. |
|
Package parse provides JSON extraction and parsing for multi-turn attack conversations, including attacker output parsing and judge response parsing.
|
Package parse provides JSON extraction and parsing for multi-turn attack conversations, including attacker output parsing and judge response parsing. |
|
Package refusal provides pattern-based detection of refusal and low-quality responses in multi-turn attack conversations.
|
Package refusal provides pattern-based detection of refusal and low-quality responses in multi-turn attack conversations. |
|
strategies
|
|
|
crescendo
Package crescendo implements the Crescendo multi-turn attack strategy.
|
Package crescendo implements the Crescendo multi-turn attack strategy. |
|
goat
Package goat implements the GOAT (Generative Offensive Agent Tester) multi-turn attack strategy.
|
Package goat implements the GOAT (Generative Offensive Agent Tester) multi-turn attack strategy. |
|
hydra
Package hydra implements the Hydra single-path multi-turn attack strategy with turn-level backtracking on refusal.
|
Package hydra implements the Hydra single-path multi-turn attack strategy with turn-level backtracking on refusal. |
|
mischievous
Package mischievous implements a subtle multi-turn attack that simulates an innocent, curious user who gradually probes AI agent boundaries through natural-sounding conversation.
|
Package mischievous implements a subtle multi-turn attack that simulates an innocent, curious user who gradually probes AI agent boundaries through natural-sounding conversation. |