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
Adapterrenders theSignatureand its inputs into messages theLMcan consume. The default isXMLAdapter. - The
XMLAdapterbuilds 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
XMLAdapterparses 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):
Output format (replace placeholders):
<haiku>
{haiku}
</haiku>
In adhering to this structure, your objective is:
Given the fields `subject`, produce the fields `haiku`.
XMLAdapter uses tags to delimit fields.
Next, the adapter templated our input into a user message:
<subject>
computer science
</subject>
Output format (replace placeholders):
<haiku>
{haiku}
</haiku>
Use this format for text outputs. Put field tags on separate lines. Write string values literally, without XML or JSON escaping.
DSPy sent both to the lm, which produced this response:
In DSPy, to produce a prompt you compose a signature and the messages are written for you.
Next: Expanding signatures →