08. DSPy Signatures
A DSPy signature specifies what fields a module should treat as input and what fields it should produce as output. This is the declarative contract between the user and the system. Signatures are not prompts—they describe behavior, not implementation.
The signature format uses field annotations:
class SummarizeDocument(dspy.Signature):
"""Summarize documents concisely for technical readers."""
document = dspy.InputField(desc="The full document text to summarize")
summary = dspy.OutputField(desc="3-5 sentence summary focusing on key findings")
confidence = dspy.OutputField(desc="Confidence score 0.0 to 1.0")
The desc argument provides hints to the compiler about what the content should be. These hints are extracted and used when generating prompts. Well-written descriptions improve compilation outcomes.
Signature inheritance allows extension:
# Base signature
class AnalyzeText(dspy.Signature):
text = dspy.InputField()
analysis = dspy.OutputField()
# Extended signature adds another capability
class AnalyzeTextWithSentiment(AnalyzeText):
sentiment = dspy.OutputField()
# Extended signature narrows scope
class AnalyzeTextBrief(AnalyzeText):
analysis = dspy.OutputField() # Same output field
# But compiler treats brief context differently based on context hints
The output of a signature is a namedtuple-like object with attributes matching the output field names. Accessing result.analysis returns the extracted value. This uniform structure enables easy composition: the output from one module becomes inputs to another.
# Example: Sentiment-aware topic extraction
class ExtractTopic(dspy.Signature):
"""Identify the main topic and subject of the input."""
text = dspy.InputField()
topic = dspy.OutputField()
topic_confidence = dspy.OutputField()
class AssessTopicValence(dspy.Signature):
"""Determine sentiment toward the topic."""
text = dspy.InputField()
topic = dspy.InputField()
valence = dspy.OutputField() # positive, negative, neutral
valence_explanation = dspy.OutputField()
# Compose: output of first feeds into second
topic_extractor = dspy.Predict(ExtractTopic)
valence_assessor = dspy.Predict(AssessTopicValence)
def full_analysis(text: str):
# Module 1: extract topic
topic_result = topic_extractor(text=text)
# Module 2: assess sentiment toward that topic
valence_result = valence_assessor(
text=text,
topic=topic_result.topic
)
return {
'topic': topic_result.topic,
'topic_confidence': topic_result.topic_confidence,
'valence': valence_result.valence,
'explanation': valence_result.valence_explanation
}
news_article = """
The newly announced QuantumChip processor achieves exceptional
energy efficiency according to benchmarks released today. The chip
uses 40% less power than previous generation while delivering
twice the computational throughput.
"""
result = full_analysis(news_article)
print(result)
A common misunderstanding: when extending a signature, the base specification doesn't carry over. class Extended(Base) creates an independent signature. The fields are copied, not inherited. This means extended signatures won't automatically call base implementations unless explicitly composed.
The signature compilation process extracts all desc values and assembles them into prompt context. If multiple modules use similar field names, the descriptions guide the model toward the right interpretation. A field named summary in a summarization context means something different than summary in a financial analysis context—DSPy disambiguates through descriptions.
Identify 3 different tasks that share field names but require different content interpretations. Write explicit descriptions for each that disambiguate the interpretation. Verify that a model using these descriptions produces different outputs for the same field name across contexts.