shanepeckham/MutaGenAI

GitHub: shanepeckham/MutaGenAI

一款利用进化搜索在无需微调或标注数据的情况下,自动优化大语言模型系统提示词的 AI/ML 工具。

Stars: 0 | Forks: 0

# MutagenAI Evolve LLM system prompts using evolutionary search — no fine-tuning, no GPUs, no labelled data required. ## Table of contents - [Getting started — the wizard](#getting-started--the-wizard) - [What the wizard asks](#what-the-wizard-asks) - [Three paths through the wizard](#three-paths-through-the-wizard) - [How to evaluate when you have no ground truth](#how-to-evaluate-when-you-have-no-ground-truth) - [Example walkthrough](#example-walkthrough) - [Wizard code generation improvements](#wizard-code-generation-improvements) - [How prompt evolution works](#how-prompt-evolution-works) - [The approach](#the-approach) - [The evolutionary loop](#the-evolutionary-loop) - [Core modules](#core-modules) - [Ground-truth prompt evolution](#ground-truth-prompt-evolution) - [Quick start — PromptEvolver](#quick-start--promptevolver) - [Baseline comparison](#baseline-comparison) - [Ollama vs Azure OpenAI](#ollama-vs-azure-openai) - [Agentic workload scenarios](#agentic-workload-scenarios) - [Six agentic benchmarks](#six-agentic-benchmarks) - [BFCL V4 — single-turn function calling](#bfcl-v4--single-turn-function-calling) - [τ-Bench — conversational agents](#τ-bench--conversational-agents) - [xLAM 60 k — broad function calling](#xlam-60-k--broad-function-calling) - [ToolBench — multi-tool API orchestration](#toolbench--multi-tool-api-orchestration) - [API-Bank — multi-level tool-use evaluation](#api-bank--multi-level-tool-use-evaluation) - [Browser Agent Tasks — failure recovery](#browser-agent-tasks--failure-recovery) - [Consolidated results](#consolidated-results) - [Key takeaways from benchmarks](#key-takeaways-from-benchmarks) - [No-eval prompt evolution — 7 strategies](#no-eval-prompt-evolution--7-strategies) - [The seven strategies](#the-seven-strategies) - [Quick start — NoEvalPromptEvolver](#quick-start--noevalpromptevolver) - [Which strategy should I pick?](#which-strategy-should-i-pick) - [xLAM no-eval worked example](#xlam-no-eval-worked-example) - [The problem](#the-problem) - [What this recipe does](#what-this-recipe-does) - [Configuration](#configuration) - [Results](#results) - [What the results mean](#what-the-results-mean) - [How to apply this to your own agent](#how-to-apply-this-to-your-own-agent) - [API-Bank no-eval vs ground-truth comparison](#api-bank-no-eval-vs-ground-truth-comparison) - [Seed templates — external prompt configuration](#seed-templates--external-prompt-configuration) - [Agent routing — static vs evolved prompt (GPT-4.1)](#agent-routing--static-vs-evolved-prompt-gpt-41) - [Entity classification — static vs evolved prompt](#entity-classification--static-vs-evolved-prompt) - [Dashboard and visualisation](#dashboard-and-visualisation) - [Red teaming — hardening open-source SLMs](#red-teaming--hardening-open-source-slms) - [Model migration — swap models without losing accuracy](#model-migration--swap-models-without-losing-accuracy) - [Cookbook recipes](#cookbook-recipes) - [Installation](#installation) ## Getting started — the wizard The wizard walks you through nine steps and generates a self-contained Python script tailored to your project: mutagenai init # interactive walkthrough mutagenai init --output my_agent.py # custom output file ### What the wizard asks | Step | Question | Why it matters | |------|----------|----------------| | 1 | **Task description** | Drives mutation generation and rubrics | | 2 | **Ground truth?** | yes / partial / no — determines scoring path | | 3 | **Test inputs** | Unlabelled queries your agent handles | | 4 | **Scoring strategy** | Picks from 7 strategies (see below) | | 5 | **Domain mutations** | Custom or auto-generated rewrite rules | | 6 | **Human evaluation** | always / final pick / fully automated | | 7 | **Seed templates** | Your existing prompts or auto-generated | | 8 | **LLM backend** | Ollama (local) / OpenAI / Azure OpenAI | | 9 | **Configuration** | Standard (fast) / Deep (thorough) / Custom | ### Three paths through the wizard **Path A — Full ground truth.** You have labelled input/output pairs. The wizard generates a script that scores candidates against your labels and evolves the prompt for maximum accuracy. **Path B — No labels.** You have only unlabelled inputs. The wizard lets you pick from seven label-free strategies: | Strategy | How it works | |---|---| | LLM-as-Judge | A second LLM call rates each output 0–10 | | Self-Consistency | Same input, multiple runs — agreement = quality | | Proxy Metrics | Structural checks: valid JSON, format, length | | Tool-Use Success | Actually execute tool calls, score by status | | Preference Pairs | Compare against hand-crafted good/bad examples | | Human-as-Judge | You rate outputs interactively during evolution | | Composite | Weighted mix of the above (recommended) | **Path C — Human-in-the-loop.** The wizard supports three human-evaluation modes: - **`always`** — You rate candidates every generation (gold standard). - **`final`** — Evolution runs fully automated, then presents the top-K prompts for your final pick. - **`no`** — Fully automated, no human involvement. ### How to evaluate when you have no ground truth Most real-world agents have no labelled dataset. You have a system prompt, some test queries, and an intuition for what "good" looks like — but no gold-standard answers to score against. The wizard handles this by replacing the ground-truth fitness function with **proxy signals** that give the evolutionary loop enough information to rank candidates. Evolution does not need perfect absolute scores. It only needs to answer one question reliably: **"Is prompt A better than prompt B?"** The seven no-eval strategies provide that relative ranking without labels. **Which strategy should you pick?** Use this decision tree: Do you have labelled eval data? ├─ YES → Use the standard PromptEvolver (ground-truth scoring) └─ NO ├─ Can you describe "good" in words? │ └─ YES → LLM-as-Judge (write a rubric) ├─ Does the agent call real APIs/tools? │ └─ YES → Tool-Use Success (HTTP status codes) ├─ Is there one correct answer per input? │ └─ YES → Self-Consistency (agreement across runs) ├─ Is output format critical (JSON, SQL, brackets)? │ └─ YES → Proxy Metrics (structural checks) ├─ Do you have 5-10 good/bad output examples? │ └─ YES → Preference Scoring (few-shot pairs) ├─ Is quality subjective or safety-critical? │ └─ YES → Human Tournament (you pick winners) └─ Not sure? → Composite (recommended — blends multiple signals) **Recommended starting point: Composite.** The wizard's default Composite scorer blends LLM-as-Judge (35 %), Self-Consistency (30 %), and Proxy Metrics (35 %). Weights are normalised to sum to 1.0. This covers three independent signal types — semantic quality, output stability, and structural correctness — so it is robust even when individual signals are noisy. Proxy checks are now **problem-type-aware**: tool routing gets JSON and agent-name checks; classification gets single-label and format checks. The [xLAM worked example](#xlam-no-eval-worked-example) below proves that this approach preserves 95 %+ ground-truth accuracy while having zero access to labels. **How to know if evolution is working without labels:** 1. **No-eval fitness should increase** across generations. If the composite score plateaus at generation 1, your rubric or proxy checks may be too coarse. 2. **Spot-check 3–5 outputs** from the best prompt. Do they look reasonable? This is the human sanity check. 3. **Compare to the default prompt.** Run both prompts on your test inputs and eyeball the difference. If the evolved prompt produces more consistent, better-formatted outputs, evolution is working. 4. **Use `human_eval_mode: final`** if you want a formal step. Evolution runs fully automated, then presents the top-K prompts for your final selection. ### Example walkthrough 🧬 MutagenAI — Prompt Evolution Wizard Step 1 of 9 — Task Description Task description: You are an API-calling assistant that maps queries to tool calls in the format [ToolName(param=value)]. Step 2 of 9 — Ground Truth Ground truth availability: no Step 4 of 9 — Scoring Strategy Strategies: 7 (Composite) Step 6 of 9 — Human Evaluation Human evaluation mode: final ✓ Generated: evolve_prompt.py Run it with: uv run python evolve_prompt.py The generated script includes all imports, seed templates, scoring setup, evolution loop, results saving, and (if selected) a human final-selection step. ### Wizard code generation improvements The wizard generates smarter, more effective evolution scripts out of the box. Eight improvements ensure that generated code follows best practices discovered through benchmarking: | Improvement | What changed | |---|---| | **Seed template diversification** | A single user seed is auto-expanded to 6 structural variants (CoT, output-format-first, minimalist, contrastive, persona, intent-matching) so every island starts with different material. | | **Problem-type proxy checks** | Generic proxy checks (`has_function_name`, `bracket_format`) replaced with task-aware checks. Tool routing gets `valid_json`, `has_sequence_or_array`, `contains_agent_name`, `no_verbose_explanation`, `at_least_one_selection`. Classification gets `valid_json`, `single_label`, `not_empty`, `no_verbose_explanation`. Generation gets `valid_json`, `is_json_object`, `has_fields`, `no_markdown_fences`, `not_empty`. | | **Task-specific LLM Judge rubrics** | The rubric is no longer a truncated task description. For tool routing: checks agent relevance, logical order, precision, JSON validity. For classification: checks predicted class, format, reasoning. For generation: checks valid JSON, required fields, grounded values, non-empty strings, non-empty arrays. | | **Adaptive mutations enabled** | Generated scripts now set `adaptive_mutations=True` and `llm_mutation_rate=0.3` in `NoEvalConfig`, enabling the evolver to learn which mutations are effective. | | **`refine_after_splice` enabled** | Crossover offspring are refined by the LLM to improve coherence, reducing the chance of Frankenstein prompts. | | **Domain mutations wired** | `DOMAIN_MUTATIONS` are now passed as `custom_mutations` to `NoEvalPromptEvolver`, so user-defined and auto-generated mutations actually drive evolution. | | **Normalized CompositeScorer weights** | Weights in the composite scorer now sum to 1.0 (e.g. judge 0.35, consistency 0.30, proxy 0.35) for clarity. | | **Scaled-up config presets** | `standard` preset: 5 generations, 6 population, 2 islands (was 3/4/2). `deep` preset: 10 generations, 8 population, 3 islands (was 5/6/3). | ## How prompt evolution works ### The approach Most teams improve LLM performance by training bigger models or fine-tuning on task-specific data. Both require GPUs and large datasets. Prompt evolution takes a different path: **keep the model frozen and evolve the system prompt instead.** The idea is simple. When you ask an LLM to call an API, to plan a multi-step tool chain, or to follow a customer-service policy, the *instructions* you put in the system prompt matter far more than the model's parameter count. A vague "you are a helpful assistant" prompt scores 5–19 % on the benchmarks below. A well-structured prompt — with the right role description, output format, constraint ordering, and chain-of-thought hints — can reach 90–100 %. The question is: how do you find that perfect prompt automatically? ### The evolutionary loop MutagenAI treats prompt engineering as an **optimisation problem** and solves it with evolutionary search: 1. **Seed population.** Start with 4 prompt templates ranging from a bare-bones one-liner to a detailed step-by-step planner. Each template has a placeholder where tool definitions get injected at test time. 2. **Evaluate.** Every candidate prompt is sent to the LLM along with the test cases. The model's output is scored — either against ground-truth labels or via no-eval strategies (LLM-as-Judge, Self-Consistency, Proxy Metrics, etc.). 3. **Select and breed.** The best-scoring prompts survive. The worst are replaced by *mutated* copies of the winners — small edits like adding a constraint, injecting a chain-of-thought hint, or reordering instructions. Pairs of good prompts are also *crossed over*. Meanwhile, CMA-ES tunes the numeric knobs (temperature, top-p) — see below. 4. **Island migration.** The population is split across 2 islands that evolve independently. Every 3 generations the best prompt from each island migrates to its neighbour, injecting diversity. 5. **Converge.** After 3–5 generations the prompts converge on a structure that consistently scores highest. ┌───────────────────────────────┐ │ Population of prompt variants │ │ (4 candidates × 2 islands) │ └───────────┬───────────────────┘ │ ┌───────────▼───────────────────┐ │ For each candidate prompt: │ │ 1. Run prompt on test inputs │ │ 2. Score outputs via SCORER │ ← your chosen strategy │ 3. Assign fitness score │ └───────────┬───────────────────┘ │ ┌───────────▼───────────────────┐ │ Tournament select → crossover │ │ → mutate → next generation │ └───────────┬───────────────────┘ │ ┌───────────▼───────────────────┐ │ Migrate best across islands │ │ every 3 generations │ └───────────────────────────────┘ ### What is CMA-ES? (the short version) Imagine you're playing "hot and cold" to find hidden treasure in a huge field — blindfolded. You throw a bunch of darts. Some land closer to the treasure, some farther away. You keep the best ones and throw your next batch *near where the good ones landed*. At first your darts land in a circle around your best guess. But say the treasure is inside a long, narrow valley. A circle wastes most darts on the hillsides. CMA-ES notices "the good darts keep landing in a line going *this* way" and stretches the circle into an oval pointing down the valley. That oval is the **covariance matrix** — a description of the shape and direction of where darts are thrown. CMA-ES also adjusts how *far* it throws. If several rounds in a row the good darts keep moving the same direction, it takes bigger steps. If they zigzag, it shrinks — it must be close. In MutagenAI, CMA-ES tunes the *continuous knobs* — `temperature` and `top_p` — while the evolutionary algorithm handles the *words* in the prompt. CMA-ES needs no formula for "how good is temperature = 0.7"; it just tries values, keeps the best, and learns which combinations work together. ### Advanced evolution features Three additional features give the evolutionary loop finer control over parent selection, evaluation cost, and mutation targeting. #### Score-proportional selection By default MutaGenAI uses tournament selection to pick parents. Set `selection_method=SelectionMethod.SCORE_PROPORTIONAL` on `PromptEvolverConfig` to switch to a sigmoid-weighted scheme that favours high-scoring candidates while penalising over-selected parents. This improves diversity by ensuring every promising candidate gets a chance to breed, not just the tournament winner. from MutaGenAI import PromptEvolverConfig, SelectionMethod config = PromptEvolverConfig( selection_method=SelectionMethod.SCORE_PROPORTIONAL, ) #### Progressive evaluation (shallow → deep) When evaluation datasets are large, running every candidate through the full set is expensive. Progressive evaluation runs a cheap shallow pass first (`eval_sample_size` samples). Only candidates that meet a promotion threshold are re-evaluated on a larger deep sample for a more reliable score. config = PromptEvolverConfig( eval_sample_size=10, # shallow pass size eval_promotion_threshold=75.0, # minimum score (0–100) to promote eval_deep_sample_size=50, # deep pass size ) Both fields default to `None`, which disables progressive evaluation entirely — every candidate is scored once on `eval_sample_size` samples. #### Structured failure buckets Adaptive mutations already target the worst-performing categories. Failure buckets add a second axis: they classify *how* each sample failed (wrong tool, wrong parameters, unparseable output, no output, partial match) and inject mutation hints that specifically address that failure mode. The buckets are problem-type-aware. Tool-routing, classification, and generation tasks each have their own mutation dictionaries. Set `problem_type` on the config to match your workload: from MutaGenAI import PromptEvolverConfig, ProblemType config = PromptEvolverConfig( problem_type=ProblemType.GENERATION, # or TOOL_ROUTING, CLASSIFICATION ) Failure bucket mutations are generated automatically each generation and blended into the mutation pool alongside adaptive and built-in mutations. ## Core modules | Module | Purpose | |---|---| | [`mutagenai/prompt_evolver.py`](mutagenai/prompt_evolver.py) | `PromptEvolver` — ground-truth prompt evolution with island-model EA + CMA-ES continuous tuning | | [`mutagenai/strategies.py`](mutagenai/strategies.py) | `NoEvalPromptEvolver` + 7 scoring strategies for label-free evolution | | [`mutagenai/wizard.py`](mutagenai/wizard.py) | `mutagenai init` wizard — interactive questionnaire that generates a ready-to-run script | | [`mutagenai/seed_loader.py`](mutagenai/seed_loader.py) | Load seed templates from external JSON files | | [`mutagenai/dashboard.py`](mutagenai/dashboard.py) | Plotting functions for benchmark visualisation (BFCL, xLAM, τ-bench, ToolBench, API-Bank, Browser Agent) | | [`docs/algorithm_animation.html`](docs/algorithm_animation.html) | Interactive step-through animation showing how the evolutionary loop works (open in a browser) | ## Ground-truth prompt evolution When you have labelled input/output pairs, `PromptEvolver` evolves the system prompt and sampling parameters (temperature, top-p) for maximum accuracy. ### Quick start — PromptEvolver from mutagenai import ( PromptEvolver, PromptEvolverConfig, Tool, EvalSample, LLMBackend, ) tools = [ Tool("get_weather", "Get current weather", {"location": "string"}), Tool("send_email", "Send an email", {"to": "string", "subject": "string"}), ] dataset = [ EvalSample("Weather in London?", "get_weather", {"location": "London"}), EvalSample("Email Bob about the project", "send_email", {"to": "Bob"}), ] evolver = PromptEvolver( tools=tools, eval_dataset=dataset, config=PromptEvolverConfig(iterations=5, backend=LLMBackend.OLLAMA), ) result = evolver.run() print(result.summary()) ### Baseline comparison Ollama llama3.2, 6 tools, 24 samples: | Prompt | Accuracy | Delta vs Naive | |---|---:|---:| | Naive (tool list only) | 42.5 % | — | | Minimal JSON instruction | 89.6 % | +47.1 % | | Verbose (kitchen sink) | 95.0 % | +52.5 % | | High temperature (creative) | 78.8 % | +36.3 % | | Zero temperature (greedy) | 89.6 % | +47.1 % | | **EVOLVED (MutagenAI)** | **96.7 %** | **+54.2 %** | ### Ollama vs Azure OpenAI Same baselines on both backends: | Prompt | Ollama | Azure OpenAI | Delta | |---|---:|---:|---:| | Naive (tool list only) | 44.2 % | 0.0 % | -44.2 % | | Minimal JSON instruction | 90.4 % | 92.1 % | +1.7 % | | Verbose (kitchen sink) | 89.2 % | 98.8 % | +9.6 % | | **EVOLVED (MutagenAI)** | **100.0 %** | **100.0 %** | **+0.0 %** | Both backends reached **100 % accuracy** after evolution. ### Agentic workload scenarios Three realistic agentic workloads with 8–9 tools and 20 evaluation samples each: | Scenario | Tools | Ollama Evolved | Azure Evolved | Best Baseline | |---|---:|---:|---:|---:| | Customer-Support Triage | 9 | 93.8 % | 95.0 % | 79.5 % | | Code-Assistant Agent | 9 | 100.0 % | 100.0 % | 88.0 % | | Data-Pipeline Orchestrator | 8 | 96.2 % | 97.5 % | 86.0 % | ## Six agentic benchmarks MutagenAI has been evaluated across six real-world agentic benchmarks covering function calling, conversational agents, multi-tool orchestration, and failure recovery. ### BFCL V4 — single-turn function calling [Berkeley Function Calling Leaderboard V4](https://gorilla.cs.berkeley.edu/leaderboard.html) — the gold-standard benchmark for evaluating function-calling accuracy. **Ollama (llama3.2)** | Category | Default | Evolved | Delta | |---|---:|---:|---:| | simple_python | 98.4 % | **100.0 %** | +1.5 % | | multiple | 94.2 % | 95.5 % | +0.0 % | | parallel | 96.0 % | 96.9 % | +0.0 % | | live_simple | 77.6 % | **80.5 %** | +0.7 % | **Azure OpenAI (GPT-4.1)** | Category | Default | Evolved | Delta | |---|---:|---:|---:| | simple_python | 98.4 % | 100.0 % | +0.0 % | | parallel | 96.0 % | **97.5 %** | +0.6 % | | live_simple | 77.6 % | **77.5 %** | +5.0 % | ### τ-Bench — conversational agents [τ-bench](https://github.com/sierra-research/tau2-bench) (Sierra Research) — tests tool-using conversational agents on customer-service scenarios. Top models reach only ~46 % pass@1 on airline. **Ollama (llama3.2)** | Domain | Algorithm | Default | Evolved | Delta | |---|---|---:|---:|---:| | airline | standard | 30.0 % | **36.9 %** | +5.7 % | | airline | deep | 30.0 % | **46.6 %** | +3.1 % | | retail | standard | 52.0 % | **54.6 %** | +0.6 % | **Azure OpenAI (GPT-4.1)** | Domain | Algorithm | Default | Evolved | Delta | |---|---|---:|---:|---:| | airline | standard | 28.0 % | **39.5 %** | +2.1 % | | airline | deep | 28.0 % | **46.5 %** | +8.3 % | | retail | standard | 50.1 % | **51.6 %** | +0.0 % | ### xLAM 60 k — broad function calling [Salesforce xLAM](https://huggingface.co/datasets/Salesforce/xlam-function-calling-60k) — 60 000 examples across 3 673 APIs and 21 categories. **Ollama (llama3.2)** | Category | Default | Evolved | Delta | |---|---:|---:|---:| | finance | 50.7 % | **98.0 %** | +1.9 % | | social | 40.3 % | **94.0 %** | +1.2 % | | data | 46.3 % | **100.0 %** | +3.5 % | | entertainment | 58.1 % | **96.8 %** | +3.9 % | **Azure OpenAI (GPT-4.1)** | Category | Default | Evolved | Delta | |---|---:|---:|---:| | social | 26.7 % | **100.0 %** | +2.5 % | | data | 28.3 % | **100.0 %** | +0.0 % | | finance (deep) | 26.7 % | **100.0 %** | +0.0 % | ### ToolBench — multi-tool API orchestration [ToolBench](https://github.com/OpenBMB/ToolBench) — 16 464 REST APIs, three difficulty tiers. Published baseline: ToolLLaMA 66.7 %, GPT-4 71.1 %. **Ollama (llama3.2)** | Split | Algorithm | Default | Evolved | Delta | |---|---|---:|---:|---:| | g1_instruction | standard | 16.0 % | **89.5 %** | +41.0 % | | g2_category | standard | 4.7 % | **65.0 %** | +21.5 % | | g3_instruction | standard | 19.0 % | **39.7 %** | +16.3 % | | g3_instruction | deep | 19.0 % | **74.3 %** | +25.8 % | ### API-Bank — multi-level tool-use evaluation [API-Bank](https://github.com/AlibabaResearch/DAMO-ConvAI/tree/main/api-bank) — 73 runnable API tools, 314 dialogues. Published baseline: GPT-4 83.8 % calling, 41.2 % retrieval. **Ollama (llama3.2)** | Level | Algorithm | Default | Evolved | Delta | |---|---|---:|---:|---:| | level_1 | standard | 52.3 % | **100.0 %** | +4.0 % | | level_2 | standard | 57.1 % | **88.9 %** | +3.7 % | | level_2 | deep | 57.1 % | **82.6 %** | +7.7 % | ### Browser Agent Tasks — failure recovery [Browser Agent Tasks](https://huggingface.co/datasets/DataCreatorAI/tool-calling-browser-agent-tasks) — 1 062 multi-turn conversations with failure recovery scenarios. **Ollama (llama3.2)** | Category | Algorithm | Default | Evolved | Delta | |---|---|---:|---:|---:| | normal | standard | 25.9 % | **29.0 %** | +2.7 % | | failure_recovery | standard | 24.3 % | **28.0 %** | +4.0 % | | multi_tool | standard | 22.3 % | **30.9 %** | +0.2 % | | failure_recovery | deep | 24.3 % | **35.2 %** | +8.5 % | ### Consolidated results | Benchmark | Best Evolved (Ollama 3 B) | Default | Absolute gain | |---|---:|---:|---:| | BFCL simple_python | **100.0 %** | 98.4 % | +1.5 % | | xLAM data | **100.0 %** | 46.3 % | +53.7 % | | API-Bank level_1 | **100.0 %** | 52.3 % | +47.7 % | | ToolBench G1 | **89.5 %** | 16.0 % | +73.5 % | | τ-bench airline (deep) | **46.6 %** | 30.0 % | +16.6 % | | Browser failure_recovery (deep) | **35.2 %** | 24.3 % | +10.9 % | ### Key takeaways from benchmarks 1. **Prompt structure matters more than model size.** A default "you are a helpful assistant" prompt scores 5–19 %. A well-structured prompt jumps to 40–100 %. Evolution adds a further 1–41 % on top. 2. **A 3 B local model can beat GPT-4.** On xLAM and API-Bank, Ollama llama3.2 with an evolved prompt matches or exceeds GPT-4's published scores — at zero API cost. 3. **The hardest benchmarks benefit most.** ToolBench G1 saw +41 % absolute gain; τ-bench airline saw +8.3 % with GPT-4.1 deep search. 4. **Multi-tool planning responds to prompt engineering.** ToolBench G3 improved from 19 % to 74.3 % under deep evolution. 5. **Evolution finds what humans miss.** Hand-crafted prompts plateau; the evolutionary loop discovers non-obvious structural patterns. 6. **Format compliance is model-specific.** GPT-4.1 scored < 7 % on API-Bank despite being far larger — it wraps calls in markdown that strict parsers reject. Prompt evolution effectiveness depends on the model's willingness to obey exact output constraints. ## Token optimization Agent system prompts can be verbose (800–1 500 tokens). Token optimization applies evolutionary pressure toward shorter prompts without sacrificing accuracy. The feature is **off by default** and activated via five `PromptEvolverConfig` fields. ### How it works Two complementary mechanisms run inside the existing evolutionary loop: 1. **Baseline-relative efficiency bonus** — after computing raw accuracy the engine calculates `efficiency = baseline_tokens / candidate_tokens` (capped at `token_efficiency_cap`), converts it to a 0–100 bonus, and blends it with the raw score using `token_weight`. 2. **Lexicographic tournament tiebreaker** — within the same accuracy band (`token_accuracy_band` percentage points) the tournament selects the candidate with fewer tokens instead of higher raw score. ### Quick start — token-aware evolution from mutagenai import ( PromptEvolver, PromptEvolverConfig, Tool, EvalSample, LLMBackend, count_prompt_tokens, ) baseline_prompt = "You are a helpful assistant..." config = PromptEvolverConfig( iterations=5, backend=LLMBackend.OLLAMA, minimize_tokens=True, # enable token optimization token_weight=0.10, # 10 % of score from efficiency token_efficiency_cap=2.0, # cap efficiency bonus at 2x shorter token_accuracy_band=2.0, # tiebreak within 2 pp accuracy baseline_prompt_tokens=count_prompt_tokens(baseline_prompt), ) evolver = PromptEvolver(tools=tools, eval_dataset=dataset, config=config) result = evolver.run() ### Configuration reference | Field | Type | Default | Description | |---|---|---|---| | `minimize_tokens` | `bool` | `False` | Master switch for token optimization | | `token_weight` | `float` | `0.10` | Blend weight for the efficiency bonus (0–1) | | `token_efficiency_cap` | `float` | `2.0` | Maximum efficiency ratio before capping | | `token_accuracy_band` | `float` | `2.0` | Accuracy band width for the tournament tiebreaker (pp) | | `baseline_prompt_tokens` | `int` | `0` | Token count of the baseline prompt (use `count_prompt_tokens()`) | ### Utility `count_prompt_tokens(text)` counts tokens using tiktoken `cl100k_base` with a `len(text) // 4` fallback when tiktoken is not installed. ## No-eval prompt evolution — 7 strategies The benchmarks above all use ground-truth labels. Real-world agents rarely have these. The **no-eval strategies** module (`mutagenai.strategies`) replaces the ground-truth fitness function with seven alternative signal sources. ### The seven strategies | # | Strategy | Signal source | LLM calls per eval | Best for | |---|---|---|---|---| | 1 | **LLM-as-Judge** | Second LLM scores output against rubric | 1 extra | General quality | | 2 | **Synthetic Eval** | LLM generates input/output pairs from description | 1 generate + 1 per eval | No test data at all | | 3 | **Tool-Use Success** | API return codes (200/400/500) | 0 extra | Agents with real endpoints | | 4 | **Self-Consistency** | Agreement across N runs | N-1 extra | Deterministic tasks | | 5 | **Proxy Metrics** | Structural checks (JSON, format, length) | 0 extra | Format compliance | | 6 | **Preference Scoring** | Good/bad output pairs as references | 1 extra | 5–10 example preferences | | 7 | **Human Tournament** | Human picks best per generation | 0 extra | Highest quality | ### Quick start — NoEvalPromptEvolver from mutagenai import ( NoEvalPromptEvolver, NoEvalConfig, LLMJudge, SelfConsistencyScorer, ProxyMetricsScorer, CompositeScorer, ) # 1. Define the task task = """You are a customer-service agent for an online store. Answer questions about orders, returns, shipping, and accounts. If you need to call a tool, respond with JSON: {"tool": "...", "parameters": {...}}""" # 2. Provide unlabelled test inputs inputs = [ "Where is my order #12345?", "I want to return the shoes I bought last week.", "How do I change my shipping address?", ] # 3. Pick a scoring strategy scorer = CompositeScorer([ (LLMJudge(rubric="Score 0-10 on helpfulness, accuracy, and tone."), 0.5), (ProxyMetricsScorer(ProxyMetricsScorer.common_checks()), 0.3), (SelfConsistencyScorer(num_samples=3), 0.2), ]) # 4. Configure and run config = NoEvalConfig(iterations=5, population_size=4, num_islands=2) evolver = NoEvalPromptEvolver( task_description=task, test_inputs=inputs, scorer=scorer, config=config, ) result = evolver.run() print(result.best_prompt) ### Which strategy should I pick? Do you have labelled eval data? ├─ YES → Use PromptEvolver (ground-truth) └─ NO ├─ Can you describe "good" in words? → LLM-as-Judge ├─ Agent calls real APIs/tools? → Tool-Use Success ├─ One correct answer per input? → Self-Consistency ├─ Output format critical? → Proxy Metrics ├─ Have 5-10 good/bad examples? → Preference Scoring ├─ Quality is subjective? → Human Tournament └─ Not sure? → Composite (recommended) ## xLAM no-eval worked example This is a complete, end-to-end walkthrough of evolving a prompt **when you have no ground truth**. It uses the wizard approach applied to the **Salesforce xLAM function-calling 60k** benchmark — but we pretend we have no labels. We then reveal the hidden labels afterwards to measure how well the no-eval strategies actually performed. ### The problem You have an LLM agent that calls functions. Users send natural-language queries; the agent must output structured function calls in the format `[func_name(param=value, ...)]`. You have 20 representative user queries and the tool schemas, but **no expected outputs** — no labelled dataset, no annotation team, no evaluation harness. How do you systematically improve the system prompt? ### What this recipe does 1. **Loads 150 xLAM cases** (30 per category) from the Hugging Face dataset. 2. **Strips all labels** — the experiment sees only queries and tool schemas. 3. **Selects 20 unlabelled test inputs** (stratified random sample). 4. **Auto-generates seed templates** from a one-line task description. 5. **Auto-generates mutations** — generic prompt rewrites. 6. **Evolves prompts** using four strategies: Composite, LLM-as-Judge, Self-Consistency, and Proxy Metrics. 7. **Reveals ground truth** and evaluates every evolved prompt against the real xLAM labels. ### Configuration The experiment uses the wizard's **standard** preset: | Parameter | Value | What it controls | |---|---|---| | Generations | 5 | Number of evolutionary cycles | | Population | 6 | Prompt variants per island | | Islands | 2 | Parallel sub-populations | | Elite size | 3 | Top candidates that survive unchanged | | Migration | every 3 gens | Best prompts shared between islands | ### Results **Model:** Ollama `llama3.2` (3B parameters, running locally). | Approach | No-Eval Fitness | GT Score | Func Name | Param Acc | Wall Time | |---|---|---|---|---|---| | Default prompt (no evolution) | — | 95.4 % | 100.0 % | 77.4 % | — | | **Composite (wizard default)** | **93.2 %** | **95.5 %** | **100.0 %** | **77.8 %** | **2 582 s** | | LLM-as-Judge | 86.5 % | 94.6 % | 100.0 % | 76.8 % | 2 236 s | | Self-Consistency | 91.6 % | 95.4 % | 100.0 % | 77.4 % | 1 449 s | | Proxy Metrics | 100.0 % | 94.6 % | 100.0 % | 76.8 % | 578 s | | **GT Evolution** ★ | — | **97.4 %** | — | — | — | ★ GT Evolution is the average evolved score across 5 categories from the ground-truth recipe, which runs GT-guided prompt evolution with the same model. This is the **ceiling** — the best result when labels are available. **Column definitions:** - **No-Eval Fitness** — the score the strategy assigned during evolution (0–100 %, without seeing labels). This is the signal evolution optimised. - **GT Score** — ground-truth accuracy, measured after evolution by comparing outputs to the hidden xLAM labels. Invisible to evolution. - **Func Name** — percentage of cases where the model selected the correct function name. - **Param Acc** — percentage of parameter key–value pairs that matched the ground-truth arguments. - **Wall Time** — total clock time including all LLM calls. ### What the results mean 1. **Composite evolution beats the default baseline.** The Composite strategy reaches 95.5 % GT — a +0.1 % lift over the 95.4 % default baseline — and the highest Param Acc of any strategy (77.8 %). 2. **Function names are always correct — the challenge is parameters.** Every strategy achieves 100.0 % Func Name accuracy. All variation lives in Param Acc (76.8–77.8 %). 3. **GT-guided evolution sets the ceiling at 97.4 %.** The best no-eval strategy (Composite, 95.5 %) closes **5 %** of the gap between the default (95.4 %) and the GT ceiling (97.4 %) — without ever seeing a label. 4. **Self-Consistency is the best individual strategy.** 95.4 % GT with 77.4 % Param Acc and moderate wall time (1 449 s). 5. **Proxy Metrics has the highest no-eval fitness but lower GT.** 100.0 % no-eval but 94.6 % GT — structural format checks overfit slightly. Still the fastest strategy (578 s). 6. **Composite is the safest default.** It blends three independent signals — making it robust when you don't know which signal matters most. ### How to apply this to your own agent 1. **Run `mutagenai init`** — at Step 2 select "no" for ground truth, at Step 4 pick **Composite**. 2. **Provide 10–30 representative test inputs** — real queries. 3. **Write proxy checks** for your output format (valid JSON, bracket format, required fields). 4. **Write a rubric** for the LLM-as-Judge scorer — list 3–5 criteria with point values. 5. **Run evolution** and spot-check 3–5 outputs. 6. **Iterate** — tighten the rubric, add proxy checks, increase generations. ## API-Bank no-eval vs ground-truth comparison How close can no-eval evolution get to ground-truth–guided evolution on a real benchmark? This recipe runs the **same API-Bank task** through both pipelines and compares the results head-to-head. ### Results **Model:** Ollama `llama3.2` (3B parameters, running locally). | Approach | No-Eval Fitness | GT Score | API Name | Param Acc | Wall Time | |---|---|---|---|---|---| | Default prompt (no evolution) | — | 55.0 % | — | — | — | | LLM-as-Judge | 90.0 % | 57.9 % | 50.0 % | 44.7 % | 1 474 s | | Preference Scoring | 69.0 % | 60.0 % | 50.0 % | 49.9 % | 959 s | | Proxy Metrics | 100.0 % | 63.4 % | 63.3 % | 56.9 % | 471 s | | Tool-Use Success | 55.0 % | 68.2 % | 66.7 % | 57.3 % | 469 s | | Self-Consistency | 78.1 % | 81.8 % | 83.3 % | 71.2 % | 980 s | | **Composite (recommended)** | **81.3 %** | **82.3 %** | **83.3 %** | **74.1 %** | **2 029 s** | | **GT Evolution** | — | **100.0 %** | **100.0 %** | **100.0 %** | **470 s** | **Key finding:** No-eval evolution delivers a **+27 point improvement** over the default prompt without labels. The Composite strategy closes **61 %** of the gap between the default (55.0 %) and the GT ceiling (100 %). ## Seed templates — external prompt configuration Seed templates define the starting population for prompt evolution. Storing them as external JSON files makes them version-controlled, shareable, and easy to swap between experiments. ### File format Place JSON files in the `seed_templates/` directory at the project root: { "name": "entity-classification", "description": "Diverse seeds for entity classification.", "seeds": [ "Classify this text as Agent, Task, Tool, Input, Output, or Human.", "Think about what role this text plays…", "You are an expert in agentic AI systems…", "Entity type?" ] } ### Loading seeds in code from mutagenai import load_seed_templates, list_seed_templates # List available template files print(list_seed_templates()) # ['entity_classification'] # Load a specific template seeds = load_seed_templates("entity_classification") # Pass to NoEvalPromptEvolver evolver = NoEvalPromptEvolver( task_description="Classify entities in agentic AI.", test_inputs=test_inputs, scorer=scorer, config=config, seed_templates=seeds, ) ### Output schema and automatic proxy checks Seed templates can include an `output_schema` field that defines the expected JSON structure of LLM output. The schema is substituted into seed text via the `{output_schema}` placeholder, and can be converted into proxy checks automatically: { "name": "medical-records", "seeds": ["Generate a JSON record matching: {output_schema}"], "output_schema": { "diagnosis": "string", "medications": [], "details": {"reasoning": "string", "confidence": "number"} } } from MutaGenAI import schema_to_proxy_checks schema = {"diagnosis": "string", "medications": [], "details": {"reasoning": "string"}} checks = schema_to_proxy_checks(schema, weight=1.0) # Generates: valid_json, has_diagnosis, diagnosis_non_empty, # has_medications, medications_is_list, # has_details, details_has_reasoning ### Designing effective seeds Diverse initialisation is the single most impactful factor for evolution quality. Design seeds that vary across **structural archetypes**: | Archetype | What it does | Example | |---|---|---| | **Direct instruction** | Simple baseline | "Classify this text as X, Y, or Z." | | **Chain-of-thought** | Elicits reasoning before answering | "Think about what role this text plays, then classify…" | | **Persona** | Frames the model's role | "You are an expert in X. Determine…" | | **Definitional** | Provides class boundary semantics | "An Agent acts autonomously, a Task is work…" | | **Minimalist** | Explores the terse end of prompt space | "Entity type?" | | **Output-strict** | Enforces format compliance | "Return exactly one word from: X, Y, Z." | | **Contrastive** | Scenario framing | "This must be precise for system correctness…" | Rule of thumb: **one seed per archetype**, and set `population_size` ≥ the number of seeds so every archetype enters the initial gene pool. ## Agent routing — static vs evolved prompt (GPT-4.1) This experiment benchmarks an **evolved** system prompt against a **static** baseline on multi-step agent routing using Azure OpenAI GPT-4.1. The dataset is [V1rtucious/multi-step-agent-routing](https://huggingface.co/datasets/V1rtucious/multi-step-agent-routing) (616 train / 154 test rows, 27 specialist agents). **Evolution config:** 8 generations, population 8 × 2 islands, `NoEvalPromptEvolver` with `CompositeScorer` (LLMJudge 0.3 + SelfConsistency 0.3 + ProxyMetrics 0.3), adaptive mutations, `llm_mutation_rate=0.3`, `describe_entities=True`, `refine_after_splice=True`. The evolution evaluated 134 candidates over ≈ 2.7 hours and converged to a best no-eval fitness of **88.6 %**. **Benchmark:** 100 samples from train + 100 from test (seed 42), scored on agent-set precision / recall / F1. ### Results | | Train F1 | Test F1 | Test Precision | Test Recall | |---|---|---|---|---| | Static prompt | 52.0 % | 49.3 % | 42.4 % | 67.5 % | | **Evolved prompt** | 51.2 % | 49.2 % | 39.1 % | 72.8 % | | Delta | -0.8 % | **-0.2 %** | -3.3 % | **+5.3 %** | **Evolved prompt — test by complexity (mean F1):** | Complexity | F1 | |---|---| | High | 58.6 % | | Medium | 51.6 % | | Low | 38.1 % | **Evolved prompt — test by routing pattern (mean F1):** | Routing pattern | F1 | |---|---| | Approval chain | 56.8 % | | Conditional branching | 56.8 % | | Data enrichment | 54.1 % | | Investigative | 52.7 % | | Linear sequential | 42.9 % | **Key findings:** 1. **Recall improved significantly** (+5.3 % test, +9.4 % train) — the evolved prompt's `"Respond with JSON only. No explanation."` prefix helped the model include more relevant agents rather than hedging. 2. **Precision dropped** (-3.3 %) as the model traded conservative selection for broader coverage. 3. **High-complexity requests benefited most** (58.6 % F1 vs 38.1 % for low), suggesting the evolved prompt helps with multi-step routing where more agents need to be activated. 4. **Investigative and approval-chain patterns** scored highest, indicating the JSON-only instruction particularly aids structured decision flows. **Run the benchmark yourself:** uv run python examples/experiments/agent_routing/run_benchmark.py ## Entity classification — static vs evolved prompt This experiment tests whether starting with diverse seed prompts and evolving them yields better results than starting with an AI-generated expert prompt. ### Results | | Validation | Test | |---|---|---| | Static AI-generated prompt | 62.5 % | 61.0 % | | **Evolved prompt** | **71.5 %** | **62.0 %** | | Delta (evolved - static) | **+9.0 %** | **+1.0 %** | **Per-class validation accuracy:** | Class | Static | Evolved | Delta | |---|---|---|---| | Agent | 27.3 % | 45.5 % | +18.2 % | | Task | 14.3 % | 54.3 % | +40.0 % | | Tool | 96.5 % | 84.2 % | -12.3 % | | Input | 74.4 % | 67.4 % | -7.0 % | | Output | 68.3 % | 90.2 % | +21.9 % | | Human | 15.4 % | 38.5 % | +23.1 % | **Key finding:** The static prompt was heavily biased toward Tool (96.5 %) while failing on Task (14.3 %) and Human (15.4 %). Evolution rebalanced these, with Task jumping +40.0 % and Human +23.1 %. ## Dashboard and visualisation All plotting functions auto-detect the environment: **Plotly** for interactive notebooks, **Matplotlib** for scripts and static output. from mutagenai.dashboard import plot_bfcl_evolution # BFCL benchmark convergence and comparison plot_bfcl_evolution("bfcl_experiment_log.json") ### Available plots | Function | What it shows | |---|---| | `plot_bfcl_evolution()` | BFCL benchmark convergence and comparison | | `plot_tau_bench_evolution()` | τ-bench convergence and sub-score breakdown | | `plot_xlam_evolution()` | xLAM convergence curves and backend comparison | | `plot_toolbench_evolution()` | ToolBench convergence and tier comparison | | `plot_apibank_evolution()` | API-Bank convergence and accuracy breakdown | | `plot_browser_agent_evolution()` | Browser Agent convergence and failure recovery | Each benchmark recipe saves a JSON log that feeds directly into its corresponding dashboard function. ## Red teaming — hardening open-source SLMs The [`MutaGenAI.redteam`](MutaGenAI/redteam/) subpackage turns the evolutionary engine into an **authorized, defensive** red-teaming harness for securing open-source small language models (SLMs). It pairs out of the box with [Microsoft PyRIT](https://github.com/microsoft/PyRIT) for curated harmful-behavior datasets, prompt converters, scorers, and target coverage. ### Two modes | Mode | You evolve | Fitness | Deliverable | |---|---|---|---| | **Harden** | the model's *system prompt* | maximize safe refusals | a hardened system prompt + ASR before/after | | **Attack** | a caller-supplied *attack scaffold* (user turn) | maximize jailbreak success | a coverage map of where the fixed target is weak | The realistic threat model — attacker controls the user turn, defender's system prompt is fixed — is handled by `AttackEvolver`, which inverts which turn evolves. Hardening is the defensive loop: find weaknesses, evolve a system prompt that closes them, then re-measure the attack-success rate (ASR). ### Quick start (harden a local SLM) from MutaGenAI.redteam import ( RedTeamScope, TargetModel, TargetConfig, RedTeamHarness, RedTeamConfig, RedTeamMode, ) from MutaGenAI.redteam.pyrit_bridge import load_behaviors # 1) Authorization guardrail (fails closed if not fully set). scope = RedTeamScope( operator="you", targets=["ollama:llama3.2"], authorized=True, acknowledged_policy=True, ) # 2) Target under test, with its baseline system prompt pinned. target = TargetModel(TargetConfig( model="llama3.2", system_prompt="You are a helpful assistant.", )) # 3) Your own behavior set (never shipped by this library). behaviors = load_behaviors(source="file", path="behaviors.txt") # 4) Evolve a hardened system prompt; measure ASR before vs. after. report = RedTeamHarness( scope, target, RedTeamConfig(mode=RedTeamMode.HARDEN, iterations=10), behaviors=behaviors, ).run() print(report.summary()) # ASR before/after + reduction # report JSON is written under the git-ignored redteam_runs/ directory ### Pairing with PyRIT pip install 'MutaGenAI[llm,redteam]' # installs pyrit from MutaGenAI.redteam.pyrit_bridge import ( load_behaviors, expand_seeds_with_converters, pyrit_available, ) # Curated behaviors from a PyRIT dataset (network + PyRIT required) behaviors = load_behaviors(source="harmbench", limit=50) # Diversify your base scaffolds with PyRIT converters for a richer # initial population (attack mode). seeds = expand_seeds_with_converters( base_scaffolds, ["Base64Converter", "ROT13Converter", "CaesarConverter"], ) The bridge also exposes `PyRITScorerAdapter` (use a PyRIT scorer such as `SelfAskRefusalScorer` as the fitness signal) and `make_target_from_pyrit` (drive any PyRIT `PromptChatTarget` as the target). The bridge is fully lazy — nothing imports PyRIT unless you call it, so the rest of the harness runs without it. ### Red-team modules | Module | Responsibility | |---|---| | [`scope.py`](MutaGenAI/redteam/scope.py) | `RedTeamScope` authorization guardrail (fails closed) | | [`refusal.py`](MutaGenAI/redteam/refusal.py) | `RefusalDetector` — pattern-based refusal detection | | [`scorer.py`](MutaGenAI/redteam/scorer.py) | `RefusalScorer`, `AttackSuccessScorer`, `SafetyJudge` | | [`target.py`](MutaGenAI/redteam/target.py) | `TargetModel` — the model under test, fixed system prompt | | [`attack_evolver.py`](MutaGenAI/redteam/attack_evolver.py) | `AttackEvolver` — evolve the user turn (Mode B) | | [`pyrit_bridge.py`](MutaGenAI/redteam/pyrit_bridge.py) | Microsoft PyRIT datasets, converters, scorers, targets | | [`harness.py`](MutaGenAI/redteam/harness.py) | `RedTeamHarness` — orchestrates both modes + reporting | | [`report.py`](MutaGenAI/redteam/report.py) | `RedTeamReport` — ASR metrics, coverage, safe persistence | Optional MutaGenAI modules (`quality_diversity`, `leaderboard`, `live`) are feature-detected: when present, coverage/leaderboard/streaming capabilities light up automatically; when absent, the harness degrades gracefully. ## Model migration — swap models without losing accuracy When you replace one model with another, prompts tuned for the old model often regress on the new one. The [`MutaGenAI.migration`](MutaGenAI/migration.py) module migrates your existing prompts to a new model — preserving accuracy and, where there's headroom, optimising past it. You start from your known-good seed prompts and some labelled test data. The approach is deliberately efficient: you are never starting from scratch. 1. **Warm start** — evolution is seeded with the old model's winning prompt (`PromptEvolver(..., seed_templates=[known_prompt])`). 2. **Three anchors** — measure `A_old` (old model + old prompt, the bar), `A_transfer` (new model + old prompt, the naive swap), and `A_evolved` (new model + evolved prompt), and surface the **regression set** — the samples the old model got right that the new one breaks. 3. **Early stop** — `PromptEvolverConfig(early_stop_score=A_old)` stops the moment the bar is met (including immediately after seeding), so a clean swap spends no compute. 4. **Decoding re-tuning** — CMA-ES retunes temperature/top-p, which often recovers most of the transfer loss on its own. from MutaGenAI.prompt_evolver import PromptEvolver, PromptEvolverConfig, LLMBackend, ProblemType from MutaGenAI.migration import evaluate_prompt, make_client, MigrationReport old = make_client("llama3.2") new = make_client("qwen3:8b", ollama_think=False) # disable reasoning for clean labels a_old = evaluate_prompt(WINNING_PROMPT, tools, samples, old) a_transfer = evaluate_prompt(WINNING_PROMPT, tools, samples, new) result = PromptEvolver( tools, samples, PromptEvolverConfig(backend=LLMBackend.OLLAMA, ollama_model="qwen3:8b", ollama_think=False, problem_type=ProblemType.CLASSIFICATION, early_stop_score=a_old.accuracy * 100), # preserve the bar seed_templates=[WINNING_PROMPT], # warm start ).run() a_evolved = evaluate_prompt(result.best_prompt, tools, samples, new, temperature=result.best_temperature, top_p=result.best_top_p) print(MigrationReport.build(source_eval=a_old, transfer_eval=a_transfer, evolved_eval=a_evolved, source_model="llama3.2", target_model="qwen3:8b").summary()) ### Worked example: llama3.2 -> qwen3:8b (entity classification, 60 samples) Seeded with the experiment's winning prompt ([`migrate_llama_to_qwen.py`](examples/experiments/entity_classification/migrate_llama_to_qwen.py)): | Configuration | Accuracy | |---|---:| | `A_old` (llama3.2 + old prompt) | 46.7% | | `A_transfer` (qwen3:8b + old prompt) | 68.3% | | `A_evolved` (qwen3:8b + evolved prompt) | **81.7%** | Evolution added **+13.3%** over the naive swap (retuning temp 0.70->0.99, top_p 0.95->0.70) and recovered 4 of 6 transfer regressions — all warm-started from the existing prompt. ## Cookbook recipes All prompt evolution recipes live in [`examples/cookbook/`](examples/cookbook/): | Recipe | Script | What it demonstrates | |---|---|---| | 1 | [`prompt_evolution_azure.py`](examples/cookbook/prompt_evolution_azure.py) | Ollama vs Azure OpenAI (GPT-4.1) head-to-head | | 2 | [`prompt_evolution_agentic.py`](examples/cookbook/prompt_evolution_agentic.py) | Three agentic workloads (support, code, data pipeline) | | 3 | [`prompt_evolution_bfcl.py`](examples/cookbook/prompt_evolution_bfcl.py) | BFCL V4 benchmark — closing the FC-vs-Prompt gap | | 4 | [`prompt_evolution_tau_bench.py`](examples/cookbook/prompt_evolution_tau_bench.py) | τ-bench conversational agent benchmark | | 5 | [`prompt_evolution_xlam.py`](examples/cookbook/prompt_evolution_xlam.py) | xLAM / APIGen function-calling 60 k | | 6 | [`prompt_evolution_toolbench.py`](examples/cookbook/prompt_evolution_toolbench.py) | ToolBench multi-tool API orchestration (16 k APIs) | | 7 | [`prompt_evolution_apibank.py`](examples/cookbook/prompt_evolution_apibank.py) | API-Bank multi-level tool-use evaluation (73 APIs) | | 8 | [`prompt_evolution_browser_agent.py`](examples/cookbook/prompt_evolution_browser_agent.py) | Browser Agent Tasks with failure recovery | | 9 | [`prompt_evolution_no_eval.py`](examples/cookbook/prompt_evolution_no_eval.py) | No-eval strategies — 7 label-free approaches | | 10 | [`prompt_evolution_apibank_no_eval.py`](examples/cookbook/prompt_evolution_apibank_no_eval.py) | API-Bank no-eval vs ground-truth comparison | | 11 | [`prompt_evolution_xlam_no_eval.py`](examples/cookbook/prompt_evolution_xlam_no_eval.py) | xLAM no-eval wizard-style worked example | | 12 | [`prompt_evolution_entity_classification.py`](examples/cookbook/prompt_evolution_entity_classification.py) | Entity classification — static vs evolved prompt | | 13 | [`redteam_harden_slm.py`](examples/cookbook/redteam_harden_slm.py) | Harden an SLM's system prompt; ASR before/after | | 14 | [`redteam_attack_pyrit.py`](examples/cookbook/redteam_attack_pyrit.py) | Authorized attack-scaffold evolution paired with PyRIT | | 15 | [`migrate_llama_to_qwen.py`](examples/experiments/entity_classification/migrate_llama_to_qwen.py) | Model migration — move a winning prompt from llama3.2 to qwen3:8b | ## Installation # Install uv (if you don't have it) curl -LsSf https://astral.sh/uv/install.sh | sh # Clone and sync git clone https://github.com/shanepeckham/MutagenAI.git cd MutagenAI uv sync --extra all # Start a local LLM backend ollama serve ollama pull llama3.2 # Run your first evolution mutagenai init **Optional dependency groups:** pip install mutagenai # core engine (numpy only) pip install mutagenai[llm] # + httpx, azure-identity pip install mutagenai[viz] # + matplotlib, plotly pip install mutagenai[wizard] # + rich pip install mutagenai[redteam] # + pyrit (Microsoft PyRIT) pip install mutagenai[all] # everything For Azure OpenAI, set `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_DEPLOYMENT` (uses RBAC via `DefaultAzureCredential`). For OpenAI, set `OPENAI_API_KEY`. Copy the sample env file and fill in the values you need: cp .env.sample .env See [`.env.sample`](.env.sample) for the full list of supported variables (Ollama, Azure OpenAI, OpenAI, Hugging Face).
标签:AI红队测试, AI风险缓解, C2, DLL 劫持, Petitpotam, 人工智能, 基线管理, 大语言模型, 无监督学习, 用户模式Hook绕过, 进化算法, 逆向工具