dspy-signatures

Defining input/output signatures for DSPy modules and language model tasks

DSPy Signatures

Scope: Signature syntax, field types, typed signatures, inline vs class-based Lines: ~380 Last Updated: 2025-10-25

When to Use This Skill

Activate this skill when:

Core Concepts

What are Signatures?

Definition: Signatures specify the input/output interface for a language model task

Purpose:

Key insight: Signatures are like function signatures, but for LM tasks

Signature Syntax

Three formats:

  1. String format: "input1, input2 -> output"
  1. Expanded string: "question: str, context: str -> answer: str, confidence: float"
  1. Class-based: Python class with dspy.InputField and dspy.OutputField

Field Types and Constraints

Supported types:

Field metadata:


Patterns

Pattern 1: Simple String Signatures

import dspy

# Basic question answering
signature = "question -> answer"

# Classification
signature = "text -> category"

# Summarization
signature = "document -> summary"

# Use with Predict module
qa = dspy.Predict("question -> answer")
result = qa(question="What is DSPy?")
print(result.answer)

When to use:

Pattern 2: Multi-Field Signatures

import dspy

# Multiple inputs
signature = "question, context -> answer"

# Multiple outputs
signature = "text -> category, confidence"

# Complex task
signature = "title, author, year -> summary, genre, rating"

# Example usage
classifier = dspy.Predict("text, hint -> label, confidence")
result = classifier(
    text="This is a great product!",
    hint="Classify sentiment as positive, negative, or neutral"
)
print(f"Label: {result.label}, Confidence: {result.confidence}")

When to use:

Pattern 3: Typed Signatures

import dspy

# Explicit type annotations
signature = "question: str, context: str -> answer: str, confidence: float"

# Using with module
rag = dspy.Predict("question: str, context: str -> answer: str")

# Types help DSPy format outputs correctly
result = rag(
    question="What is the capital of France?",
    context="France is a country in Europe. Paris is its capital."
)

When to use:

Pattern 4: Class-Based Signatures

import dspy

class QASignature(dspy.Signature):
    """Answer questions based on provided context."""

    # Input fields
    question = dspy.InputField(desc="User's question")
    context = dspy.InputField(desc="Relevant context for answering")

    # Output fields
    answer = dspy.OutputField(desc="Concise answer to the question")
    confidence = dspy.OutputField(
        desc="Confidence score between 0 and 1",
        prefix="Confidence:"
    )

# Use class-based signature
qa = dspy.ChainOfThought(QASignature)
result = qa(
    question="What is DSPy?",
    context="DSPy is a framework for programming language models."
)

print(result.answer)
print(result.confidence)

Benefits:

Pattern 5: Signatures with Hints and Constraints

import dspy

class SentimentAnalysis(dspy.Signature):
    """Analyze sentiment of text with confidence scoring."""

    text = dspy.InputField(desc="Text to analyze for sentiment")

    # Provide hint about expected values
    sentiment = dspy.OutputField(
        desc="Sentiment label: positive, negative, or neutral"
    )

    # Constrain output format
    score = dspy.OutputField(
        desc="Sentiment score from -1.0 (negative) to 1.0 (positive)",
        prefix="Score (between -1.0 and 1.0):"
    )

    explanation = dspy.OutputField(
        desc="Brief explanation of the sentiment classification"
    )

# Use signature
analyzer = dspy.Predict(SentimentAnalysis)
result = analyzer(text="This product exceeded my expectations!")

print(f"Sentiment: {result.sentiment}")
print(f"Score: {result.score}")
print(f"Explanation: {result.explanation}")

When to use:

Pattern 6: Signatures for Multi-Step Reasoning

import dspy

class ComplexQA(dspy.Signature):
    """Answer complex questions that require reasoning."""

    question = dspy.InputField(desc="Complex question requiring reasoning")
    context = dspy.InputField(desc="Background context", prefix="Context:")

    # Intermediate reasoning step
    reasoning = dspy.OutputField(
        desc="Step-by-step reasoning process",
        prefix="Let's think step by step:"
    )

    # Final answer
    answer = dspy.OutputField(desc="Final answer based on reasoning")

# Use with ChainOfThought module
cot = dspy.ChainOfThought(ComplexQA)
result = cot(
    question="If it takes 5 machines 5 minutes to make 5 widgets, how long would it take 100 machines to make 100 widgets?",
    context="Each machine works independently at a constant rate."
)

print("Reasoning:", result.reasoning)
print("Answer:", result.answer)

When to use:

Pattern 7: List-Based Signatures

import dspy

class MultipleChoice(dspy.Signature):
    """Select best answer from multiple choices."""

    question = dspy.InputField(desc="Question to answer")
    choices = dspy.InputField(desc="List of possible answers")

    selected = dspy.OutputField(desc="Selected answer from choices")
    reasoning = dspy.OutputField(desc="Why this answer was chosen")

# Usage
mc = dspy.Predict(MultipleChoice)
result = mc(
    question="What is the capital of France?",
    choices=["London", "Paris", "Berlin", "Madrid"]
)

print(f"Selected: {result.selected}")
print(f"Reasoning: {result.reasoning}")

When to use:

Pattern 8: Custom Formatting with Prefix

import dspy

class CodeReview(dspy.Signature):
    """Review code and provide structured feedback."""

    code = dspy.InputField(desc="Code to review")
    language = dspy.InputField(desc="Programming language")

    # Custom prefixes for clear output structure
    bugs = dspy.OutputField(
        desc="List of potential bugs",
        prefix="BUGS:"
    )

    improvements = dspy.OutputField(
        desc="Suggested improvements",
        prefix="IMPROVEMENTS:"
    )

    rating = dspy.OutputField(
        desc="Code quality rating (1-10)",
        prefix="RATING:"
    )

# Use signature
reviewer = dspy.ChainOfThought(CodeReview)
result = reviewer(
    code="def add(a,b): return a+b",
    language="Python"
)

Benefits:


Quick Reference

Signature Format Comparison

| Format | Syntax | Use Case | |--------|--------|----------| | Simple | "input -> output" | Prototyping, simple tasks | | Multi-field | "in1, in2 -> out1, out2" | Multiple inputs/outputs | | Typed | "in: str -> out: float" | Type validation | | Class-based | class Sig(dspy.Signature) | Production, complex tasks |

Common Field Types

# Text
question = dspy.InputField(desc="Question text")

# Numeric
score: float = dspy.OutputField(desc="Score from 0 to 1")

# Boolean
is_valid: bool = dspy.OutputField(desc="True if valid")

# List
options: list[str] = dspy.InputField(desc="List of choices")

Signature Best Practices

✅ DO: Use descriptive field names (question, not q)
✅ DO: Add field descriptions for clarity
✅ DO: Use class-based signatures for production
✅ DO: Include type hints for structured outputs
✅ DO: Provide examples in descriptions when needed

❌ DON'T: Use vague field names (input, output)
❌ DON'T: Omit descriptions for complex tasks
❌ DON'T: Mix too many unrelated outputs in one signature
❌ DON'T: Forget to specify expected output format

Quick Signature Templates

# Classification
"text -> category, confidence: float"

# QA
"question, context -> answer"

# Summarization
"document: str -> summary: str, key_points: list[str]"

# Sentiment
"text -> sentiment, score: float, explanation"

# Extraction
"text, entity_type -> entities: list[str]"

Anti-Patterns

Vague signatures: LM doesn't know what to do

# Bad
signature = "input -> output"

✅ Use descriptive names:

# Good
signature = "customer_review -> sentiment_label, confidence_score: float"

Missing field descriptions: LM guesses intent

# Bad
class BadSig(dspy.Signature):
    text = dspy.InputField()
    result = dspy.OutputField()

✅ Add clear descriptions:

# Good
class GoodSig(dspy.Signature):
    """Classify text into categories."""
    text = dspy.InputField(desc="Text to classify")
    category = dspy.OutputField(desc="Category: news, sports, or tech")

Too many outputs: Unfocused task

# Bad - asking for too much at once
signature = "text -> sentiment, category, summary, keywords, language, toxicity"

✅ Split into focused signatures:

# Good - focused tasks
classify_sig = "text -> category, confidence: float"
sentiment_sig = "text -> sentiment, score: float"

No type hints for structured data: Parsing issues

# Bad - LM might return text instead of number
signature = "text -> score"

✅ Specify types:

# Good
signature = "text -> score: float, max_score: int"

Related Skills


Last Updated: 2025-10-25 Format Version: 1.0 (Atomic)