๐ฏ What We'll Build
We'll create a program that:
- Takes a question as input
- Uses a language model to generate an answer
- Returns the answer
This is the "Hello World" of DSPy!
๐ The Complete Program
Here's the full program. Don't worry if you don't understand everything yetโwe'll break it down step by step.
"""
Your First DSPy Program
A simple question-answering application
"""
import os
from dotenv import load_dotenv
import dspy
# Load environment variables
load_dotenv()
def main():
# Step 1: Configure the language model
lm = dspy.LM(
model="openai/gpt-4o-mini",
api_key=os.getenv("OPENAI_API_KEY")
)
dspy.configure(lm=lm)
# Step 2: Define the task signature
class QuestionAnswer(dspy.Signature):
"""Answer questions with factual information."""
question: str = dspy.InputField()
answer: str = dspy.OutputField()
# Step 3: Create a predictor
qa = dspy.Predict(QuestionAnswer)
# Step 4: Use it!
question = "What is the capital of France?"
result = qa(question=question)
# Step 5: Display the result
print(f"Question: {question}")
print(f"Answer: {result.answer}")
if __name__ == "__main__":
main()
๐ Step-by-Step Breakdown
Configure the Language Model
lm = dspy.LM(
model="openai/gpt-4o-mini",
api_key=os.getenv("OPENAI_API_KEY")
)
dspy.configure(lm=lm)
What's happening:
dspy.LM()creates a language model instance- We specify which model to use (
gpt-4o-mini) - We provide the API key from environment variables
dspy.configure()sets this as the default LM
Think of this as: Setting up your "engine" that powers all DSPy operations.
Define the Task Signature
class QuestionAnswer(dspy.Signature):
"""Answer questions with factual information."""
question: str = dspy.InputField()
answer: str = dspy.OutputField()
What's happening:
- We create a class that inherits from
dspy.Signature - The docstring describes what the task does
questionis an input field (what we provide)answeris an output field (what we want back)
Think of this as: A contract that says "Give me a question, I'll give you an answer."
Create a Predictor
qa = dspy.Predict(QuestionAnswer)
What's happening:
dspy.Predictis a module that makes predictions- We pass our
QuestionAnswersignature to it - This creates a predictor that can answer questions
Think of this as: Creating a function that implements our contract.
Use It!
question = "What is the capital of France?"
result = qa(question=question)
What's happening:
- We call our predictor like a function
- We pass the question as a keyword argument
- DSPy automatically generates a prompt, calls the LM, and returns the result
Think of this as: Just using the function we created!
Display the Result
print(f"Answer: {result.answer}")
What's happening:
resultis a prediction object- We access the
answerfield (from our signature) - DSPy has extracted this from the LM's response
Think of this as: Getting the output from our function.
โถ๏ธ Running Your Program
1. Save the File
Save the code above as hello_dspy.py.
2. Ensure Your Environment is Ready
# Activate virtual environment
source venv/bin/activate
# Check .env file exists with API key
cat .env
3. Run It!
python hello_dspy.py
4. Expected Output
Question: What is the capital of France? Answer: Paris
๐ฎ What's Happening Behind the Scenes?
When you run this program, DSPy:
You didn't write the promptโDSPy did it for you!
๐งช Experimenting
Try modifying the program to explore DSPy:
Experiment 1: Different Questions
questions = [
"What is the capital of France?",
"Who invented the telephone?",
"What is 25 multiplied by 4?",
]
for question in questions:
result = qa(question=question)
print(f"Q: {question}")
print(f"A: {result.answer}\n")
Experiment 2: Add Field Descriptions
class QuestionAnswer(dspy.Signature):
"""Answer questions with factual information."""
question: str = dspy.InputField()
answer: str = dspy.OutputField(desc="concise answer in one sentence")
The description helps guide the model's response format!
Experiment 3: Multiple Output Fields
class DetailedQA(dspy.Signature):
"""Answer questions with details."""
question: str = dspy.InputField()
answer: str = dspy.OutputField()
confidence: str = dspy.OutputField(desc="high, medium, or low")
explanation: str = dspy.OutputField(desc="brief reasoning")
Experiment 4: Use Chain of Thought
# Change one line!
qa = dspy.ChainOfThought(QuestionAnswer)
# Now it shows reasoning
result = qa(question="What is the capital of France?")
print(f"Reasoning: {result.rationale}")
print(f"Answer: {result.answer}")
Just one line change adds step-by-step reasoning!
๐ Understanding the Code Structure
Typical DSPy program structure:
# 1. Imports
import dspy
from dotenv import load_dotenv
# 2. Configuration
load_dotenv()
lm = dspy.LM(...)
dspy.configure(lm=lm)
# 3. Signature Definition
class MyTask(dspy.Signature):
input_field: str = dspy.InputField()
output_field: str = dspy.OutputField()
# 4. Module Creation
module = dspy.Predict(MyTask)
# 5. Usage
result = module(input_field="...")
print(result.output_field)
This pattern will be consistent across all DSPy programs!
๐ Summary
You've learned:
- โ How to configure DSPy with a language model
- โ How to define a signature (task specification)
- โ
How to create a predictor with
dspy.Predict - โ How to use the predictor to generate results
- โ The basic structure of DSPy programs
Key concepts:
- Signatures define inputs and outputs
- Modules (like
Predict) implement the behavior - Configuration sets up the language model
- Results are structured objects with named fields