multiturn

package
v0.0.22 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 2, 2026 License: Apache-2.0 Imports: 19 Imported by: 0

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

View Source
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.

View Source
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.

View Source
const DetectorJudge = "judge.Judge"

DetectorJudge is the detector name used for multi-turn judge-based scoring.

Variables

This section is empty.

Functions

func ClassifyRefusal

func ClassifyRefusal(wasRefused bool, judgeScore float64) string

ClassifyRefusal determines the refusal type from the binary refusal flag and judge score.

func ParseRefusalResponse

func ParseRefusalResponse(output string) bool

ParseRefusalResponse extracts the refusal verdict from judge output.

func RefusalJudgePrompt

func RefusalJudgePrompt(question, response string) string

RefusalJudgePrompt builds the evaluation prompt for refusal detection.

func RefusalJudgeSystemPrompt

func RefusalJudgeSystemPrompt() string

RefusalJudgeSystemPrompt returns the system prompt for refusal detection.

func ResponseCharBudget

func ResponseCharBudget(model string) int

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

func SecondaryJudgeSystemPrompt(goal string) string

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

func SuccessJudgeSystemPrompt(goal string) string

SuccessJudgeSystemPrompt returns the system prompt for success evaluation.

func TruncateStr

func TruncateStr(s string, maxLen int) string

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

func (*BaseMultiTurnProbe) Probe

Probe executes the multi-turn attack against the target generator.

type Config

type Config = config.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

func ConfigFromMap(m registry.Config, defaults Config) Config

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).

func Defaults

func Defaults() Config

Defaults returns a Config with sensible defaults for multi-turn attacks.

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).

func NewScanMemory

func NewScanMemory() *ScanMemory

NewScanMemory creates a new empty ScanMemory.

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:

  1. BeforeTurn hooks
  2. Generate attacker question
  3. Query target
  4. AfterQuery hooks (output scrubbing, unblocking)
  5. BeforeJudge hooks (fast refusal detection)
  6. Judge evaluation (skipped if BeforeJudge set ShouldSkipTurn)
  7. AfterJudge hooks (penalized phrases, backtrack decision)
  8. Record turn + AfterTurn hooks
  9. Feedback to attacker OR backtrack
  10. Success check → OnSuccess hooks

func (*UnifiedEngine) SetTurnCallback

func (e *UnifiedEngine) SetTurnCallback(cb func(TurnRecord))

SetTurnCallback sets an optional callback fired after each completed turn.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL