> ## Documentation Index
> Fetch the complete documentation index at: https://dsrs.herumbshandilya.com/llms.txt
> Use this file to discover all available pages before exploring further.

# MIPROv2

> Trace-guided instruction and demo optimization: config, usage, and when to use it

MIPROv2 (Multi-prompt Instruction PRoposal Optimizer v2) is a trace-guided instruction and demo optimizer. It differs from COPRO by running your program first, then using the captured execution traces plus a library of prompting best practices to generate candidate instructions, rather than searching blind. It also bootstraps few-shot demos from successful runs.

## How it works

MIPROv2 works in four phases:

### Phase 1: Trace collection

One traced teacher pass over the trainset. Every example runs through your module under trace capture, collecting whole-program scores plus per-`Predict` input/output spans.

### Phase 2: Demo bootstrapping

Successful spans scoring at least `min_demo_score` become few-shot demos on the predictor that produced them (top `max_bootstrapped_demos` by score, deduplicated on inputs). A span scores as its rollout does, unless the metric attached a span-level eval via `TypedMetric::evaluate_spans` — that score then takes precedence (see [Evaluation](/docs/components/evaluation#per-span-credit)). Demos are installed before instruction search, so candidates are scored against the module as it will actually run.

### Phase 3: Candidate generation

Uses the traces and a rotation of prompting tips to generate `num_candidates` instruction variants per predictor. The prompting tips library includes:

* Use clear, specific language
* Consider chain-of-thought for complex tasks
* Specify output formats
* Use role-playing when appropriate
* Handle edge cases explicitly
* Request structured outputs when needed

### Phase 4: Trial evaluation

* Evaluates up to `num_trials` candidates per predictor on one sampled minibatch (candidates injected ambiently — the module is never touched during evaluation)
* Computes performance scores
* Selects the best performing candidate
* Installs the accumulated winner (demos + best instructions) once at the end through `OptimizeTarget::install`

## Configuration

Default settings:

```rust theme={null}
let optimizer = MIPROv2::builder()
    .num_candidates(10)          // Instruction variants per predictor
    .num_trials(20)              // Max candidates evaluated per predictor
    .minibatch_size(25)          // Examples per candidate evaluation
    .max_bootstrapped_demos(4)   // Demos installed per predictor
    .min_demo_score(0.0)         // Score gate for demo-eligible spans
    .build();
```

You can also set `eval_concurrency` (concurrent LM calls during evaluation, default 16) and `seed` (fixes minibatch sampling for reproducible runs). The full field table is in the [optimizers reference](/docs/components/optimizers#miprov2).

## Usage example

```rust theme={null}
use dspy_rs::MIPROv2;

// Create optimizer
let optimizer = MIPROv2::builder()
    .num_candidates(10)
    .num_trials(20)
    .minibatch_size(25)
    .build();

// Typed metric implementing TypedMetric<Row, MyModule> for your trainset row type
let metric = ExactMatchMetric;

// Optimize your module (MyModule declares its leaves via predictors!)
optimizer.compile_module(&mut module, &train_examples, &metric).await?;
```

The metric is the same `TypedMetric` used by every optimizer: `evaluate(&self, example, prediction, trace) -> Result<Eval>`, where `example` is your full trainset row. MIPROv2 only reads the numerical score; feedback is ignored. See the [evaluation reference](/docs/components/evaluation) for the trait.

`train_examples` is a slice of any row type implementing `ToInput` toward the module's input — a `#[derive(Example)]` struct or `(Input, Output)` tuples; see [Data](/docs/components/data). The module must declare its leaves with `predictors!`; see [Modules](/docs/components/modules#predictor-discovery-predictors).

### Typed data loading

Use the shared data ingress reference: [`DataLoader`](/docs/components/data).

## Comparison: COPRO vs MIPROv2 vs GEPA

| Optimizer          | Strategy                                                  | Needs feedback? | Cost                                       |
| ------------------ | --------------------------------------------------------- | --------------- | ------------------------------------------ |
| `BootstrapFewShot` | One-shot demo harvesting from a teacher pass              | No              | Low (2 × trainset)                         |
| `COPRO`            | Breadth-first instruction search                          | No              | Low (breadth × depth × trainset)           |
| `SIMBA`            | Minibatch introspective ascent (demos + rules)            | No              | Low (steps × minibatch)                    |
| `GEPA`             | Genetic-Pareto evolution with feedback                    | **Yes**         | Medium-high (iterations × eval)            |
| `MIPROv2`          | Trace-guided candidate generation                         | No              | Medium (candidates × trials × trainset)    |
| `Structural`       | LM-guided graph edits over `ir::Edit` (program lane only) | No              | Medium (examples + iterations × minibatch) |

GEPA is the only optimizer that requires textual feedback from the metric (`Eval::with_feedback`). The others use numerical scores alone. Full configuration tables for all six live in the [optimizers reference](/docs/components/optimizers).

### When to use MIPROv2

* You have decent training data (15+ examples recommended)
* Quality matters more than speed
* Task benefits from prompting best practices and few-shot demos
* Need trace-informed candidate generation

### When to use COPRO

* You need fast iteration
* Compute budget is limited
* Task is straightforward

### When to use GEPA

* Complex tasks with subtle failure modes
* You can provide rich feedback
* Multi-objective optimization
* Need diverse solutions

## Implementation notes

The code follows standard Rust practices:

* No unsafe blocks
* Results for error handling with context via anyhow
* Strong types (`Candidate`, `PromptingTips`)
* Builder pattern for configuration
* Async throughout, no blocking calls

Key public types:

* `PromptingTips` - the library of best practices (`default_tips()`, `format_for_prompt()`)
* `Candidate` - the shared engine currency MIPROv2 registers its instruction variants as (see [Optimizer engine](/docs/components/optimizer-engine))

`compile_module` returns `()`: MIPROv2 installs the winner on the module and reports nothing further (`Report::None` through the trait).

Cost is roughly `num_predictors × (trainset_size + num_trials × minibatch_size)` LM calls, minus rollout-cache hits.

## Testing

Run tests:

```bash theme={null}
cargo test --test test_miprov2
```

The suite covers trace selection, candidate generation, configuration, and edge cases.

## Example

<Card title="MIPROv2 Example" icon="file-code" href="https://github.com/krypticmouse/DSRs/blob/main/crates/dspy-rs/examples/08-optimize-mipro.rs">
  Complete working example with HuggingFace data loading
</Card>

The example loads data, measures baseline performance, runs optimization, and shows the improvement.

## References

* [DSPy Framework](https://github.com/stanfordnlp/dspy)
* [DSPy Paper](https://arxiv.org/abs/2310.03714)
