DSPy Guide 2025: Program LLMs with Stanford's Framework
DSPy lets you build reliable LLM pipelines by declaring what you want rather than wrestling with prompts. This guide covers Signatures, Modules, Optimizers, and a working QA pipeline quickstart.
What Is DSPy?
DSPy (Declarative Self-improving Python) is a Stanford research framework that treats LLM pipelines as programs, not prompt templates. The core insight: instead of hand-crafting prompts and manually adjusting them when the LLM changes, you write a pipeline in Python, define a quality metric, and let DSPy's Optimizer automatically figure out the best prompts and few-shot examples.
This makes your pipeline modular and LLM-agnostic. Swap GPT-4 for Claude Sonnet, recompile with the optimizer, and your pipeline adapts without a manual prompt rewrite.
pip install dspy-ai
Key Concepts: Signatures, Modules, Optimizers
DSPy has three core abstractions:
- Signature — declares the input and output fields of an LLM call. Think of it as a typed function signature. No prompt text needed.
- Module — a reusable LLM call strategy:
Predict(direct),ChainOfThought(adds reasoning),ReAct(tool use),MultiChainComparison(self-consistency). - Optimizer — automatically generates few-shot examples and/or instructions to maximize your metric on a trainset.
signatures_example.py
import dspy
# A Signature is just a class with docstring + typed fields
class QuestionAnswer(dspy.Signature):
"""Answer questions with short factual responses."""
question: str = dspy.InputField()
answer: str = dspy.OutputField(desc="1-2 sentence answer")
class SentimentAnalysis(dspy.Signature):
"""Classify the sentiment of a review."""
review: str = dspy.InputField()
sentiment: str = dspy.OutputField(desc="positive, negative, or neutral") Built-in Modules
| Module | What it does | When to use |
|---|---|---|
| dspy.Predict | Single LLM call | Simple extraction, classification |
| dspy.ChainOfThought | Adds reasoning field before output | Multi-step reasoning, math |
| dspy.ReAct | Interleaves Thought/Act/Observe | Tool use, search, agents |
| dspy.MultiChainComparison | Generates N chains, picks best | Self-consistency, high accuracy |
QA Pipeline Quickstart
A complete working example: configure a language model, build a ChainOfThought QA module, and compile it with BootstrapFewShot:
qa_pipeline.py
import dspy
from dspy.teleprompt import BootstrapFewShot
# 1. Configure the language model
lm = dspy.LM('openai/gpt-4o-mini', api_key='sk-...')
# Or use Claude: dspy.LM('anthropic/claude-sonnet-4-5', api_key='sk-ant-...')
# Or local Ollama: dspy.LM('ollama/llama3', base_url='http://localhost:11434')
dspy.configure(lm=lm)
# 2. Define Signature and Module
class QASignature(dspy.Signature):
"""Answer questions accurately and concisely."""
question: str = dspy.InputField()
answer: str = dspy.OutputField()
class RAGPipeline(dspy.Module):
def __init__(self):
self.qa = dspy.ChainOfThought(QASignature)
def forward(self, question: str):
return self.qa(question=question)
# 3. Define metric
def exact_match(example, pred, trace=None):
return example.answer.lower() == pred.answer.lower()
# 4. Compile with BootstrapFewShot
trainset = [
dspy.Example(question="What year was Python created?", answer="1991").with_inputs("question"),
dspy.Example(question="Who created Linux?", answer="Linus Torvalds").with_inputs("question"),
# ... more examples
]
optimizer = BootstrapFewShot(metric=exact_match)
compiled_pipeline = optimizer.compile(RAGPipeline(), trainset=trainset)
# 5. Use the compiled pipeline
result = compiled_pipeline(question="What is the capital of France?")
print(result.answer) # Paris Optimizers: BootstrapFewShot vs MIPRO
DSPy 2.x ships several optimizers. The two most important:
from dspy.teleprompt import BootstrapFewShot, MIPROv2 # BootstrapFewShot — fast, generates few-shot examples only optimizer = BootstrapFewShot(metric=my_metric, max_bootstrapped_demos=4) compiled = optimizer.compile(program, trainset=trainset) # MIPROv2 — slower, optimizes both instructions AND few-shot examples # Requires a larger trainset (50+ examples recommended) optimizer = MIPROv2(metric=my_metric, auto="medium") compiled = optimizer.compile(program, trainset=trainset, num_trials=25)
Start with BootstrapFewShot — it's fast and requires fewer training examples. Upgrade to MIPROv2 once you have a solid trainset and want to squeeze out more accuracy.
DSPy vs LangChain vs LlamaIndex vs Direct Prompting
| Approach | Prompt control | Auto-optimization | Best for |
|---|---|---|---|
| DSPy | Declarative | Yes (Optimizers) | Reliable, repeatable pipelines |
| LangChain | Manual templates | No | Rapid prototyping, tool chains |
| LlamaIndex | Manual templates | Partial | RAG, document Q&A |
| Direct prompting | Full manual | No | Simple one-off calls |
Also see: LangChain guide · LlamaIndex guide · Ollama guide
Monitor the LLMs Your Pipeline Uses
DSPy pipelines call OpenAI, Anthropic, or other LLM APIs. Prismix monitors live API status and alerts you when your LLM backend goes down.
Check LLM API Status →