DSPy Explained: Signatures, Modules and Prompt Optimization

Understand DSPy signatures, modules and optimizers, try the current docs example, and decide when measured prompt optimization is worth the work.

Published

DSPy Explained: Signatures, Modules and Prompt Optimization

DSPy is worth trying when you can score a repeated AI task and hand-tuning its prompts has become a maintenance job. It moves that work into a Python program and an optimization loop; the small first-run design below budgets up to 100 single-step program passes before retries. If your business cannot agree on what a correct answer looks like, start there before adding DSPy.

Verified on 15 October 2026 against DSPy's current documentation, PyPI and Google's model pricing. PyPI lists DSPy 3.4.1, released 12 October 2026, with Python 3.10 or later, below 3.16. The package to install is dspy. PyPI package metadata.

What Is DSPy, and What Does Compile Mean?

DSPy is an open-source Python framework for defining language-model tasks as programs, then improving their prompts or, with specific optimizers, their model weights. Its useful promise is repeatable improvement against your definition of success. It does not promise that your first generated prompt will beat a carefully written one. DSPy overview.

Think of a rehearsal. The signature is the brief, the module is how the performance happens, and the metric is the judging rule. The optimizer changes something, listens to the result, and keeps useful changes. Without a judging rule, you can rehearse differently forever without knowing whether anything improved.

Signatures Describe the Job

A signature declares the inputs, outputs and task instructions. "ticket -> category" says that a ticket goes in and a category comes out. A class-based signature can add typed fields and a docstring explaining the task. Your choice of fields still matters: an optimizer cannot rescue an ambiguous business definition of “urgent.” Signature documentation.

DSPy Modules Describe How It Runs

A module implements a strategy for doing the task. dspy.Predict is the basic prediction building block. dspy.ChainOfThought adds a reasoning field before the answer. dspy.ReAct supports a reasoning-and-tool-use loop. You can compose modules in Python when one model step is insufficient. Module documentation.

For a support-ticket router, begin with Predict. Adding a tool loop to classify a short message creates more moving parts before you have demonstrated a need for them.

Compilation Is an Optimization Procedure

An optimizer changes selected parts of a program using examples and a metric. For prompt optimization, .compile() returns a program carrying revised instructions or demonstrations, meaning examples included in the prompt. It does not turn the application into a native executable or eliminate future model calls. Weight training is a separate optimizer choice. Optimizer selection guide.

DSPy's current documentation showing its first haiku-generating program
DSPy: the current first-program example

DSPy Tutorial: The Current Docs' Minimal Example

The current starter program generates a haiku. That small example reveals the interface without involving a database, tools or an optimization search.

Install the verified release in your Python environment:

Bash
python -m pip install 'dspy[litellm]==3.4.1'

Set GEMINI_API_KEY in your environment to a valid Google Gemini API key. Save this as first_dspy.py, then run python first_dspy.py:

Python
import os
import dspy

lm = dspy.LM(
    "gemini/gemini-2.5-flash-lite",
    api_key=os.environ["GEMINI_API_KEY"],
    engine="litellm",
)
dspy.configure(lm=lm)

haiku_signature = "subject -> haiku"
haiku_generator = dspy.Predict(haiku_signature)
result = haiku_generator(subject="computer science")
print(result.haiku)

The final four lines are the current docs' first program. The setup follows DSPy's model configuration pattern, with Google Gemini 2.5 Flash-Lite substituted so the experiment below uses one inexpensive model. The provider string follows the LiteLLM Gemini integration; the explicit backend is supported by the DSPy LM API.

DSPy builds a prompt from the signature, sends it to the configured model and parses the returned haiku field. This is an inference example, not an optimization run. No particular poem or quality improvement is claimed here.

DSPy Prompt Optimization Needs a Metric

No metric, no meaningful optimization. A metric is a function that turns a prediction into a score. For ticket routing, it can compare the predicted category with a human-approved label. For a generated answer, correctness may require a rubric or an executable check. DSPy's metric interface accepts your scoring function; the framework does not decide what your business should reward. Metrics and evaluation.

That is why a small labelled evaluation set matters. If you are tired of changing “be concise” to “respond briefly,” collect actual failures instead: a refund request sent to technical support, a login problem mistaken for billing, or a message containing two requests. Label the intended result before looking at a new prompt's output.

For a first pilot, use 60 distinct examples, deliberately split into 20 training, 20 validation and 20 final-test examples. These are proposed experiment sizes, not DSPy requirements or proof of production reliability.

  • Training examples supply material the optimizer can learn from.
  • Validation examples help you compare versions and make development decisions.
  • Final-test examples stay untouched until you freeze the candidate and evaluation rules.

Keep related tickets from the same customer or incident in one split. Otherwise, near-duplicates can make an optimized prompt look more general than it is. Include rare failures that matter, rather than filling every split with easy cases.

Architectural optimization loop connecting examples, program, metric and optimizer, with a separate holdout chamber
The final holdout stays outside the tuning loop.

There is a precise caveat to “no metric.” You can run an unoptimized DSPy program without one, and LabeledFewShot can attach examples without scoring them. That is configuration, not evidence that a change improved your task. Nor must every task have a handwritten reference answer: an executable correctness check can supply the signal. For business classification, approved labels are usually the clearest starting point. Few-shot baseline.

DSPy Optimizers: The Names in Today's Docs

Start with the simplest optimizer that addresses a failure you can name. More search is useful only when the metric can distinguish better candidates from worse ones.

The current optimizer API index lists these exact entries: BetterTogether, BootstrapFewShot, BootstrapFewShotWithRandomSearch, BootstrapFinetune, BootstrapRS, COPRO, Ensemble, GEPA, InferRules, KNN, KNNFewShot, LabeledFewShot, MIPROv2 and SIMBA.

The entries are not all independent search algorithms. BootstrapRS aliases BootstrapFewShotWithRandomSearch; KNN is the retrieval helper used by KNNFewShot. The current selection guide also names AvatarOptimizer for agent-style programs, although it is absent from that API index. Family details, selection guide.

What needs changingNames to understandFirst decision
Examples in the promptLabeledFewShot, BootstrapFewShot, BootstrapFewShotWithRandomSearch / BootstrapRSTry a simple demonstration baseline before searching many sets.
Examples matched to each inputKNNFewShot, supported by KNNUseful when different inputs need different examples; adds retrieval and runtime work.
InstructionsCOPRO, GEPAUse GEPA when informative failure feedback can guide revisions.
Instructions and demonstrationsMIPROv2, SIMBA, InferRulesConsider these when the initial examples or rules are inadequate.
Model weights or combined strategiesBootstrapFinetune, BetterTogetherRequires a reason to train weights and a model that supports it.
Combining existing candidatesEnsembleMultiple programs can contribute to an answer, with more inference work.

MIPROv2 proposes instructions and uses Bayesian optimization, a search method informed by previous scores, to explore combinations of instructions and demonstrations. SIMBA works through minibatches and proposes rules or demonstrations. Those are different approaches, not renamed versions of BootstrapFewShot. MIPROv2 API, SIMBA API.

GEPA uses a reflection model to read execution feedback and propose instruction changes. A score accompanied by a specific explanation, such as “refund requests belong to billing even when they mention a broken feature,” gives it a more useful signal than a bare failure score. The detailed docs allow numeric-only metrics, but explain that these lose the richer feedback. GEPA mechanics.

Bound the First Optimization Run

A small BootstrapFewShot experiment is easier to account for than an open-ended instruction search. The following design uses one Predict module, a deterministic label metric and the same model for teaching and prediction.

DSPy BootstrapFewShot: A Deliberately Small Pass

Prepare train.jsonl, validation.jsonl and test.jsonl, each with 20 distinct, human-labelled tickets. Each line is a JSON object with text and label; allowed labels are billing, technical, account and other. For example, a request to change an invoice address could carry the label billing. These must be your examples, not the haiku output.

Save this separate script as optimize_tickets.py and run it after setting the same API key:

Python
import json
import os
from typing import Literal
import dspy

lm = dspy.LM(
    "gemini/gemini-2.5-flash-lite",
    api_key=os.environ["GEMINI_API_KEY"],
    engine="litellm",
    cache=False,
)
dspy.configure(lm=lm)

class RouteTicket(dspy.Signature):
    """Choose the support queue responsible for the ticket's main request."""
    text: str = dspy.InputField()
    label: Literal["billing", "technical", "account", "other"] = (
        dspy.OutputField()
    )

def load_examples(path):
    with open(path, encoding="utf-8") as source:
        rows = [json.loads(line) for line in source if line.strip()]
    assert len(rows) == 20
    return [dspy.Example(**row).with_inputs("text") for row in rows]

train = load_examples("train.jsonl")
validation = load_examples("validation.jsonl")
test = load_examples("test.jsonl")
assert len({e.text for e in train + validation + test}) == 60

def correct_label(example, prediction, trace=None):
    return prediction.label == example.label

program = dspy.Predict(RouteTicket)
evaluate = dspy.Evaluate(
    devset=validation, metric=correct_label, num_threads=1
)
print("Baseline validation:", evaluate(program).score)

optimizer = dspy.BootstrapFewShot(
    metric=correct_label,
    max_bootstrapped_demos=4,
    max_labeled_demos=0,
    max_rounds=1,
)
optimized = optimizer.compile(program, trainset=train)
print("Optimized validation:", evaluate(optimized).score)

# Freeze this candidate before opening the final test results.
final_test = dspy.Evaluate(
    devset=test, metric=correct_label, num_threads=1
)
print("Baseline final test:", final_test(program).score)
print("Optimized final test:", final_test(optimized).score)
optimized.save("candidate_ticket_router.json")

This is an adaptation using the documented BootstrapFewShot constructor and evaluation interface, not a claimed benchmark. It requests up to four successful demonstrations, allows one bootstrap attempt per training example and adds no raw labelled demonstrations. The saved file is a candidate for review, not an automatic production promotion.

The accounting for this design is:

  • Baseline on validation: 20 program passes.
  • Bootstrap over the training set: up to 20 teacher passes, potentially fewer when enough demonstrations pass.
  • Optimized program on validation: 20 program passes.
  • Both versions on the final test set: 40 program passes.

That is up to 100 single-step passes, including evaluation; compilation alone accounts for at most 20. With one successful model request per pass, no retries and no parsing fallbacks, those passes become at most 100 task-model calls. The earlier haiku sanity check is separate. An LLM judge, an agent loop, retries or another optimization attempt changes the total.

Four architectural bays showing 20 baseline passes, up to 20 bootstrap passes, 20 comparison passes and 40 final-test passes
An illustrative first-run budget, before retries or additional model steps.

At Google's Standard paid text list price for Gemini 2.5 Flash-Lite, $0.10 per million input tokens and $0.40 per million output tokens, 100 calls averaging an assumed 1,000 input and 100 output tokens cost $0.014: 100,000 ÷ 1,000,000 × $0.10 + 10,000 ÷ 1,000,000 × $0.40. Google's pricing.

That is model-only arithmetic, not a measured invoice or spending cap. The input assumption must include DSPy's formatting and attached demonstrations; longer tickets, larger outputs and billable thinking increase the amount. There is no batch or cache discount in the estimate. Your actual provider usage is the accounting record.

DSPy Framework vs Hand-Written Prompts and Fine-Tuning

Use DSPy when you need a repeatable way to improve a measurable program. Keep a handwritten prompt when it already meets the task's requirements. Consider fine-tuning when you have evidence that changing prompts is insufficient and training a model fits your deployment constraints.

ApproachWhat changesWhat you must ownGood first use
Hand-written promptsInstructions and examples edited by a personTask definition and evaluationA small, stable task with an adequate prompt
DSPy prompt optimizationInstructions or demonstrations selected programmaticallyPython program, examples and metricRepeated classification, extraction or answer workflows needing systematic improvement
Fine-tuningModel weights learned from training dataTraining data, model support and evaluationStable behavior that warrants training and maintaining a model variant

These are compatible techniques. DSPy includes BootstrapFinetune, and BetterTogether combines prompt and weight optimization. The practical difference is what you are changing and maintaining, not which label sounds more advanced. Prompt and weight optimization.

The crossover is operational: if every model change or new failure case sends an engineer back to manually editing several prompts, a scored optimization loop becomes attractive. If a short prompt already clears your acceptance criteria, replacing it with a framework has no demonstrated payoff.

Who Should Act, Wait or Skip It?

Engineers should act when they have a repeated task, a working baseline and representative failures. Ticket routing and structured extraction are useful starting points because the expected outputs can be checked. Optimize a small component before expanding the system. The wall DSPy hits is the quality of your objective: it can select a higher-scoring candidate while your metric rewards the wrong behavior.

Operators should own the labels and error priorities. A support manager may care much more about misrouting an account-access incident than an ordinary billing question. If the score treats both equally, the optimization may contradict the manager's priorities. Resolve that policy before asking an engineer to improve the score.

Buyers should ask for evidence on unseen examples. Ask which baseline was compared, which cases regressed, what the serving program now includes, and whether the workflow still meets its latency requirements. An optimizer's name is not acceptance evidence. The site's Promptfoo review is adjacent reading for teams organizing evaluation work.

A business team should skip DSPy for occasional copywriting, one-off research or a workflow that changes before examples can accumulate. Also skip it when nobody can maintain Python code or agree on a scoring rule. A good prompt, a checklist and human review can be a more proportionate system. Wait when your real problem is missing source data or an undefined process; optimizing wording will not supply either.

What's Overhyped About DSPy?

“Programming instead of prompting” does not make prompts disappear. DSPy creates and manages them; people still define the task, judge failures and decide whether the outcome is acceptable.

“Compile” does not mean the model learned permanently. With prompt optimizers, the learned artifact is instructions or demonstrations. You still call a model at inference time, and demonstrations occupy prompt space. You can save learned state as JSON and load it into a fresh program. Saving and loading.

“Automatic optimization” does not mean an automatic win. The useful comparison is against your existing approach on unseen inputs. A higher development score can reflect overfitting, a permissive judge or a dataset that misses expensive failures.

And metric calls are not provider calls. GEPA's max_metric_calls budgets scoring invocations; reflection also calls a model, and a scored program may itself make multiple requests. Its auto settings do not imply a universal invoice. GEPA budget controls.

Questions Engineers Ask About DSPy

How does DSPy work?

You declare inputs and outputs with a signature, choose or compose modules, and configure a language model. An adapter builds the messages and parses results. An optimizer can then improve selected parts using examples and a metric.

Is DSPy production ready?

Treat readiness as a property of your program. Pin the package and model configuration, preserve a baseline, test representative failures and define rollback criteria. The existence of a current release does not certify your workflow.

Is DSPy still relevant?

Yes, for repeated, measurable language-model tasks whose prompts need maintenance. Its value is smaller when a simple prompt already meets your needs or the task lacks a stable evaluation rule.

What are the key differences between LangChain and DSPy?

LangChain is a framework for building agents with model and tool integrations. DSPy's distinguishing emphasis is declaring model tasks and optimizing programs against metrics. Choose according to the problem: connecting an agent's components or systematically improving a scored task. LangChain overview.

Can DSPy be used for prompt optimization?

Yes. Depending on the optimizer, it can select demonstrations, revise instructions or search both. Prompt optimization leaves the underlying model weights unchanged; fine-tuning is a separate route.

Who created DSPy?

DSPy was developed by Omar Khattab and collaborators. Its repository is maintained under Stanford NLP; the package's research citation names the contributing authors. Project and authors.

How does DSPy Gepa work?

GEPA uses feedback from program executions to propose instruction changes through a reflection model, evaluates candidates and returns a selected program. Specific failure feedback makes the search better informed than a score alone. It does not guarantee a quality gain.

What is the full form of the DSPy framework?

DSPy stands for Declarative Self-improving Python, as stated in the official package description.

Your Next Step

On Monday, pick one recurring ticket-routing or extraction failure and ask the process owner to label representative examples. Freeze a baseline, define a scoring rule and run one bounded experiment. Review changed answers before approving the candidate. If you cannot define the score, the useful next task is agreeing on the business rule.

Get practical AI engineering explainers in the newsletter.

Published
Category
Build
Related Articles
Agent Skills: How One Folder Works Across AI Tools

Agent Skills: How One Folder Works Across AI Tools

Agent Skills explained: the SKILL.md standard, verified tool support, a portable weekly-report example, context overhead and security boundaries.Oct 15, 2026Build
Best Embedding Models in 2026: Gemini, OpenAI, Voyage, Cohere, Qwen and BGE Compared

Best Embedding Models in 2026: Gemini, OpenAI, Voyage, Cohere, Qwen and BGE Compared

Compare Gemini, OpenAI, Voyage, Cohere and open embedding models by retrieval fit, languages, deployment and migration work.Oct 15, 2026Build
LangSmith vs Langfuse

LangSmith vs Langfuse

Compare LangSmith and Langfuse for tracing, evaluations, datasets, prompts and self-hosting. Choose by framework fit and operational ownership.Oct 14, 2026Build
OpenAI AgentKit: Shutdown Dates and Migration Guide

OpenAI AgentKit: Shutdown Dates and Migration Guide

What survives AgentKit, what shuts down, and how to migrate Agent Builder, ChatKit backends, evals, and prompt objects before the 2026 deadlines.Oct 14, 2026Build
Base44 Alternatives 2026: Lovable, Bolt, Replit, v0, Emergent, Blink and Whacka

Base44 Alternatives 2026: Lovable, Bolt, Replit, v0, Emergent, Blink and Whacka

Compare Lovable, Bolt, Replit, v0, Emergent, Blink and Whacka on current pricing, code export, databases, hosting and mobile apps.Oct 14, 2026Build
vLLM vs SGLang

vLLM vs SGLang

Compare vLLM and SGLang for agents and RAG: current releases, hardware, caching, JSON, LoRA, deployment and GPU cost calculations.Oct 14, 2026Build
LlamaIndex vs LangChain

LlamaIndex vs LangChain

Choose LlamaIndex or LangChain for RAG and agents: current versions, MIT licences, short Python examples, paid-service costs and when to combine both.Oct 13, 2026Build
What Is MCP? Model Context Protocol Explained

What Is MCP? Model Context Protocol Explained

What MCP is, who maintains it, how hosts and servers work, and whether you need it. Includes MCP vs APIs, app support and security checks.Oct 13, 2026Build
Newsletter

One letter, every Sunday.Working systems, not hot takes.

Weekly. No spam. Unsubscribe anytime.