Quality-diversity evolutionary red-teaming for LLMs and agents - single-turn, multi-turn, and agentic/MCP attacks
Project description
rotalabs-redqueen
Quality-diversity evolutionary red-teaming for LLMs and agents, from Rotalabs.
Rather than hand-crafting jailbreaks, rotalabs-redqueen evolves diverse, effective attack strategies and maps the vulnerability space with MAP-Elites. It operates at the semantic level and spans the full 2026 attack surface:
- Single-turn prompt attacks (strategies, encodings, personas)
- Multi-turn Crescendo-style escalation
- Agentic / tool-use / MCP multi-step exploit plans
Seeded runs are bit-reproducible (and cross-language portable), and a campaign can be projected into an audit-ready compliance report (OWASP, MITRE ATLAS, EU AI Act Art. 55, NIST AI RMF).
2.0 is a breaking release. See
CHANGELOG.mdfor migration from 1.x.
Installation
pip install rotalabs-redqueen # core + mock target
pip install rotalabs-redqueen[openai] # + OpenAI
pip install rotalabs-redqueen[anthropic]
pip install rotalabs-redqueen[llm] # all providers
pip install rotalabs-redqueen[dev] # tests/lint
Quick start
import asyncio
from rotalabs_redqueen import (
LLMAttackGenome, JailbreakFitness, MockTarget, HeuristicJudge, evolve,
)
async def main():
target = MockTarget() # swap for OpenAITarget / AnthropicTarget / GeminiTarget / OllamaTarget
fitness = JailbreakFitness(target, HeuristicJudge())
result = await evolve(
genome_class=LLMAttackGenome,
fitness=fitness,
generations=50,
population_size=20,
seed=1234, # same seed -> same result, every time
progress=False,
)
if result.best:
print("fitness:", result.best.fitness.value)
print("prompt:", result.best.genome.to_prompt())
asyncio.run(main())
Multi-turn and agentic attacks
The genome's phenotype is a Stimulus — a single prompt, a conversation, or an agentic action
plan — so the same engine drives every surface. Just swap the genome class:
from rotalabs_redqueen import MultiTurnGenome, AgenticGenome, JailbreakFitness, MockTarget, evolve
# Crescendo-style multi-turn escalation
mt = await evolve(genome_class=MultiTurnGenome,
fitness=JailbreakFitness(MockTarget()),
generations=50, population_size=20, seed=1, progress=False)
# Multi-step tool-use / MCP exploit plans
ag = await evolve(genome_class=AgenticGenome,
fitness=JailbreakFitness(MockTarget()),
generations=50, population_size=20, seed=1, progress=False)
To drive a real MCP server instead of the deterministic MockTarget, use MCPTarget — it
performs the MCP handshake and executes each agentic step as a tools/call, surfacing tool output
for judging:
from rotalabs_redqueen.llm import MCPTarget
target = MCPTarget(command=["npx", "-y", "@modelcontextprotocol/server-everything"])
# agentic action-plan steps become MCP tool calls; the tool output is what the judge scores
Quality-diversity with MAP-Elites
from rotalabs_redqueen import (
LLMAttackGenome, JailbreakFitness, MockTarget,
MapElitesArchive, BehaviorDimension, AttackStrategy, Encoding, evolve,
)
archive = MapElitesArchive(dimensions=[
BehaviorDimension("strategy", 0.0, 1.0, len(AttackStrategy)),
BehaviorDimension("encoding", 0.0, 1.0, len(Encoding)),
BehaviorDimension("has_persona", 0.0, 1.0, 2),
])
result = await evolve(genome_class=LLMAttackGenome,
fitness=JailbreakFitness(MockTarget()),
generations=100, archive=archive, seed=1, progress=False)
cov = result.archive.coverage()
print(f"coverage: {cov.coverage_percent:.1f}% ({cov.filled_cells} diverse attacks)")
Compliance report
Project the archive over the attack taxonomy into standards-aligned evidence:
from rotalabs_redqueen import ReportExporter
exporter = ReportExporter()
report = exporter.export(result.archive.get_all(),
campaign_id="run-1",
coverage=result.archive.coverage())
print(exporter.render(report, "markdown").decode()) # or "json"
The report groups successful attacks by harm category and crosswalks them to OWASP LLM/Agentic Top-10, MITRE ATLAS, EU AI Act Article 55, and NIST AI RMF GOVERN 1.7.
Co-evolution (attacker vs defender)
Evolve an attacker population against a defender population — defenders evolve guardrails that reduce attack success, attackers adapt to bypass them:
from rotalabs_redqueen import (
coevolve, LLMAttackGenome, SystemPromptDefense,
JailbreakFitness, DefenderBlockFitness, MockTarget, HeuristicJudge,
)
base, judge = MockTarget(), HeuristicJudge()
result = await coevolve(
attacker_class=LLMAttackGenome,
defender_class=SystemPromptDefense,
attacker_fitness_vs=lambda d: JailbreakFitness(d.as_defense(base), judge),
defender_fitness_vs=lambda a: DefenderBlockFitness(a, base, judge),
generations=20, population_size=24, seed=1,
)
print(result.best_defender.to_dict(), result.attacker_fitness, result.defender_fitness)
Persistence and continuous red-teaming
Archives serialize, so attacks accumulate across runs (e.g. a CI gate that gets stronger over time):
from rotalabs_redqueen import MapElitesArchive, LLMAttackGenome, Rng
result.archive.save("file://archive.json")
prior = MapElitesArchive.load("file://archive.json", LLMAttackGenome)
warm_start = prior.seed(10, Rng(0)) # sample elite genomes to seed the next run
Command line
rotalabs-redqueen run --target mock:random --generations 20 --seed 1
rotalabs-redqueen run --target openai:gpt-4 --use-archive --output results.json
rotalabs-redqueen run --target mock:random --llm-judge anthropic:claude-sonnet-4-20250514
rotalabs-redqueen info --strategies | --encodings | --targets
Architecture
Core framework (generic, reusable for any QD problem): Genome, Fitness, Population,
Selection (tournament / novelty / lexicase), MapElitesArchive, Evolution, and the
canonical Rng.
LLM domain: LLMAttackGenome / MultiTurnGenome / AgenticGenome; targets — OpenAI,
Anthropic, Gemini, Ollama, Mock, and MCPTarget (drives a real MCP server over stdio);
Judge (heuristic, LLM); JailbreakFitness / MultiTargetFitness; co-evolution (coevolve,
SystemPromptDefense, DefenderBlockFitness).
| Surface | Genome | Stimulus kind |
|---|---|---|
| Single-turn | LLMAttackGenome |
single_turn |
| Multi-turn | MultiTurnGenome |
multi_turn |
| Agentic / MCP | AgenticGenome |
agentic |
Extending
Custom genome
from rotalabs_redqueen import Genome, BehaviorDescriptor, Stimulus
class MyGenome(Genome["MyGenome"]):
@classmethod
def random(cls, rng): ...
def mutate(self, rng): ...
def crossover(self, other, rng): ...
def to_stimulus(self) -> Stimulus:
return Stimulus.single_turn(prompt="...")
def behavior(self) -> BehaviorDescriptor: ...
def distance(self, other) -> float: ...
def to_dict(self) -> dict: ...
@classmethod
def from_dict(cls, data) -> "MyGenome": ...
rng is the canonical Rng — use rng.random(), rng.integers(n), rng.choice(n, size, replace=False),
rng.shuffle(list) so runs stay reproducible.
Custom target
from rotalabs_redqueen import LLMTarget, Message, TargetResponse
class MyTarget(LLMTarget):
@property
def name(self) -> str:
return "my-target"
async def _complete(self, messages: list[Message]) -> TargetResponse:
text = await my_llm_api([{"role": m.role, "content": m.content} for m in messages])
return TargetResponse(content=text, model="my-model")
interact() (single-turn + scripted multi-turn rollout) is provided by the base class.
Custom judge
from rotalabs_redqueen import Judge, JudgeResult
class MyJudge(Judge):
async def judge(self, stimulus, transcript) -> JudgeResult:
score = my_score(transcript.assistant_text)
return JudgeResult(success=score >= 0.5, score=score)
Reproducibility & conformance
Seeded campaigns are deterministic and cross-language portable: the canonical PRNG (xoshiro256++ + SplitMix64) is cross-validated against an independent implementation, and an L1–L5 conformance suite gates engine, LLM-domain, report, multi-turn/agentic, and co-evolution behavior against golden fixtures, all reproduced byte-for-byte by the TypeScript package.
pytest # full suite incl. conformance
python -m rotalabs_redqueen._gen_conformance # regenerate golden fixtures (intentional changes only)
Use cases
Red-teaming, guardrail/defense testing, robustness benchmarking, and documented adversarial-testing evidence for compliance.
Responsible use
For defensive security research — testing systems you own or are authorized to test. Do not use it to attack systems without authorization or to circumvent the safety of production systems.
Links
- Website: https://rotalabs.ai
- GitHub: https://github.com/rotalabs/rotalabs-redqueen
- Documentation: https://rotalabs.github.io/rotalabs-redqueen/
- Contact: research@rotalabs.ai
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rotalabs_redqueen-2.0.0.tar.gz.
File metadata
- Download URL: rotalabs_redqueen-2.0.0.tar.gz
- Upload date:
- Size: 206.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a041891a5d07f1a9f518de71b21352869c094b323af867b4114179c9715514b5
|
|
| MD5 |
1f51d22d9fbacd5840865a5133b85214
|
|
| BLAKE2b-256 |
2b218200d735a4ee01cdda080cb8a1e1efabe79a7eb1b96a3229e12884dbc9b1
|
File details
Details for the file rotalabs_redqueen-2.0.0-py3-none-any.whl.
File metadata
- Download URL: rotalabs_redqueen-2.0.0-py3-none-any.whl
- Upload date:
- Size: 66.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
49b70f638cd24dada337928a72d330d04e881c3929995f492f2919a9dc78cb61
|
|
| MD5 |
bb9983c0c0fd0f289d330181b3426e85
|
|
| BLAKE2b-256 |
6fe7551e1356218df3977fad3a82d22edd306df9fdbb5c8707867353247adb65
|