Skip to main content
MIPROv2 (Multi-prompt Instruction Proposal Optimizer v2) is an optimizer that uses LLMs to generate and refine prompts automatically. It differs from COPRO by using an LLM to understand the program and apply prompting best practices, rather than just iterating on existing instructions.

How it Works

Stage 1: Trace Generation

async fn generate_traces<M>(
    &self,
    module: &M,
    examples: &[Example],
) -> Result<Vec<Trace>>
  • Runs the existing program with training examples
  • Captures input/output pairs along with evaluation scores
  • Creates a dataset of execution traces that show current behavior

Stage 2: Candidate Prompt Generation

This stage has two sub-steps: First, an LLM analyzes the signature and traces to generate a program description. Then it uses that description along with prompting tips to generate candidate instructions. 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

Stage 3: Evaluation and Selection

  • Evaluates each candidate on a minibatch of examples
  • Computes performance scores
  • Selects the best performing candidate
  • Applies it to the module

Configuration

Default settings:
let optimizer = MIPROv2::builder()
    .num_candidates(10)
    .minibatch_size(25)
    .temperature(1.0)
    .track_stats(true)
    .build();
You can adjust:
  • Number of candidates to generate
  • Minibatch size for evaluation
  • Temperature for generation diversity
  • Whether to display progress stats

Usage Example

use dspy_rs::{MIPROv2, Optimizer};

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

// Optimize your module
optimizer.compile(&mut module, train_examples).await?;

Comparison: COPRO vs MIPROv2 vs GEPA

FeatureCOPROMIPROv2GEPA
ApproachIterative refinementLLM-guided generationReflective evolution
FeedbackScore onlyScore onlyScore + Text
SelectionBest candidateBatch evaluationPareto frontier
LLM callsModerateHighMedium-High
SpeedFasterSlowerMedium
DiversityLowMediumHigh
Best forQuick iterationBest resultsComplex tasks

When to Use MIPROv2

  • You have decent training data (15+ examples recommended)
  • Quality matters more than speed
  • Task benefits from prompting best practices
  • Need LLM-generated program understanding

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 (Trace, PromptCandidate, PromptingTips)
  • Builder pattern for configuration
  • Async throughout, no blocking calls
Key types:
  • Trace - Input/output pair with evaluation score
  • PromptCandidate - Instruction text with score
  • PromptingTips - Library of best practices

Testing

Run tests:
cargo test test_miprov2
There are 29 test cases covering trace generation, candidate selection, configuration, and edge cases.

Example

MIPROv2 Example

Complete working example with HuggingFace data loading
The example loads data, measures baseline performance, runs optimization, and shows the improvement.

References

I