DSPy: optimizing prompts and LLM programs
Table of contents
- Key takeaways
- What is DSPy?
- Signatures, modules and optimizers
- Compiling instead of hand-writing prompts
- A worked metrics example
- When is DSPy worth it?
- Frequently asked questions
- Does DSPy replace writing prompts entirely?
- Which models and providers does DSPy work with?
- What is the difference between MIPROv2 and GEPA?
- Conclusion
- Sources
DSPy is a Python framework from Stanford that treats calls to an LLM as code: you define signatures with typed inputs and outputs, pick a module such as chain of thought, and let an optimizer write the prompts for you from examples and a metric. That way you compile programs instead of hand-tuning prompts.
DSPy is a Python framework that treats calls to a language model as if they were code: you declare what goes in and what comes out, and an optimizer writes the prompts for you. It came out of Stanford’s NLP group, and its promise is to stop fighting with brittle text strings and start programming AI systems that compile and improve themselves. In this guide you will see what DSPy is, its three pieces (signatures, modules and optimizers), why compiling beats hand-writing prompts, a worked metrics example and when it really pays off. The same explanation is available in Spanish.
Key takeaways
- DSPy is an open-source framework (MIT licence) from Stanford’s NLP group; it has over 36,200 stars on GitHub and is on version 3.2.1, released on 5 May 2026. It requires Python 3.10 or higher.
- Its central idea: you do not write prompts, you write signatures (what goes in and out) and modules (how the model reasons). An optimizer generates and tunes the prompts from examples and a metric.
- The foundational paper (arXiv:2310.03714, Khattab et al., 2023) measures gains of over 25 % on GPT-3.5 and around 65 % on Llama 2 13B against ordinary few-shot prompting.
- It ships several optimizers:
BootstrapFewShot,MIPROv2(tunes instructions and examples with Bayesian search) andGEPA, which evolves prompts by reading execution traces. - It is provider-agnostic: under the hood it uses LiteLLM, so the same program runs on OpenAI, Anthropic, Gemini or models you run on your own machine with Ollama.
What is DSPy?
DSPy is a Python framework for building AI systems in which the language model is one component, not the centre of everything. Instead of writing a long prompt and praying it works, you describe your task as a signature with typed inputs and outputs, and let the framework turn that declaration into concrete instructions for the model. The name stands for "Declarative Self-improving Python".
The project was born in Stanford’s natural-language-processing group, led by Omar Khattab, and it is free software under the MIT licence. It has had a notable reception: it holds more than 36,200 stars on GitHub and is on version 3.2.1, released on 5 May 2026. It installs with a single command and requires Python 3.10 or higher:
pip install dspy
The official documentation sums it up like this: "Express your tasks as structured signatures, not prompts, to produce maintainable, modular, and optimizable programs." That is the underlying difference with building an agent by hand with the provider’s SDK: in DSPy the prompt stops being text you write and becomes an artefact the framework generates and improves.
Signatures, modules and optimizers
DSPy rests on three abstractions worth understanding from the start.
A signature (Signature) declares what the model receives and what it returns. It can be as short as a string, "question -> answer", or a class with typed fields when you need more control:
import dspy
class Classify(dspy.Signature):
"""Classify the sentiment of a sentence."""
sentence: str = dspy.InputField()
sentiment: str = dspy.OutputField()
A module decides how the model reasons over that signature, without you rewriting the task. dspy.Predict asks for the direct answer; dspy.ChainOfThought forces step-by-step reasoning; dspy.ReAct adds tools and an action loop. Swapping strategy means swapping one class for another, just as in LangGraph you change the graph topology without touching the nodes.
And an optimizer (formerly called a teleprompter) is the piece that gives the framework its name: it takes your program, a handful of examples and a metric, and searches for the prompts and few-shot examples that maximize that metric. The most used are BootstrapFewShot (generates demonstrations from the model itself), MIPROv2 (tunes instructions and examples with Bayesian search) and GEPA, discussed below.
Compiling instead of hand-writing prompts
Here is the shift in mindset. The traditional way, you write a prompt, test it, see it fail on an odd case, tweak it, another breaks, and so on in a manual loop that does not scale. DSPy replaces that craft work with a compilation: you define the logic once and the optimizer takes care of writing and tuning the prompts.
Model configuration happens once and applies to the whole program, because under the hood DSPy uses LiteLLM and talks to any provider:
import dspy
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
reason = dspy.ChainOfThought("question -> answer")
out = reason(question="How many sides does a hexagon have?")
print(out.answer)
The advantage is not just convenience. When you switch model (from GPT-4o to Claude, or to one you serve with vLLM on your own infrastructure), you do not rewrite prompts: you recompile and the optimizer adapts the instructions to the new model. The foundational paper (arXiv:2310.03714) quantifies the gain: compiled pipelines beat ordinary few-shot prompting by over 25 % with GPT-3.5 and around 65 % with Llama 2 13B, and even beat expert-written examples.
A worked metrics example
The optimizer needs two things: training examples and a metric, a Python function that scores whether a prediction is good. Examples are wrapped in dspy.Example and you mark which fields are inputs with with_inputs. The metric can be as simple as comparing the expected answer with the model’s:
trainset = [
dspy.Example(question="Capital of France?", answer="Paris").with_inputs("question"),
dspy.Example(question="Capital of Japan?", answer="Tokyo").with_inputs("question"),
]
def is_correct(example, prediction, trace=None):
return example.answer.lower() == prediction.answer.lower()
optimizer = dspy.MIPROv2(metric=is_correct, auto="light")
compiled = optimizer.compile(reason, trainset=trainset)
compiled.save("program.json")
When you call compile, MIPROv2 proposes several instructions, tries different combinations of few-shot examples and keeps the one that scores best against your metric. The result is saved to a JSON you can load in production. The metric is the heart of the system: it defines what "good" means for your task, which is why it is worth investing time in it, just as you would when evaluating structured outputs or measuring an agent’s quality. Without an honest metric, the optimizer tunes toward the wrong target.
GEPA (Genetic-Pareto) is the newest optimizer and works differently: instead of a numeric reward, it reads execution traces, diagnoses failures in natural language and keeps a Pareto frontier of diverse prompts. Its paper (Agrawal et al., ICLR 2026) claims it beats MIPROv2 by around 13 % while using up to 35 times fewer runs.
When is DSPy worth it?
DSPy shines when you have a repeatable task, a set of examples and a clear way to score the result: classification, data extraction, question answering over documents or any pipeline with several chained model steps. In those cases, compiling saves you the manual prompt tuning and gives you a system that re-adapts when you change model.
It is not the right tool for everything. If you just want a single conversational call to a model, setting up signatures, metrics and a training set is too much ceremony. It also has a learning curve: you think in programs and compilation, not text, and that is disorienting at first. And it depends on your being able to define a metric; if your task is purely subjective and you cannot score it, the optimizer loses its bearings. To orchestrate complex stateful flows, a framework like LangGraph stays complementary: DSPy optimizes each call, the graph coordinates the whole. If you are still deciding what an AI agent is and what you need, start there before adding optimization.
Frequently asked questions
Does DSPy replace writing prompts entirely?
Not quite. You still describe the task (the signature, the class instructions, the metric), but you stop writing and tweaking by hand the exact text the model sees. DSPy generates that text for you and tunes it with the optimizer. Think of it as moving from writing assembly to writing in a high-level language: you are still programming, just at a different level of abstraction.
Which models and providers does DSPy work with?
With practically all of them, because under the hood it uses LiteLLM. You configure the model once with dspy.LM("openai/gpt-4o-mini") or whatever identifier applies, and the same program works with OpenAI, Anthropic, Google Gemini, Mistral or models you run on your own machine with Ollama or serve with vLLM. Switching provider is changing one line and recompiling.
What is the difference between MIPROv2 and GEPA?
MIPROv2 optimizes instructions and few-shot examples through Bayesian search guided by a numeric metric. GEPA does not use a scalar reward: it reads execution traces, describes in natural language why each case failed and evolves the prompts while keeping a Pareto frontier. According to its 2026 paper, GEPA beats MIPROv2 by around 13 % with far fewer runs, so it pays off when each model call is expensive.
Conclusion
DSPy proposes a deep change: treating prompts as a compiled artefact rather than text you tune by hand again and again. With three pieces (signatures, modules and optimizers) you describe what you want and let an optimizer like MIPROv2 or GEPA write the instructions from your examples and your metric. At version 3.2.1, MIT-licensed and with over 36,200 stars, it is one of the most solid bets for taking your LLM systems from craft to engineering. The next step is to install it with pip install dspy, define your first signature and compile a simple module with an accuracy metric over half a dozen examples.
Sources: [1] Official DSPy documentation[1], [2] DSPy on GitHub[2], [3] Foundational paper on arXiv[3], [4] dspy package on PyPI[4], [5] Analysis of DSPy optimizers at Future AGI[5].
Sources
Source code
Access all the source code for this post on GitHub.
View on GitHub