Spoiler Alert! Try to complete the exercises on your own before viewing these solutions. Learning by doing is the most effective approach!
Inline Signature Basics
import dspy
from dotenv import load_dotenv
import os
# Load environment variables
load_dotenv()
# Configure DSPy
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# ============================================
# Exercise 1a: Simple Translation
# ============================================
translate_to_spanish = dspy.Predict("text -> spanish_translation")
result = translate_to_spanish(text="Hello, how are you today?")
print("1a) Translation:")
print(f" Input: Hello, how are you today?")
print(f" Spanish: {result.spanish_translation}")
print()
# ============================================
# Exercise 1b: Headline Generator
# ============================================
generate_headline = dspy.Predict("article -> headline")
article = """
Scientists at MIT have developed a new battery technology that could
triple the range of electric vehicles while cutting charging time in half.
The breakthrough uses a novel silicon-graphene composite that remains
stable over thousands of charge cycles.
"""
result = generate_headline(article=article)
print("1b) Headline Generator:")
print(f" Generated Headline: {result.headline}")
print()
# ============================================
# Exercise 1c: Fact Checker
# ============================================
fact_check = dspy.Predict("claim, context -> is_supported, explanation")
claim = "The Eiffel Tower is located in London."
context = "The Eiffel Tower is a famous landmark in Paris, France. It was built for the 1889 World's Fair."
result = fact_check(claim=claim, context=context)
print("1c) Fact Checker:")
print(f" Claim: {claim}")
print(f" Supported: {result.is_supported}")
print(f" Explanation: {result.explanation}")
Key Concepts
Inline signatures use the arrow syntax (->) to separate
inputs from outputs. Multiple inputs/outputs are comma-separated.
Your First Class-Based Signature
import dspy
from dotenv import load_dotenv
load_dotenv()
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# ============================================
# Class-based signature for Context QA
# ============================================
class ContextQA(dspy.Signature):
"""Answer questions accurately based on the provided context.
Only use information from the context to answer the question.
If the answer cannot be found in the context, say so."""
context: str = dspy.InputField(
desc="Background information containing the answer"
)
question: str = dspy.InputField(
desc="The question to answer based on the context"
)
answer: str = dspy.OutputField(
desc="A concise, accurate answer derived from the context"
)
# Create predictor
qa = dspy.Predict(ContextQA)
# Test with sample question
context = """
The Great Wall of China is one of the most impressive architectural
feats in history. Construction began during the 7th century BC, with
the most well-known sections built during the Ming Dynasty (1368-1644).
The wall stretches approximately 13,170 miles (21,196 kilometers) and
was primarily built to protect against invasions from the north.
"""
result = qa(
context=context,
question="How long is the Great Wall of China?"
)
print("Context QA Signature Test:")
print(f"Question: How long is the Great Wall of China?")
print(f"Answer: {result.answer}")
print()
# Test with question not in context
result2 = qa(
context=context,
question="What color is the Great Wall?"
)
print(f"Question: What color is the Great Wall?")
print(f"Answer: {result2.answer}")
Key Concepts
The docstring provides the main instruction. Field descriptions add specific guidance. Notice how we instruct the model to acknowledge when information isn't available.
Multi-Output Signature
import dspy
from typing import Literal
from dotenv import load_dotenv
load_dotenv()
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# ============================================
# Email Analysis Signature
# ============================================
class EmailAnalysis(dspy.Signature):
"""Analyze an email to extract key information and determine
the appropriate response priority."""
email_text: str = dspy.InputField(
desc="The full text of the email including sender and body"
)
subject: str = dspy.OutputField(
desc="Brief summary of what the email is about (5-10 words)"
)
sender_intent: Literal["request", "inform", "complaint", "question", "update", "invitation"] = dspy.OutputField(
desc="The primary intent of the sender"
)
urgency: Literal["low", "medium", "high"] = dspy.OutputField(
desc="How urgently this email needs attention"
)
action_required: bool = dspy.OutputField(
desc="Whether the recipient needs to take action"
)
suggested_response: str = dspy.OutputField(
desc="A brief suggested reply (1-2 sentences)"
)
# Create predictor
analyzer = dspy.Predict(EmailAnalysis)
# Test email
email = """
Hi Team,
The client meeting has been moved to tomorrow at 3 PM instead of Thursday.
Please confirm your availability ASAP as we need to finalize attendees.
This is critical - they're making a decision on our proposal next week.
Thanks,
Sarah
"""
result = analyzer(email_text=email)
print("Email Analysis Results:")
print("=" * 50)
print(f"Subject: {result.subject}")
print(f"Sender Intent: {result.sender_intent}")
print(f"Urgency: {result.urgency}")
print(f"Action Required: {result.action_required}")
print(f"Suggested Response: {result.suggested_response}")
Expected Output:
Email Analysis Results: ================================================== Subject: Client meeting rescheduled to tomorrow at 3 PM Sender Intent: request Urgency: high Action Required: True Suggested Response: I can confirm my availability for the meeting tomorrow at 3 PM.
Key Concepts
Using Literal types constrains outputs to specific values.
Combining multiple output types (string, bool, Literal) gives
structured, actionable results.
Using Literal Types
import dspy
from typing import Literal
from dotenv import load_dotenv
load_dotenv()
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# ============================================
# Content Moderation Signature
# ============================================
class ContentModeration(dspy.Signature):
"""Analyze content for potential policy violations and
determine if human review is needed."""
content: str = dspy.InputField(
desc="The text content to moderate"
)
category: Literal["safe", "mild", "sensitive", "harmful"] = dspy.OutputField(
desc="Overall safety category of the content"
)
flags: list[str] = dspy.OutputField(
desc="List of detected issues: violence, hate_speech, adult_content, spam, misinformation (empty list if none)"
)
confidence: float = dspy.OutputField(
desc="Confidence in classification from 0.0 to 1.0"
)
requires_review: bool = dspy.OutputField(
desc="True if human review is recommended"
)
explanation: str = dspy.OutputField(
desc="Brief reasoning for the classification"
)
# Create predictor
moderator = dspy.Predict(ContentModeration)
# Test cases
test_cases = [
"Check out this amazing recipe for chocolate cake! Perfect for beginners.",
"Breaking: Aliens confirmed to live among us! Government hiding the truth!",
"Apple announces new iPhone with improved camera and battery life."
]
print("Content Moderation Results:")
print("=" * 60)
for i, content in enumerate(test_cases, 1):
result = moderator(content=content)
print(f"\nTest Case {i}:")
print(f"Content: {content[:50]}...")
print(f"Category: {result.category}")
print(f"Flags: {result.flags}")
print(f"Confidence: {result.confidence}")
print(f"Requires Review: {result.requires_review}")
print(f"Explanation: {result.explanation}")
print("-" * 40)
Key Concepts
The flags field as list[str] allows flexible
detection of multiple issues. The requires_review boolean
enables automated escalation decisions.
Signature Collection for a Blog Platform
import dspy
from typing import Literal
from dotenv import load_dotenv
load_dotenv()
lm = dspy.LM("openai/gpt-4o-mini")
dspy.configure(lm=lm)
# ============================================
# Signature 1: Generate Blog Post
# ============================================
class GenerateBlogPost(dspy.Signature):
"""Generate a complete, engaging blog post from a topic and key points."""
topic: str = dspy.InputField(
desc="The main topic or title idea for the blog post"
)
key_points: list[str] = dspy.InputField(
desc="Main points to cover in the post"
)
tone: Literal["professional", "casual", "educational", "entertaining"] = dspy.InputField(
desc="The desired writing tone"
)
title: str = dspy.OutputField(
desc="Engaging blog post title"
)
introduction: str = dspy.OutputField(
desc="Hook paragraph that captures reader attention"
)
body: str = dspy.OutputField(
desc="Main content with 3-4 sections, using markdown headers"
)
conclusion: str = dspy.OutputField(
desc="Summary paragraph with call-to-action"
)
# ============================================
# Signature 2: SEO Optimizer
# ============================================
class SEOOptimizer(dspy.Signature):
"""Generate SEO metadata for a blog post to improve search visibility."""
blog_post: str = dspy.InputField(
desc="The complete blog post content"
)
target_keyword: str = dspy.InputField(
desc="Primary keyword to optimize for"
)
meta_title: str = dspy.OutputField(
desc="SEO-optimized title (50-60 characters)"
)
meta_description: str = dspy.OutputField(
desc="Compelling meta description (150-160 characters)"
)
keywords: list[str] = dspy.OutputField(
desc="List of 5-8 relevant keywords"
)
url_slug: str = dspy.OutputField(
desc="URL-friendly slug (lowercase, hyphens, no special chars)"
)
# ============================================
# Signature 3: Social Media Posts
# ============================================
class SocialMediaPosts(dspy.Signature):
"""Create platform-specific social media content from a blog post."""
blog_post: str = dspy.InputField(
desc="The blog post to create social content from"
)
blog_title: str = dspy.InputField(
desc="The title of the blog post"
)
tweet: str = dspy.OutputField(
desc="Twitter/X post (max 280 characters), include relevant hashtags"
)
linkedin_post: str = dspy.OutputField(
desc="Professional LinkedIn post (100-200 words)"
)
instagram_caption: str = dspy.OutputField(
desc="Engaging Instagram caption with emojis and hashtags"
)
# ============================================
# Signature 4: Content Analyzer
# ============================================
class ContentAnalyzer(dspy.Signature):
"""Analyze a blog post for quality and provide improvement suggestions."""
blog_post: str = dspy.InputField(
desc="The complete blog post to analyze"
)
reading_time: int = dspy.OutputField(
desc="Estimated reading time in minutes"
)
difficulty_level: Literal["beginner", "intermediate", "advanced"] = dspy.OutputField(
desc="Content difficulty level"
)
target_audience: str = dspy.OutputField(
desc="Description of ideal reader"
)
topics_covered: list[str] = dspy.OutputField(
desc="List of main topics addressed"
)
improvement_suggestions: list[str] = dspy.OutputField(
desc="3-5 specific suggestions to improve the post"
)
# ============================================
# Complete Pipeline Demo
# ============================================
def run_content_pipeline(topic: str, key_points: list[str]):
"""Run all four signatures in sequence."""
print("🚀 Starting Content Pipeline")
print("=" * 60)
# Step 1: Generate blog post
print("\n📝 Step 1: Generating Blog Post...")
generator = dspy.Predict(GenerateBlogPost)
post_result = generator(
topic=topic,
key_points=key_points,
tone="educational"
)
full_post = f"# {post_result.title}\n\n{post_result.introduction}\n\n{post_result.body}\n\n{post_result.conclusion}"
print(f" ✓ Title: {post_result.title}")
# Step 2: SEO Optimization
print("\n🔍 Step 2: Optimizing for SEO...")
seo_optimizer = dspy.Predict(SEOOptimizer)
seo_result = seo_optimizer(
blog_post=full_post,
target_keyword=topic
)
print(f" ✓ Meta Title: {seo_result.meta_title}")
print(f" ✓ URL Slug: {seo_result.url_slug}")
print(f" ✓ Keywords: {seo_result.keywords}")
# Step 3: Social Media Content
print("\n📱 Step 3: Creating Social Media Posts...")
social_creator = dspy.Predict(SocialMediaPosts)
social_result = social_creator(
blog_post=full_post,
blog_title=post_result.title
)
print(f" ✓ Tweet: {social_result.tweet[:100]}...")
# Step 4: Content Analysis
print("\n📊 Step 4: Analyzing Content Quality...")
analyzer = dspy.Predict(ContentAnalyzer)
analysis_result = analyzer(blog_post=full_post)
print(f" ✓ Reading Time: {analysis_result.reading_time} min")
print(f" ✓ Difficulty: {analysis_result.difficulty_level}")
print(f" ✓ Target Audience: {analysis_result.target_audience}")
print("\n" + "=" * 60)
print("✅ Pipeline Complete!")
return {
"post": post_result,
"seo": seo_result,
"social": social_result,
"analysis": analysis_result
}
# Run the pipeline
if __name__ == "__main__":
results = run_content_pipeline(
topic="Getting Started with Python",
key_points=[
"Why Python is great for beginners",
"Setting up your development environment",
"Writing your first Python program",
"Next steps and resources"
]
)
Key Concepts
This solution demonstrates: (1) Creating a family of related signatures, (2) Using outputs from one signature as inputs to another, (3) Building a complete content pipeline. Each signature is focused on a single task while working together as a system.
📝 Chapter Summary
In this chapter, you learned:
Congratulations!
You've completed Chapter 2: Signatures! You now know how to define clear, effective task specifications.