- Install DSRs
- Set up your language model
- Define a signature
- Create a module
- Execute the pipeline and see the results
1
Install DSRs
You can add DSRs to your project just like any other Rust crate, using either of the following two methods:Option 1: Add via Cargo commandThis creates an alias
dsrs for the dspy-rs crate which is the intended way to use it.Option 2: Add to Cargo.tomlWe need to install DSRS using the name
dspy-rs for now, because
dsrs is an already-published crate.
This may change in the future if the dsrs crate name is donated back or becomes available.2
Set up your language model
The first step in DSRs is to configure your Language Model (LM). DSRs supports
any LM supported via the
async-openai crate. You can define your LM
configuration using the builder pattern as follows.Once the LM instance is created, pass it to the configure function along with
a chat adapter to set the global LM and adapter for your application.ChatAdapter is the default adapter in DSRs and is responsible for converting
the instructions and the structure from your signature (defined in the next step)
into a prompt that the LM can follow to complete the task.- OpenAI
- Ollama
3
Define the task via a signature
A signature is a declarative definition of the inputs and outputs of your task. In defining a signature, you define a schema for your LM call.You can create your signature in DSRs in one of two ways: either using an
inline macro, or via an attribute macro. While the inline macro is more concise (and easier to get started), the attribute macro gives you more control over the signature.Let us define a signature using the inline macro:Inline MacroThe input fields are to the left of the
-> arrow, and the output fields are to the right. Multiple fields can be comma-separated, for e.g., (question: String, context: String) -> answer: String.4
Create a Module
Modules in DSRs are the building blocks of your application. They encapsulate the logic for a specific task and can be composed together to create complex workflows.A
Predictor is the simplest module in DSRs. It takes a signature and input data, and orchestrates the LM call to produce a prediction.5
Execute the Pipeline
LM calls in DSRs are asynchronous and return a future, so we need to use the The predictor takes an
tokio runtime to execute a function that uses a predictor.Example as input, which is a mapping from field names to values. The output would be similar to the following:Going Further
Now that you have built your first pipeline using DSRs, you can explore some of the more advanced features of the library.Create Signatures with Attribute Macros
If you want to exert more fine-grained control over the signature, you can define it by using an attribute macro over astruct. In the example below, notice how
- we use the attribute macro,
[#Signature]to indicate that the underlyingstructis a signature, and - the doc comment specifically asks the LM to answer the question like a pirate.
#[input] and #[output]
attributes, which is useful when you have multiple input and output fields, and
when you want to add descriptions to each field.
The advantage of this approach is that you can use doc comments at the
top of the struct, to specify important domain information or provide specific
instructions to the LM.
Let us use the above signature in our pipeline to answer questions like a pirate.
Build Modules for Complex Pipelines
Predictors are the simplest modules in DSRs. You can also compose more complex modules that define your own logic. Let us examine how to do that.Module trait for your own struct that
composes together one or more predictors and your own arbitrary logic. In this case, the AnswerQuestion module wraps a predictor that uses the QA signature defined earlier.
The forward method is async because it wraps a predictor call.
We can define the main function to use this module as follows:
- Create a new instance of the module (line 16).
- Call the
forwardmethod on the module (line 30).
