Programmatic prompt optimisation
Hand-tuning prompts is guessing with extra steps: you change a word, run five examples, and decide it feels better. Prompt optimisation replaces that with a search loop against a metric.
The framing that makes it click: a prompt is a parameter. Once you have an eval set and a scorer, you can optimise it the way you optimise any other parameter, instead of editing strings by intuition.
What you need before you start
eval set + a scorer ──▶ you can optimise
missing either ──▶ you are guessingThis is the actual barrier, and it is why most teams cannot use these tools: they have no eval set. Building one is the prerequisite, and it is valuable even if you never run an optimiser. See Building eval sets.
DSPy: programs, not prompt strings
DSPy’s idea is to separate what you want from how it is asked. You declare the signature of a step; the framework compiles the wording.
import dspy
class Extract(dspy.Signature):
"""Pull the invoice total from the text."""
document: str = dspy.InputField()
total: float = dspy.OutputField()
program = dspy.ChainOfThought(Extract)Then an optimiser searches — over few-shot example selection, instruction phrasing, or both — scoring candidates against your metric and keeping what wins.
The payoff is not a magic prompt. It is that the prompt stops being source code you maintain by hand. Change the model, recompile; change the task, recompile. The alternative is a 200-line prompt nobody dares edit.
Gotcha: an optimiser will overfit a small eval set exactly like any other search over a small validation set. Hold out a test split you never optimise against, or you will ship a prompt tuned to forty examples.
The techniques underneath
| Technique | Searches over |
|---|---|
| Few-shot selection | which examples to include |
| Instruction search | the wording of the task |
| Bootstrapping | examples the model got right itself |
| Ensembling | several compiled programs |
Few-shot selection is where most of the gain is, and it is the cheapest. Choosing four demonstrations well beats rewriting the instructions, because examples pin down format and edge cases in a way prose does not.
Bootstrapping is the clever one: run the program on training inputs, keep the traces where it got the answer right, and use those as demonstrations. Your few-shot examples are then in the model’s own idiom, which works better than examples a human wrote.
When this is worth it
| Situation | Verdict |
|---|---|
| One stable prompt | not worth it |
| Multi-step pipeline | strong fit |
| Frequent model swaps | strong fit |
| No eval set | build that first |
The compelling case is a pipeline. Hand-tuning three chained prompts is intractable, because changing the first shifts the input distribution of the second. An optimiser handles that jointly; a human does not.
The second is model migration. When a prompt hand-tuned for one model degrades on the next, recompiling against the eval set is minutes instead of a week of fiddling.
The honest limitations
- It optimises what you measure. A weak metric produces a prompt that games it, and LLM-as-judge metrics have their own biases — see LLM as judge.
- Compiled prompts are unreadable. You gain a metric and lose the ability to skim what the model is being told, which matters when debugging.
- It costs tokens. Optimisation runs many candidates over your eval set. Budget it as a training job, not as a config change.
- It cannot fix the wrong decomposition. If the pipeline is badly shaped, no prompt search rescues it.
The pragmatic middle
You do not need a framework to get most of the value:
- Build the eval set and the scorer.
- Version prompts, and record which version produced which trace.
- Change one thing, re-run the eval, keep the winner.
- Automate that loop when it becomes tedious.
That is prompt optimisation with a spreadsheet, and it beats intuition. The framework is an accelerator for a discipline you need regardless — which is the right thing to say when asked whether you have used DSPy.
Related
Interview angle 5
- “Have you used DSPy?” - the useful answer is about the idea rather than the library: a prompt is a parameter, so once you have an eval set and a scorer you can search over few-shot selection and instruction wording instead of editing strings by feel. The prompt stops being hand-maintained source code.
- “What’s the prerequisite?” - an eval set and a metric. Without them there is nothing to optimise against, which is why most teams cannot use these tools. Building the eval set is worth doing even if you never run an optimiser.
- “Where does the gain actually come from?” - few-shot example selection, more than instruction rewording. Examples pin down format and edge cases in a way prose does not, and bootstrapping — keeping traces where the model got it right — produces demonstrations in the model’s own idiom.
- “When is it worth the machinery?” - multi-step pipelines and frequent model swaps. Hand-tuning chained prompts is intractable because changing the first shifts the second’s input distribution; recompiling after a model change is minutes rather than a week.
- “What’s the risk?” - overfitting a small eval set, exactly like any search over a small validation set, so hold out a split you never optimise against. And compiled prompts are unreadable, so you trade debuggability for a number.