
Dspy Miprov2
- 4 installs
- 11 repo stars
- Updated June 28, 2026
- lebsral/dspy-programming-not-prompting-lms-skills
Helps with ai & agent building tasks.
About
dspy-miprov2 is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- dspy-miprov2
- AI & Agent Building
- AI-coding skill
Dspy Miprov2 by the numbers
- 4 all-time installs (skills.sh)
- Ranked #13,359 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/lebsral/dspy-programming-not-prompting-lms-skills --skill dspy-miprov2Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| repo stars | ★ 11 |
| Last updated | June 28, 2026 |
| Repository | lebsral/dspy-programming-not-prompting-lms-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Optimize Prompts with MIPROv2
Guide the user through using dspy.MIPROv2, DSPy's most powerful prompt optimizer. MIPROv2 jointly optimizes instructions and few-shot demonstrations to maximize a metric on your training data.
What is MIPROv2
MIPROv2 (Multi-prompt Instruction PRoposal Optimizer v2) is DSPy's recommended optimizer for prompt optimization. Unlike simpler optimizers that only tune few-shot examples, MIPROv2 jointly optimizes:
1. Instructions — the natural-language task descriptions in each module's prompt 2. Few-shot demonstrations — the input-output examples included in each module's prompt
It works by proposing candidate instructions, bootstrapping demonstrations, and searching over combinations using Bayesian optimization. The result is a program with better prompts that produce higher-quality outputs.
When to use MIPROv2
- Production optimization — you want the best prompt quality DSPy can deliver
- 50+ training examples — MIPROv2 needs enough data to search effectively
- Both instructions and demos matter — you want the optimizer to tune everything, not just examples
- You have budget for multiple LM calls — MIPROv2 is more expensive than BootstrapFewShot but produces better results
If you have fewer than 50 examples or need a quick first pass, start with BootstrapFewShot (see /dspy-bootstrap-few-shot), then upgrade to MIPROv2.
Basic usage
import dspy
from dspy.evaluate import Evaluate
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# 1. Your program
qa = dspy.ChainOfThought("question -> answer")
# 2. Your data (mark which fields are inputs)
trainset = [
dspy.Example(question="What is the capital of France?", answer="Paris").with_inputs("question"),
dspy.Example(question="What is 2+2?", answer="4").with_inputs("question"),
# 50-200+ examples recommended
]
devset = [
dspy.Example(question="Who wrote Hamlet?", answer="Shakespeare").with_inputs("question"),
# 20-50 held-out examples for evaluation
]
# 3. Your metric
def metric(example, prediction, trace=None):
return prediction.answer.strip().lower() == example.answer.strip().lower()
# 4. Optimize with MIPROv2
optimizer = dspy.MIPROv2(metric=metric, auto="medium")
optimized = optimizer.compile(qa, trainset=trainset)
# 5. Evaluate improvement
evaluator = Evaluate(devset=devset, metric=metric, num_threads=4, display_progress=True)
score = evaluator(optimized)
print(f"Optimized score: {score:.1f}%")
# 6. Save
optimized.save("optimized_qa.json")The auto parameter
The auto parameter controls how much computation MIPROv2 uses. It sets the number of instruction candidates, demo candidates, and search trials automatically:
| Level | What it does | Typical cost | When to use |
|---|---|---|---|
"light" | Fewer candidates, fewer trials | ~$1-2 | Quick experiments, early iteration |
"medium" | Balanced search | ~$5-10 | Default choice for most tasks |
"heavy" | More candidates, more trials | ~$15-30 | Production, maximum quality |
# Quick experiment
optimizer = dspy.MIPROv2(metric=metric, auto="light")
# Balanced (recommended starting point)
optimizer = dspy.MIPROv2(metric=metric, auto="medium")
# Maximum quality
optimizer = dspy.MIPROv2(metric=metric, auto="heavy")Start with `"medium"`. Only move to "heavy" if you have a large trainset (200+), a meaningful metric, and the budget for it. Use "light" for quick sanity checks during development.
What MIPROv2 tunes
MIPROv2 optimizes every dspy.Predict (or dspy.ChainOfThought, etc.) module in your program. For each module, it tunes:
Instructions
MIPROv2 generates candidate instructions by analyzing your training data and the task structure. It proposes multiple phrasings, then searches for the combination that maximizes your metric.
Few-shot demonstrations
MIPROv2 bootstraps demonstrations by running your program on training examples and keeping successful traces (where the metric passes). It then selects which demos to include in each module's prompt.
Joint optimization
The key advantage over simpler optimizers: MIPROv2 searches over combinations of instructions and demos together. Good instructions may need different demos than mediocre instructions, and MIPROv2 finds the best pairing.
Key parameters
optimizer = dspy.MIPROv2(
metric=metric, # Required: your metric function
auto="medium", # "light", "medium", "heavy" — controls search budget
)
optimized = optimizer.compile(
my_program, # Required: the program to optimize
trainset=trainset, # Required: list of dspy.Example with .with_inputs()
)Manual configuration (advanced)
If auto doesn't give you enough control, you can set parameters directly:
optimizer = dspy.MIPROv2(
metric=metric,
num_candidates=10, # Number of instruction candidates to generate per module
max_bootstrapped_demos=4, # Max bootstrapped demos per module
max_labeled_demos=4, # Max labeled demos per module
num_trials=30, # Number of Bayesian optimization trials
)Most users should stick with auto. Manual configuration is useful when you want to fine-tune the search budget or when you have domain-specific constraints (e.g., limiting demo count to keep prompts short).
Computational cost
MIPROv2 makes many LM calls during optimization. The cost depends on:
- auto level —
"heavy"makes roughly 5-10x more calls than"light" - Number of modules — programs with multiple Predict/ChainOfThought modules cost more
- Trainset size — more examples means more bootstrapping runs
- Model cost — using GPT-4o costs more per call than GPT-4o-mini
Cost management tips
1. Develop with `"light"`, ship with `"medium"` or `"heavy"` — iterate cheaply, then invest in the final optimization 2. Use a cheaper model for optimization, then evaluate on the target model — if your production model is expensive, optimize with a cheaper one first to validate the approach 3. Start with fewer training examples — 50-100 examples is enough for "light" and "medium"; scale up for "heavy" 4. Set `num_threads` in your evaluator to parallelize evaluation calls
Typical wall-clock time
| auto level | 50 examples | 200 examples |
|---|---|---|
"light" | 2-5 min | 5-15 min |
"medium" | 10-20 min | 20-40 min |
"heavy" | 30-60 min | 1-3 hours |
Times vary significantly based on model latency, number of modules, and thread count.
Comparison with other optimizers
| MIPROv2 | BootstrapFewShot | BootstrapFewShotWithRandomSearch | GEPA | |
|---|---|---|---|---|
| Tunes instructions | Yes | No | No | Yes |
| Tunes demos | Yes | Yes | Yes | No |
| Joint optimization | Yes | No | No | No |
| Min examples | ~50 | ~10 | ~50 | ~10 |
| Typical improvement | 15-35% | 5-20% | 10-25% | 5-15% |
| Cost | Medium-High | Low | Medium | Low |
| Best for | Production | Quick start | Better than bootstrap | Few examples |
When to use what
- BootstrapFewShot — first optimization pass, quick iteration, small datasets
- BootstrapFewShotWithRandomSearch — better than BootstrapFewShot when you have 50+ examples and more budget
- MIPROv2 — best prompt optimization, production use, 50+ examples
- GEPA — instruction-only tuning, very few examples
- BootstrapFinetune — fine-tuning model weights (different category entirely)
Stacking optimizers
A common pattern is to run BootstrapFewShot first, then MIPROv2 on the result. Bootstrap finds good demonstrations quickly, then MIPRO refines the instructions around them:
# Step 1: Quick bootstrap
bootstrap = dspy.BootstrapFewShot(metric=metric, max_bootstrapped_demos=4)
bootstrapped = bootstrap.compile(my_program, trainset=trainset)
# Step 2: Refine with MIPROv2
mipro = dspy.MIPROv2(metric=metric, auto="medium")
final = mipro.compile(bootstrapped, trainset=trainset)This often beats running either optimizer alone.
Save and load
# Save the optimized program
optimized.save("optimized_program.json")
# Load later
from my_module import MyProgram # your program class
loaded = MyProgram()
loaded.load("optimized_program.json")
# Use it
result = loaded(question="What is DSPy?")Optimized prompts are model-specific. If you switch LM providers or models, re-run the optimizer. See /ai-switching-models.
Common patterns
Evaluate before and after
Always measure the baseline before optimizing so you know the improvement:
from dspy.evaluate import Evaluate
evaluator = Evaluate(devset=devset, metric=metric, num_threads=4, display_table=5)
# Baseline
baseline_score = evaluator(my_program)
# Optimize
optimizer = dspy.MIPROv2(metric=metric, auto="medium")
optimized = optimizer.compile(my_program, trainset=trainset)
# Compare
optimized_score = evaluator(optimized)
print(f"Baseline: {baseline_score:.1f}%")
print(f"Optimized: {optimized_score:.1f}%")
print(f"Delta: {optimized_score - baseline_score:+.1f}%")Trace-aware metric for better demos
Use the trace parameter to require stricter quality during optimization. This makes MIPROv2 select higher-quality demonstrations:
def metric(example, prediction, trace=None):
correct = prediction.answer.strip().lower() == example.answer.strip().lower()
if trace is not None:
# During optimization: require reasoning too
has_reasoning = len(getattr(prediction, "reasoning", "")) > 50
return correct and has_reasoning
return correctMulti-module programs
MIPROv2 optimizes all modules in your program. For a multi-step pipeline, each module gets its own optimized instructions and demos:
class RAGPipeline(dspy.Module):
def __init__(self):
self.retrieve = dspy.Retrieve(k=3)
self.generate = dspy.ChainOfThought("context, question -> answer")
def forward(self, question):
context = self.retrieve(question).passages
return self.generate(context=context, question=question)
rag = RAGPipeline()
optimizer = dspy.MIPROv2(metric=metric, auto="medium")
optimized_rag = optimizer.compile(rag, trainset=trainset)Cross-references
- Need to prepare training data? Use
/dspy-data - Want to write and run metrics? Use
/dspy-evaluate - Starting with a simpler optimizer first? Use
/dspy-bootstrap-few-shot - Want random search over few-shot demos? Use
/dspy-bootstrap-rs - For the full measure-improve-verify loop, see
/ai-improving-accuracy - For worked examples, see examples.md
- Not sure which skill to use next? Try
/ai-doto get routed to the right one
MIPROv2 Examples
Production Optimization with MIPROv2 auto="medium"
A sentiment classifier optimized with MIPROv2 for production use. Shows the full workflow: baseline evaluation, optimization, comparison, and saving the result.
import dspy
from dspy.evaluate import Evaluate
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# Program: sentiment classification with reasoning
class SentimentClassifier(dspy.Module):
def __init__(self):
self.classify = dspy.ChainOfThought("review -> sentiment: str")
def forward(self, review):
return self.classify(review=review)
classifier = SentimentClassifier()
# Training data (80% of your examples)
trainset = [
dspy.Example(review="Absolutely love this product! Works perfectly.", sentiment="positive").with_inputs("review"),
dspy.Example(review="Terrible quality. Broke after one day.", sentiment="negative").with_inputs("review"),
dspy.Example(review="It's okay. Nothing special but gets the job done.", sentiment="neutral").with_inputs("review"),
dspy.Example(review="Best purchase I've made all year!", sentiment="positive").with_inputs("review"),
dspy.Example(review="Waste of money. Do not buy.", sentiment="negative").with_inputs("review"),
dspy.Example(review="Decent for the price. Some minor issues.", sentiment="neutral").with_inputs("review"),
dspy.Example(review="Five stars! Exceeded all my expectations.", sentiment="positive").with_inputs("review"),
dspy.Example(review="Arrived damaged and customer support was unhelpful.", sentiment="negative").with_inputs("review"),
dspy.Example(review="Average product. Works as described.", sentiment="neutral").with_inputs("review"),
dspy.Example(review="My family uses this every day. Highly recommend!", sentiment="positive").with_inputs("review"),
dspy.Example(review="Stopped working after a week. Very disappointed.", sentiment="negative").with_inputs("review"),
dspy.Example(review="Not bad, not great. It's fine.", sentiment="neutral").with_inputs("review"),
dspy.Example(review="This changed my morning routine for the better!", sentiment="positive").with_inputs("review"),
dspy.Example(review="Poor build quality. Feels cheap.", sentiment="negative").with_inputs("review"),
dspy.Example(review="Does what it says. No complaints.", sentiment="neutral").with_inputs("review"),
dspy.Example(review="Incredible value. Would buy again in a heartbeat.", sentiment="positive").with_inputs("review"),
dspy.Example(review="Returned it the same day. Completely useless.", sentiment="negative").with_inputs("review"),
dspy.Example(review="Solid product for everyday use.", sentiment="neutral").with_inputs("review"),
dspy.Example(review="A game changer! So glad I found this.", sentiment="positive").with_inputs("review"),
dspy.Example(review="The worst product I have ever purchased.", sentiment="negative").with_inputs("review"),
]
# Held-out dev set (20% of your examples)
devset = [
dspy.Example(review="Outstanding quality and fast shipping!", sentiment="positive").with_inputs("review"),
dspy.Example(review="Doesn't work as advertised. Frustrating.", sentiment="negative").with_inputs("review"),
dspy.Example(review="It's alright. Meets basic expectations.", sentiment="neutral").with_inputs("review"),
dspy.Example(review="Pleasantly surprised by how well this works.", sentiment="positive").with_inputs("review"),
dspy.Example(review="Cheap materials, fell apart quickly.", sentiment="negative").with_inputs("review"),
]
# Metric: normalized match on sentiment label
def sentiment_match(example, prediction, trace=None):
pred = prediction.sentiment.strip().lower()
gold = example.sentiment.strip().lower()
match = pred == gold
if trace is not None:
# During optimization, also require reasoning
has_reasoning = len(getattr(prediction, "reasoning", "")) > 20
return match and has_reasoning
return match
# Evaluate baseline
evaluator = Evaluate(
devset=devset,
metric=sentiment_match,
num_threads=4,
display_progress=True,
display_table=5,
)
baseline_score = evaluator(classifier)
print(f"Baseline: {baseline_score:.1f}%")
# Optimize with MIPROv2
optimizer = dspy.MIPROv2(metric=sentiment_match, auto="medium")
optimized = optimizer.compile(classifier, trainset=trainset)
# Evaluate optimized program
optimized_score = evaluator(optimized)
print(f"Optimized: {optimized_score:.1f}%")
print(f"Delta: {optimized_score - baseline_score:+.1f}%")
# Save for production
optimized.save("optimized_sentiment.json")
# Load later
production_classifier = SentimentClassifier()
production_classifier.load("optimized_sentiment.json")
result = production_classifier(review="This product is amazing!")
print(f"Sentiment: {result.sentiment}")Heavy Optimization for Maximum Quality
A multi-step RAG pipeline optimized with auto="heavy" for the highest quality. Demonstrates optimizing a complex program with multiple modules, using a composite metric, and stacking optimizers.
import dspy
from dspy.evaluate import Evaluate
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# Multi-step program: extract key info, then generate a detailed answer
class DetailedQA(dspy.Module):
def __init__(self):
self.extract = dspy.ChainOfThought("question -> key_concepts: list[str]")
self.answer = dspy.ChainOfThought("question, key_concepts -> answer")
def forward(self, question):
extraction = self.extract(question=question)
return self.answer(question=question, key_concepts=extraction.key_concepts)
qa = DetailedQA()
# Training data
trainset = [
dspy.Example(
question="What causes tides on Earth?",
answer="Tides are primarily caused by the gravitational pull of the Moon on Earth's oceans, with the Sun also contributing. The Moon's gravity creates a bulge of water on the side of Earth facing it and another on the opposite side, resulting in high tides."
).with_inputs("question"),
dspy.Example(
question="How does a refrigerator work?",
answer="A refrigerator works by circulating a refrigerant through a cycle of compression and expansion. The compressor pressurizes the refrigerant gas, which then condenses into a liquid releasing heat. The liquid evaporates inside the fridge absorbing heat, cooling the interior."
).with_inputs("question"),
dspy.Example(
question="Why do leaves change color in autumn?",
answer="Leaves change color because shorter days trigger trees to stop producing chlorophyll, the green pigment. As chlorophyll breaks down, other pigments like carotenoids (yellow/orange) and anthocyanins (red/purple) become visible."
).with_inputs("question"),
dspy.Example(
question="What is the greenhouse effect?",
answer="The greenhouse effect is the process where certain gases in Earth's atmosphere trap heat from the Sun. Solar radiation passes through the atmosphere and warms the surface, which then emits infrared radiation. Greenhouse gases absorb this radiation and re-emit it, warming the atmosphere."
).with_inputs("question"),
dspy.Example(
question="How do vaccines work?",
answer="Vaccines introduce a weakened or inactive form of a pathogen to the immune system. This triggers the body to produce antibodies and memory cells without causing the disease. If exposed to the real pathogen later, the immune system can respond quickly."
).with_inputs("question"),
dspy.Example(
question="What causes earthquakes?",
answer="Earthquakes are caused by the sudden release of energy in Earth's crust, usually due to tectonic plates moving past, colliding with, or pulling apart from each other. The point where the rupture starts is the focus, and the point directly above on the surface is the epicenter."
).with_inputs("question"),
dspy.Example(
question="How does GPS work?",
answer="GPS works using a network of satellites orbiting Earth. A GPS receiver calculates its position by measuring the time signals take to arrive from at least four satellites. Using these time differences and the known positions of the satellites, it triangulates the receiver's location."
).with_inputs("question"),
dspy.Example(
question="Why is the sky blue?",
answer="The sky appears blue because of Rayleigh scattering. Sunlight contains all colors, but shorter blue wavelengths scatter more when hitting gas molecules in the atmosphere. This scattered blue light reaches our eyes from all directions, making the sky look blue."
).with_inputs("question"),
dspy.Example(
question="How do antibiotics work?",
answer="Antibiotics work by either killing bacteria or stopping them from reproducing. Some target the bacterial cell wall, others interfere with protein synthesis or DNA replication. They are effective against bacteria but not viruses."
).with_inputs("question"),
dspy.Example(
question="What causes rainbows?",
answer="Rainbows form when sunlight enters water droplets, refracts (bends), reflects off the back of the droplet, and refracts again as it exits. This process separates white light into its component colors, creating the visible spectrum arc."
).with_inputs("question"),
]
# Held-out dev set
devset = [
dspy.Example(
question="How do solar panels generate electricity?",
answer="Solar panels use photovoltaic cells made of semiconductor materials like silicon. When photons from sunlight hit the cells, they knock electrons loose, creating an electric current. This direct current is then converted to alternating current by an inverter."
).with_inputs("question"),
dspy.Example(
question="Why do we dream?",
answer="The exact purpose of dreaming is debated, but leading theories suggest dreams help with memory consolidation, emotional processing, and problem-solving. During REM sleep, the brain is highly active and replays and reorganizes experiences from waking life."
).with_inputs("question"),
dspy.Example(
question="How does Wi-Fi work?",
answer="Wi-Fi uses radio waves to transmit data between devices and a router. The router connects to the internet via a wired connection and broadcasts a wireless signal. Devices with Wi-Fi adapters send and receive data by modulating and demodulating these radio signals."
).with_inputs("question"),
]
# Composite metric: correctness + completeness + conciseness
class JudgeAnswer(dspy.Signature):
"""Judge whether the predicted answer correctly and completely covers the key facts in the reference answer."""
question: str = dspy.InputField()
reference_answer: str = dspy.InputField()
predicted_answer: str = dspy.InputField()
is_correct: bool = dspy.OutputField(desc="True if key facts are covered accurately")
is_complete: bool = dspy.OutputField(desc="True if no major facts are missing")
judge_lm = dspy.LM("openai/gpt-4o")
def quality_metric(example, prediction, trace=None):
# Correctness and completeness via LM judge
with dspy.context(lm=judge_lm):
judge = dspy.Predict(JudgeAnswer)
result = judge(
question=example.question,
reference_answer=example.answer,
predicted_answer=prediction.answer,
)
correct = float(result.is_correct)
complete = float(result.is_complete)
# Conciseness heuristic
word_count = len(prediction.answer.split())
if word_count <= 80:
concise = 1.0
elif word_count <= 150:
concise = 0.5
else:
concise = 0.0
# During optimization, require correctness and completeness
if trace is not None:
return correct and complete and concise >= 0.5
return 0.5 * correct + 0.3 * complete + 0.2 * concise
# Evaluate baseline
evaluator = Evaluate(
devset=devset,
metric=quality_metric,
num_threads=4,
display_progress=True,
display_table=3,
)
baseline_score = evaluator(qa)
print(f"Baseline: {baseline_score:.1f}%")
# Step 1: Quick bootstrap to find good demos
bootstrap = dspy.BootstrapFewShot(metric=quality_metric, max_bootstrapped_demos=4)
bootstrapped = bootstrap.compile(qa, trainset=trainset)
bootstrap_score = evaluator(bootstrapped)
print(f"After bootstrap: {bootstrap_score:.1f}%")
# Step 2: Heavy MIPROv2 optimization on the bootstrapped result
optimizer = dspy.MIPROv2(metric=quality_metric, auto="heavy")
final = optimizer.compile(bootstrapped, trainset=trainset)
# Evaluate final result
final_score = evaluator(final)
print(f"\nResults:")
print(f" Baseline: {baseline_score:.1f}%")
print(f" After bootstrap: {bootstrap_score:.1f}%")
print(f" After MIPROv2: {final_score:.1f}%")
print(f" Total delta: {final_score - baseline_score:+.1f}%")
# Save the final optimized program
final.save("optimized_detailed_qa.json")