Chapter 2 · Section 7

Solutions

Complete solutions with explanations for all Chapter 2 exercises.

5 Solutions
⚠️

Spoiler Alert! Try to complete the exercises on your own before viewing these solutions. Learning by doing is the most effective approach!

Solution 1 ⭐ Beginner

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.

Solution 2 ⭐ Beginner

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.

Solution 3 ⭐⭐ Intermediate

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.

Solution 4 ⭐⭐ Intermediate

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.

Solution 5 ⭐⭐⭐ Advanced

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:

Signatures define the contract between inputs and outputs

Inline signatures are great for quick prototyping

Class-based signatures provide full control for production

Field types and descriptions shape LM outputs precisely

Literal types constrain outputs to specific values

🎉

Congratulations!

You've completed Chapter 2: Signatures! You now know how to define clear, effective task specifications.