Install
$ agentstack add skill-bobmatnyc-claude-mpm-skills-dspy ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ● Filesystem access Used
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ● Dynamic code execution Used
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
DSPy Framework
progressivedisclosure: entrypoint: summary: "Declarative framework for automatic prompt optimization treating prompts as code" whentouse:
- "When optimizing prompts systematically with evaluation data"
- "When building production LLM systems requiring accuracy improvements"
- "When implementing RAG, classification, or structured extraction tasks"
- "When version-controlled, reproducible prompts are needed"
quick_start:
- "pip install dspy-ai"
- "Define signature: class QA(dspy.Signature): question = dspy.InputField(); answer = dspy.OutputField()"
- "Create module: qa = dspy.ChainOfThought(QA)"
- "Optimize: optimizer.compile(qa, trainset=examples)"
token_estimate: entry: 75 full: 5500 ---
Core Philosophy
DSPy (Declarative Self-improving Python) shifts focus from manual prompt engineering to programming language models. Treat prompts as code with:
- Declarative signatures defining inputs/outputs
- Automatic optimization via compilers
- Version control and systematic testing
- Reproducible results across model changes
Key Principle: Don't write prompts manually—define task specifications and let DSPy optimize them.
Core Concepts
Signatures: Defining Task Interfaces
Signatures specify what your LM module should do (inputs → outputs) without saying how.
Basic Signature:
import dspy
# Inline signature (quick)
qa_module = dspy.ChainOfThought("question -> answer")
# Class-based signature (recommended for production)
class QuestionAnswer(dspy.Signature):
"""Answer questions with short factual answers."""
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
# Use signature
qa = dspy.ChainOfThought(QuestionAnswer)
response = qa(question="What is the capital of France?")
print(response.answer) # "Paris"
Advanced Signatures with Type Hints:
from typing import List
class DocumentSummary(dspy.Signature):
"""Generate concise document summaries."""
document: str = dspy.InputField(desc="Full text to summarize")
key_points: List[str] = dspy.OutputField(desc="3-5 bullet points")
summary: str = dspy.OutputField(desc="2-3 sentence summary")
sentiment: str = dspy.OutputField(desc="positive, negative, or neutral")
# Type hints provide strong typing and validation
summarizer = dspy.ChainOfThought(DocumentSummary)
result = summarizer(document="Long document text...")
Field Descriptions:
- Short, descriptive phrases (not full sentences)
- Examples:
desc="often between 1 and 5 words",desc="JSON format" - Used by optimizers to improve prompt quality
Modules: Building Blocks
Modules are DSPy's reasoning patterns—replacements for manual prompt engineering.
ChainOfThought (CoT):
# Zero-shot reasoning
class Reasoning(dspy.Signature):
"""Solve complex problems step by step."""
problem = dspy.InputField()
solution = dspy.OutputField()
cot = dspy.ChainOfThought(Reasoning)
result = cot(problem="Roger has 5 tennis balls. He buys 2 cans of 3 balls each. How many total?")
print(result.solution) # Includes reasoning steps automatically
print(result.rationale) # Access the chain-of-thought reasoning
Retrieve Module (RAG):
class RAGSignature(dspy.Signature):
"""Answer questions using retrieved context."""
question = dspy.InputField()
context = dspy.InputField(desc="relevant passages")
answer = dspy.OutputField(desc="answer based on context")
# Combine retrieval + reasoning
retriever = dspy.Retrieve(k=3) # Retrieve top 3 passages
rag = dspy.ChainOfThought(RAGSignature)
# Use in pipeline
question = "What is quantum entanglement?"
context = retriever(question).passages
answer = rag(question=question, context=context)
ReAct (Reasoning + Acting):
class ResearchTask(dspy.Signature):
"""Research a topic using tools."""
topic = dspy.InputField()
findings = dspy.OutputField()
# ReAct interleaves reasoning with tool calls
react = dspy.ReAct(ResearchTask, tools=[web_search, calculator])
result = react(topic="Apple stock price change last month")
# Automatically uses tools when needed
ProgramOfThought:
# Generate and execute Python code
class MathProblem(dspy.Signature):
"""Solve math problems by writing Python code."""
problem = dspy.InputField()
code = dspy.OutputField(desc="Python code to solve problem")
result = dspy.OutputField(desc="final numerical answer")
pot = dspy.ProgramOfThought(MathProblem)
answer = pot(problem="Calculate compound interest on $1000 at 5% for 10 years")
Custom Modules:
class MultiStepRAG(dspy.Module):
"""Custom module combining retrieval and reasoning."""
def __init__(self, num_passages=3):
super().__init__()
self.retrieve = dspy.Retrieve(k=num_passages)
self.generate = dspy.ChainOfThought("context, question -> answer")
def forward(self, question):
# Retrieve relevant passages
context = self.retrieve(question).passages
# Generate answer with context
prediction = self.generate(context=context, question=question)
# Return with metadata
return dspy.Prediction(
answer=prediction.answer,
context=context,
rationale=prediction.rationale
)
# Use custom module
rag = MultiStepRAG(num_passages=5)
optimized_rag = optimizer.compile(rag, trainset=examples)
Optimizers: Automatic Prompt Improvement
Optimizers compile your high-level program into optimized prompts or fine-tuned weights.
BootstrapFewShot
Best For: Small datasets (10-50 examples), quick optimization Optimizes: Few-shot examples only
from dspy.teleprompt import BootstrapFewShot
# Define metric function
def accuracy_metric(example, prediction, trace=None):
"""Evaluate prediction correctness."""
return example.answer.lower() == prediction.answer.lower()
# Configure optimizer
optimizer = BootstrapFewShot(
metric=accuracy_metric,
max_bootstrapped_demos=4, # Max examples to bootstrap
max_labeled_demos=16, # Max labeled examples to consider
max_rounds=1, # Bootstrapping rounds
max_errors=10 # Stop after N errors
)
# Training examples
trainset = [
dspy.Example(question="What is 2+2?", answer="4").with_inputs("question"),
dspy.Example(question="Capital of France?", answer="Paris").with_inputs("question"),
# ... more examples
]
# Compile program
qa_module = dspy.ChainOfThought("question -> answer")
optimized_qa = optimizer.compile(
student=qa_module,
trainset=trainset
)
# Save optimized program
optimized_qa.save("qa_optimized.json")
How It Works:
- Uses your program to generate outputs on training data
- Filters successful traces using your metric
- Selects representative examples as demonstrations
- Returns optimized program with best few-shot examples
BootstrapFewShotWithRandomSearch
Best For: Medium datasets (50-300 examples), better exploration Optimizes: Few-shot examples with candidate exploration
from dspy.teleprompt import BootstrapFewShotWithRandomSearch
config = dict(
max_bootstrapped_demos=4,
max_labeled_demos=4,
num_candidate_programs=10, # Explore 10 candidate programs
num_threads=4 # Parallel optimization
)
optimizer = BootstrapFewShotWithRandomSearch(
metric=accuracy_metric,
**config
)
optimized_program = optimizer.compile(
qa_module,
trainset=training_examples,
valset=validation_examples # Optional validation set
)
# Compare candidates
print(f"Best program score: {optimizer.best_score}")
Advantage: Explores multiple candidate programs in parallel, selecting best performer via random search.
MIPROv2 (State-of-the-Art 2025)
Best For: Large datasets (300+ examples), production systems Optimizes: Instructions AND few-shot examples jointly via Bayesian optimization
import dspy
from dspy.teleprompt import MIPROv2
# Initialize language model
lm = dspy.LM('openai/gpt-4o-mini', api_key='YOUR_API_KEY')
dspy.configure(lm=lm)
# Define comprehensive metric
def quality_metric(example, prediction, trace=None):
"""Multi-dimensional quality scoring."""
correct = example.answer.lower() in prediction.answer.lower()
reasonable_length = 10 20
# Weighted composite score
score = (
correct * 1.0 +
reasonable_length * 0.2 +
has_reasoning * 0.3
)
return score / 1.5 # Normalize to [0, 1]
# Initialize MIPROv2 with auto-configuration
teleprompter = MIPROv2(
metric=quality_metric,
auto="medium", # Options: "light", "medium", "heavy"
num_candidates=10, # Number of instruction candidates to explore
init_temperature=1.0 # Temperature for instruction generation
)
# Optimize program
optimized_program = teleprompter.compile(
dspy.ChainOfThought("question -> answer"),
trainset=training_examples,
num_trials=100, # Bayesian optimization trials
max_bootstrapped_demos=4,
max_labeled_demos=8
)
# Save for production
optimized_program.save("production_qa_model.json")
MIPROv2 Auto-Configuration Modes:
light: Fast optimization, ~20 trials, best for iteration (15-30 min)medium: Balanced optimization, ~50 trials, recommended default (30-60 min)heavy: Exhaustive search, ~100+ trials, highest quality (1-3 hours)
How MIPROv2 Works:
- Bootstrap Candidates: Generates few-shot example candidates from training data
- Propose Instructions: Creates instruction variations grounded in task dynamics
- Bayesian Optimization: Uses surrogate model to find optimal instruction + example combinations
- Joint Optimization: Optimizes both components together (not separately) for synergy
Performance Gains (2025 Study):
- Prompt Evaluation: +38.5% accuracy (46.2% → 64.0%)
- Guardrail Enforcement: +16.9% accuracy (72.1% → 84.3%)
- Code Generation: +21.9% accuracy (58.4% → 71.2%)
- Hallucination Detection: +20.8% accuracy (65.8% → 79.5%)
- Agent Routing: +18.5% accuracy (69.3% → 82.1%)
KNN Few-Shot Selector
Best For: Dynamic example selection based on query similarity
from dspy.teleprompt import KNNFewShot
# Requires embeddings for examples
knn_optimizer = KNNFewShot(
k=3, # Select 3 most similar examples
trainset=training_examples
)
optimized_program = knn_optimizer.compile(qa_module)
# Automatically selects relevant examples at inference time
# Math query → retrieves math examples
# Geography query → retrieves geography examples
SignatureOptimizer
Best For: Optimizing signature descriptions and field specifications
from dspy.teleprompt import SignatureOptimizer
sig_optimizer = SignatureOptimizer(
metric=accuracy_metric,
breadth=10, # Number of variations to generate
depth=3 # Optimization iterations
)
optimized_signature = sig_optimizer.compile(
initial_signature=QuestionAnswer,
trainset=trainset
)
# Use optimized signature
qa = dspy.ChainOfThought(optimized_signature)
Sequential Optimization Strategy
Combine optimizers for best results:
# Step 1: Bootstrap few-shot examples (fast)
bootstrap = dspy.BootstrapFewShot(metric=accuracy_metric)
bootstrapped_program = bootstrap.compile(qa_module, trainset=train_examples)
# Step 2: Optimize instructions with MIPRO (comprehensive)
mipro = dspy.MIPROv2(metric=accuracy_metric, auto="medium")
final_program = mipro.compile(
bootstrapped_program,
trainset=train_examples,
num_trials=50
)
# Step 3: Fine-tune signature descriptions
sig_optimizer = dspy.SignatureOptimizer(metric=accuracy_metric)
production_program = sig_optimizer.compile(final_program, trainset=train_examples)
# Save production model
production_program.save("production_optimized.json")
Teleprompters: Compilation Pipelines
Teleprompters orchestrate the optimization process (legacy term for "optimizers").
Custom Teleprompter:
class CustomTeleprompter:
"""Custom optimization pipeline."""
def __init__(self, metric):
self.metric = metric
def compile(self, student, trainset, valset=None):
# Stage 1: Bootstrap examples
bootstrap = BootstrapFewShot(metric=self.metric)
stage1 = bootstrap.compile(student, trainset=trainset)
# Stage 2: Optimize instructions
mipro = MIPROv2(metric=self.metric, auto="light")
stage2 = mipro.compile(stage1, trainset=trainset)
# Stage 3: Validate on held-out set
if valset:
score = self._evaluate(stage2, valset)
print(f"Validation score: {score:.2%}")
return stage2
def _evaluate(self, program, dataset):
correct = 0
for example in dataset:
prediction = program(**example.inputs())
if self.metric(example, prediction):
correct += 1
return correct / len(dataset)
# Use custom teleprompter
custom_optimizer = CustomTeleprompter(metric=accuracy_metric)
optimized = custom_optimizer.compile(
student=qa_module,
trainset=train_examples,
valset=val_examples
)
Metrics and Evaluation
Custom Metrics
Binary Accuracy:
def exact_match(example, prediction, trace=None):
"""Exact match metric."""
return example.answer.lower().strip() == prediction.answer.lower().strip()
Fuzzy Matching:
from difflib import SequenceMatcher
def fuzzy_match(example, prediction, trace=None):
"""Fuzzy string matching."""
similarity = SequenceMatcher(
None,
example.answer.lower(),
prediction.answer.lower()
).ratio()
return similarity > 0.8 # 80% similarity threshold
Multi-Criteria:
def comprehensive_metric(example, prediction, trace=None):
"""Evaluate on multiple criteria."""
# Correctness
correct = example.answer.lower() in prediction.answer.lower()
# Length appropriateness
length_ok = 10 30
)
# Citation quality (if RAG)
has_citations = (
hasattr(prediction, 'context') and
len(prediction.context) > 0
)
# Composite score
score = sum([
correct * 1.0,
length_ok * 0.2,
has_reasoning * 0.3,
has_citations * 0.2
]) / 1.7
return score
LLM-as-Judge:
def llm_judge_metric(example, prediction, trace=None):
"""Use LLM to evaluate quality."""
judge_prompt = f"""
Question: {example.question}
Expected Answer: {example.answer}
Predicted Answer: {prediction.answer}
Evaluate the predicted answer on a scale of 0-10 for:
1. Correctness
2. Completeness
3. Clarity
Return only a number 0-10.
"""
judge_lm = dspy.LM('openai/gpt-4o-mini')
response = judge_lm(judge_prompt)
score = float(response.strip()) / 10.0
return score > 0.7 # Pass if score > 7/10
Evaluation Pipeline
class Evaluator:
"""Comprehensive evaluation system."""
def __init__(self, program, metrics):
self.program = program
self.metrics = metrics
def evaluate(self, dataset, verbose=True):
"""Evaluate program on dataset."""
results = {name: [] for name in self.metrics.keys()}
for example in dataset:
prediction = self.program(**example.inputs())
for metric_name, metric_fn in self.metrics.items():
score = metric_fn(example, prediction)
results[metric_name].append(score)
# Aggregate results
aggregated = {
name: sum(scores) / len(scores)
for name, scores in results.items()
}
if verbose:
print("\nEvaluation Results:")
…
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [bobmatnyc](https://github.com/bobmatnyc)
- **Source:** [bobmatnyc/claude-mpm-skills](https://github.com/bobmatnyc/claude-mpm-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.