DSPy Optimizers Explained in 2026: BootstrapFewShot, MIPROv2, COPRO, and GEPA
DSPy optimizers compared: how BootstrapFewShot, MIPROv2, COPRO, and GEPA tune prompts, what DSPy 3.4.1 changed in GEPA, and which optimizer to run first.
Table of Contents
Originally published May 29, 2026.
You wrapped your task in a
dspy.Module, wrote a metric, and got to the line everyone gets stuck on:optimizer = ???. The docs list BootstrapFewShot, BootstrapFewShotWithRandomSearch, MIPROv2, COPRO, and GEPA, each with its own knobs, and none of them says which one is for your problem. So you pick the one from the tutorial you read last, run it for an hour, and hope.
This post settles that choice. DSPy optimizers tune your program’s prompts or weights against a metric; we will walk through what each one does, where each one wins, and a ladder for picking the cheapest one that clears your eval. If you want the method-level theory behind these (textual gradients, genetic search, meta-prompting), read our companion piece on automatic prompt optimization; this one is about DSPy’s specific implementations.
What Are DSPy Optimizers?
DSPy optimizers, formerly called teleprompters, are the algorithms behind DSPy prompt optimization: they tune the prompts and few-shot examples in a DSPy program, and in a few cases the LM weights, against a metric. You define a metric and a trainset, pick an optimizer, and it searches for instructions and demos that score higher. DSPy ships about a dozen; the four here differ in what they tune and cost.
The naming changed, which trips people up. DSPy renamed teleprompters to optimizers, so older tutorials and newer docs describe the same classes with different words. The role never changed: compile a program by searching for better prompts and demos against your metric. If you are new to the framework itself, start with what DSPy is and come back for the optimizer choice.
How Does BootstrapFewShot Work?
BootstrapFewShot is the fast baseline. It runs your program over the trainset, keeps the traces where the output passed your metric, and uses those successful runs as bootstrapped few-shot demonstrations in the prompt. No new instructions are written; the lift comes entirely from showing the model good examples of the task being done right.
It is the right first move because it is cheap and it works on small data. If a handful of bootstrapped demos clears your eval, you are done in minutes. The ceiling is also where it stops: when the instruction itself is the problem, better examples will not fix it, and you climb the ladder.
How Does MIPROv2 Work?
MIPROv2 (Multiprompt Instruction PRoposal Optimizer, version 2) optimizes two things at once: the instructions and the few-shot demos. It proposes candidate instructions, generates candidate demonstration sets, and then runs a Bayesian search over which combination of instruction and demos scores best across your trainset.
The design point is the joint search. MIPROv2 writes instructions that complement the examples it selects, instead of treating them as independent, which produces a more cohesive prompt than tuning either alone. It rewards data: with a couple hundred examples it has enough signal for the Bayesian search to find a strong combination. With twenty examples, the search is mostly noise, and BootstrapFewShot is the better spend.
A minimal MIPROv2 example, with trainset as a list of dspy.Example objects marked with .with_inputs("question"):
# MIPROv2 needs the optional Optuna extra: pip install -U "dspy[optuna]"
import dspy
dspy.configure(lm=dspy.LM("openai/gpt-6-luna"))
trainset = [
dspy.Example(question="What is the capital of France?", answer="Paris").with_inputs("question"),
# ...the rest of your labeled examples
]
def exact_match(example, pred, trace=None):
return example.answer.lower() == pred.answer.lower()
program = dspy.ChainOfThought("question -> answer")
optimizer = dspy.MIPROv2(metric=exact_match, auto="light") # or "medium", "heavy"
optimized = optimizer.compile(program, trainset=trainset)
optimized.save("optimized.json")
The auto setting sizes the run: in DSPy’s source, light, medium, and heavy cap the validation sample at 100, 300, and 1,000 examples. To tune instructions only, pass max_bootstrapped_demos=0 and max_labeled_demos=0, which turns MIPROv2 into a zero-shot instruction optimizer, the setup DSPy’s docs suggest for keeping a prompt 0-shot.
How Does COPRO Work?
COPRO optimizes instructions only, and it does so by coordinate ascent. Each round it scores a set of candidate instructions (10 per predictor by default), then hands the top-scoring attempts back to the prompt model to propose the next set. It stops after a fixed number of rounds set by depth, 3 by default, rather than when the score plateaus.
COPRO sits beside MIPROv2 rather than above it. Reach for COPRO when the demonstrations are fixed or irrelevant and the instruction is the lever, for example a task where you cannot show examples for privacy reasons but you can describe the rules. When demos matter, MIPROv2’s joint search usually wins because COPRO never touches them.
How Does GEPA Work?
GEPA (Genetic-Pareto) is the reflective evolutionary optimizer, accepted as an ICLR 2026 oral. Instead of bootstrapping demos or searching instruction combinations, it reads the program’s execution trajectory, reflects in natural language on what worked and what failed, and proposes prompts that address the gaps. It keeps a Pareto frontier of candidates that each score best on at least one example, and it can use textual feedback as signal.
Two properties make GEPA worth the setup. First, the Pareto front keeps prompts that are each best at something, so multi-objective and multi-module pipelines do not get averaged into a bland compromise. Second, the reflection is sample-efficient: the GEPA paper reports beating MIPROv2 on every tested benchmark and model (+12 percent on AIME-2025 with Qwen3 8B) and GRPO by 6 percent on average with up to 35 times fewer rollouts.
What Changed for GEPA in DSPy 3.4.1
DSPy 3.4.1, released on October 12, 2026, made GEPA’s structured InstructionProposer the default. The DSPy GEPA optimizer now calls the reflection LM through dspy.Predict with a JSON adapter, so tool trajectories, dspy.History inputs, and images reach it as structured content instead of flattened text. GEPA’s reflection instructions themselves did not change.
Three notes in the release affect GEPA upgrades. The reflection LM must be a dspy.LM that can return JSON, and results can differ from 3.4.0 even with the same seed, so re-run your baseline before you compare scores across versions. On Python 3.15, dspy.GEPA raises an ImportError, because the gepa package it depends on supports only earlier Python versions.
Which DSPy Optimizer Should You Use?
| Optimizer | Tunes | Search method | Best for | Data and compute |
|---|---|---|---|---|
| BootstrapFewShot | Few-shot demos | Bootstrapped successful traces | Fast baseline, small data | Low, minutes |
| MIPROv2 | Instructions + demos | Bayesian joint search | Demo-heavy single-metric tasks | 200+ examples, medium |
| COPRO | Instructions only | Coordinate ascent | Instruction-led, no demos | Small to medium |
| GEPA | Instructions, every predictor | Reflective Pareto evolution | Multi-objective, pipelines | Moderate, rollout-efficient |
GEPA vs MIPROv2
Pick between them by what your metric returns. MIPROv2 treats the metric as a black-box scalar and runs Bayesian search over instruction and demo combinations. GEPA reads text feedback from each metric call, returned as dspy.Prediction(score=..., feedback=...), and threads it into the next instruction, so it gains the most when your metric can explain why an output failed.
In the GEPA paper’s GPT-4.1 Mini results, GEPA gained 12.2 points of aggregate score over the unoptimized baseline, against 5.6 points for MIPROv2. DSPy’s own selection guide adds the flip side: without a feedback-rich metric, COPRO or MIPROv2 may give better mileage than GEPA on the same budget.
The DSPy Optimizer Ladder
The mistake is picking the fanciest optimizer first. The rule is to run the cheapest rung that clears your eval and climb only when it does not. We call this the DSPy Optimizer Ladder, and most single-prompt tasks never leave its first two rungs.

- Start on BootstrapFewShot. Minutes to run, works on small data. If the baseline passes, ship it.
- Climb to MIPROv2 when you have a couple hundred examples and want instructions and demos tuned together, or step sideways to COPRO or zero-shot MIPROv2 when only the instruction is in play.
- Climb to GEPA when you have multiple objectives, a multi-module pipeline, or MIPROv2 has plateaued and you can spend the rollouts.
Other DSPy Optimizers Worth Knowing
Two more DSPy optimizers help once the ladder above stops fitting. Both appear in DSPy’s own selection guide, which sorts every optimizer two ways: by whether it tunes instructions, demos, or model weights, and by what a run costs.
- BootstrapFewShotWithRandomSearch (alias
dspy.BootstrapRS): runs BootstrapFewShot with several random seeds, scores each candidate on a valset, and keeps the best. DSPy’s docs suggest it once you have 50 or more examples. - SIMBA (Stochastic Introspective Mini-Batch Ascent): the SIMBA optimizer in DSPy samples a mini-batch each step, picks out the challenging examples in it, and proposes a natural-language rule or a new demo for them.
How Do You Run Optimization Without Wiring DSPy Yourself?
DSPy is a framework: you author modules and signatures, define a metric, and run the optimizer in-process. That control is the point, and it is also the cost. If you want GEPA and other prompt optimizers without managing the plumbing, a platform is the other path.
Future AGI’s agent-learning-kit ships six open-source prompt optimizers (Random Search, Bayesian Search, ProTeGi, Meta-Prompt, PromptWizard, and GEPA) behind one interface, scored by the same evaluators it uses for LLM evaluation. Instead of compiling a module in your code, you point an optimizer at a dataset and an evaluator, and the score that picks the winner is the same score you monitor in production.
Install it with pip install agent-learning-kit; the optimizers import from fi.opt. Its GEPAOptimizer calls the open-source gepa package, the same engine dspy.GEPA uses. The GEPA reference lists GEPA’s parameters and defaults, including max_metric_calls, the evaluation budget that the example below sets to 150.
from fi.opt.optimizers import GEPAOptimizer
from fi.opt.base.evaluator import Evaluator
from fi.opt.datamappers.basic_mapper import BasicDataMapper
evaluator = Evaluator(metric=your_metric) # a Future AGI metric object
data_mapper = BasicDataMapper(key_map={"output": "generated_output", "expected": "expected"})
optimizer = GEPAOptimizer(
reflection_model="gpt-6-astra", # reflects on failures, proposes prompts
generator_model="gpt-6-luna", # runs the candidate prompts
)
result = optimizer.optimize(
evaluator=evaluator,
data_mapper=data_mapper,
dataset=test_cases, # list of {input, expected} dicts
initial_prompts=["You are a helpful assistant.\n\n{input}"],
max_metric_calls=150, # the rollout budget
)
print(result.best_generator.get_prompt_template()) # the winning prompt
print(result.final_score)
Before You Run Any Optimizer
- DSPy needs a metric and a trainset. Every optimizer maximizes the metric, so a weak metric yields a program that games it. Calibrate the metric first, and if you are scoring a multi-step program, our guide to evaluating DSPy pipelines covers signature-level metrics.
- GEPA and MIPROv2 cost rollouts. They are more compute than BootstrapFewShot. Budget them like a CI job and use the ladder so you only pay for the rung you need. Trace each compile with the open-source traceAI
DSPyInstrumentor, which traces DSPy compilation and inference, so you can see where the rollouts went.
From an Empty Optimizer Line to a Compiled Program
That optimizer = ??? line now has a test behind it. Run the cheapest optimizer your data supports, score it on a held-out set, and spend more rollouts only when that score misses your bar. Then save the compiled program and reload it in production, since DSPy’s docs note that compile cost pays off when you compile once per version and serve the saved artifact many times.
If you are weighing DSPy against other frameworks entirely, see our DSPy alternatives breakdown. If you want GEPA scored by the same evals you run on production traffic, Future AGI’s open-source optimizers in agent-learning-kit give you that loop outside your DSPy code.
Want to optimize a prompt against your eval without compiling a DSPy module? Start with Future AGI’s optimization docs and run a GEPA or ProTeGi pass on the dataset you already have.
Sources
- A Comparative Study of DSPy Teleprompter Algorithms (arXiv 2412.15298)
- Optimizing Instructions and Demonstrations for Multi-Stage Language Model Programs, the MIPRO paper (arXiv 2406.11695)
- DSPy 3.2.0 release notes, Optuna made an optional extra
- OpenAI API deprecations, GPT-4o and GPT-4o mini replaced by gpt-6-luna
- OpenAI API models, GPT-6 Astra and GPT-6 Luna
Frequently Asked Questions
What are DSPy optimizers?
How does DSPy prompt optimization work?
What is the difference between MIPROv2 and GEPA in DSPy?
What paper introduced MIPROv2?
What replaced teleprompters in DSPy?
Can you use GEPA without DSPy?
Automatic prompt optimization explained: textual gradients (ProTeGi), score trajectories (OPRO), genetic evolution (GEPA), meta-prompting, and how to pick one.
What is DSPy? Stanford NLP's MIT-licensed framework compiles signatures and modules into tuned prompts. See optimizers, a full example, and DSPy vs LangGraph.
Evaluating DSPy pipelines in 2026: why the compile metric isn't your production rubric, and how to eval the Signature instead of the program.