DSPy
DSPy (Declarative Self-improving Python) is a Stanford framework for programming language models rather than prompting them. Developed by Khattab et al. (2024), introduced at ICLR; arxiv 2310.03714. The core idea: you declare what a pipeline should do (signatures), compose it from modules, then let an optimizer (teleprompter) figure out the actual prompts and/or weights to make it work.The Three-Layer Stack
1. Signatures
Signatures declare the input-output contract of a step as a typed intent declaration, not a prompt:2. Modules
Modules apply different prompting strategies to a signature. Composable, like PyTorch layers.3. Optimizers (Teleprompters)
Optimizers take a compiled DSPy program, a training set, and a metric function. They search over prompt variations and/or few-shot examples to maximize the metric.
MIPROv2 internals (three stages):
- Bootstrapping — run program many times, collect traces, filter to high-scoring trajectories
- Grounded proposal — use LLM to draft many candidate instructions per prompt, informed by code + data + traces
- Discrete search — mini-batch sampling; scores candidate (instruction, few-shot) combos; updates a surrogate model
GEPA Optimizer
GEPA (Generalized Error-driven Prompt Augmentation) is introduced in arxiv 2507.19457 (Jul 2025). It treats prompt optimization as a reflective improvement loop:- Run current prompt on training examples; collect failures
- Feed failures to a strong reasoning LM (the “reflection LM”)
- Reflection LM generates targeted feedback explaining error patterns
- Feedback is used to refine the prompt
- Repeat until metric plateaus
Compilation
dspy.compile() takes a program + optimizer + training data and returns an optimized program with baked-in prompts. The program structure stays the same; only the instructions and examples inside each module change.
Pipeline Patterns
Common pipeline compositions used in DSPy programs:
DSPy evaluates on the final output of multi-stage pipelines. Every module in the chain can be optimized jointly — you don’t need to optimize each step individually.
Two-LM optimization setup (from dbreunig walkthrough):
When to Use DSPy
Use DSPy when:- You have a measurable metric (exact match, F1, a reward function)
- You have a training set of (input, expected output) pairs — even a few dozen suffices for BootstrapFewShot
- The pipeline has multiple steps that interact
- You want repeatable optimization rather than manual prompt iteration
- Task runs at scale (amortizes compilation cost)
- One-off queries with no metric or training data
- Adding complexity isn’t justified (a single well-crafted prompt may outperform)
- Latency-sensitive paths where compilation overhead matters
- Zero labeled examples — optimizers have no signal without training data
.save() to avoid re-running.
Caveats
- DSPy optimizes for your metric — if the metric is underspecified, the optimizer will game it
- Compiled prompts can be hard to read/debug compared to hand-written prompts
when_to_usein MIPROv2 output: check for overfitting (very specific instructions that don’t generalize)
Relation to Other Wiki Pages
- Context Engineering — DSPy’s compiler is an automated form of prompt-level context engineering
- Agent Harness — DSPy programs can function as components inside a broader agent harness