Writing our first DSPy program¶
In this tutorial we’re building a haiku-generating program, extending it in each section to introduce new DSPy concepts and capabilities.
Let’s start by writing the simplest version of our program and run it, then walk through everything DSPy is doing behind the scenes:
haiku_signature = "subject -> haiku"
haiku_generator = dspy.Predict(haiku_signature)
result = haiku_generator(subject="computer science")
print(result.haiku)
The first line specifies our Signature. Signature is a core concept in DSPy, and it’s how we define our task. Similar to function signatures in programming, a DSPy Signature describes the inputs a function accepts and the outputs it returns.
The simplest way to define a DSPy Signature is with a string of the form "inputs -> outputs". In our case, we want to provide a subject and get back a haiku.
Because we are programming with language models, the names of our variables matter. They both define the interface for our program and give the model a hint at our intent. If we change haiku to limerick, the model would note our cue and produce a limerick instead. Additionally, our program’s output would be accessible as result.limerick, rather than result.haiku.
To turn our Signature into a callable function, we use dspy.Predict. Predict is a kind of DSPy Module. If Signatures specify what we want, Modules define how we aim to achieve it. They implement a call-time strategy, manage the control flow, tools, and more.
Predict is the foundational Module. Let’s look at what happens when we call dspy.Predict(haiku_signature):
- The string,
"subject -> haiku"is parsed into a Signature class, with input and output fields defaulting to typestr. - A default instruction string is generated for the
Signatureinstance, in this case: “Given the fieldssubject, produce the fieldshaiku.” - A
Predictmodule is instantiated with thisSignature.
haiku_generator is now callable. Calling haiku_generator(subject="computer science") kicks off the following process:
- The DSPy settings are checked to ensure an
LMis configured. - An
Adapteris used to render theSignatureand its inputs into messages theLMcan consume. By default, this is theChatAdapter, but there are JSON, XML, and other variants to format your messages suitable for a givenLM. - The
ChatAdapterbuilds the prompt, which includes theSignatureinstructions, the field schema describing the inputs and outputs, formatting instructions, and the provided input (in this case, “computer science”). - The messages are sent to the
LM. Caching is enabled by default so identical calls return cached responses. - A response is returned, which the
ChatAdapterparses to extract the output fields. - The
Predictmodule returns aPredictionobject with accessible output fields. Callingresult.haikureturns the generated haiku. - The call is recorded in
LMhistory, which can be inspected later withdspy.inspect_history().
Running print(result.haiku) produces:
In just four lines we built an AI-powered program that reads like software and acts like a function.
DSPy manages all the prompting and templating. Call dspy.inspect_history(n=1) to take a look at the formatted prompt our program produced and the string the lm returned.
First, DSPy generated the system instructions:
Your input fields are:
1. `subject` (str):
Your output fields are:
1. `haiku` (str):
All interactions will be structured in the following way, with the appropriate values filled in.
[[ ## subject ## ]]
{subject}
[[ ## haiku ## ]]
{haiku}
[[ ## completed ## ]]
In adhering to this structure, your objective is:
Given the fields `subject`, produce the fields `haiku`.
The brackets and hashes style is how the default ChatAdapter structures the prompt, demarcating inputs and sections.
Next, the adapter templated our input into a user message:
[[ ## subject ## ]]
computer science
Respond with the corresponding output fields, starting with the field `[[ ## haiku ## ]]`, and then ending with the marker for `[[ ## completed ## ]]`.
DSPy sent both to the lm, which produced this response:
[[ ## haiku ## ]]
Silent code unfolds
Logic threads through hidden paths
Bugs bloom, then resolve
[[ ## completed ## ]]
In DSPy, to produce a prompt you compose a signature and the messages are written for you.
Next: Expanding signatures →