No history yet

DSPy Core Signatures

Beyond Manual Prompts

Traditional LLM development often feels like an art form. You spend hours crafting the perfect f-string, tweaking words, and adding special formatting, all to coax the right output from a specific model. This approach is brittle. When you change models or update your task, the entire prompt can break.

DSPy replaces this manual process with a more structured and reliable method: Signatures. A Signature is a declarative way to specify what a task should accomplish. Instead of telling the LLM how to format its thoughts, you define the inputs it will receive and the outputs you expect. It shifts the focus from prompt engineering to program design.

A Signature defines the logic of your task, not the specific wording of a prompt.

The simplest way to define a Signature is with an inline shorthand string. It's a concise way to map inputs to outputs for straightforward tasks. For example, a basic question-answering task can be defined with just a few characters.

import dspy

# Define a simple question-answering signature
qa_signature = "question -> answer"

# Use this signature within a DSPy module
qa_module = dspy.Predict(qa_signature)

# Run the module with an input that matches the signature
question = "What is the capital of France?"
prediction = qa_module(question=question)

print(prediction.answer)

Here, "question -> answer" tells DSPy that the module will take one input field named question and produce one output field named answer. The framework handles how to present this to any underlying LLM. You can expand this for more complex flows, like "context, question -> answer" for retrieval-augmented generation or "document -> summary, keywords" for a summarization task.

Structured Class Signatures

While inline signatures are great for simple tasks, complex pipelines need more structure and documentation. For this, DSPy provides class-based Signatures. This approach allows you to define each input and output field with detailed descriptions, creating a self-documenting and highly controllable component.

This is the core of DSPy's declarative programming philosophy. You describe the desired transformation of information, and DSPy's compiler figures out the best way to achieve it with the chosen LLM. It makes your code more readable and easier to maintain.

class SummarizeAndCategorize(dspy.Signature):
    """Summarize a given article and extract its primary category."""

    # Input Field: The full text of the article to be processed.
    article_text = dspy.InputField(desc="The complete text of the article.")

    # Output Field 1: A concise summary of the article.
    summary = dspy.OutputField(desc="A short, 2-3 sentence summary.")

    # Output Field 2: The main category of the article.
    category = dspy.OutputField(desc="e.g., Technology, Politics, Sports")

In this class, we inherit from dspy.Signature. The class docstring provides the high-level instruction for the task. It tells the LLM the overall goal: "Summarize a given article and extract its primary category."

Each field is then defined using dspy.InputField or dspy.OutputField. The desc parameter for each field provides a specific instruction for that piece of data. This separation is powerful. It decouples the overall task logic (in the docstring) from the specific formatting of inputs and outputs (in the field descriptions). This structure is what makes DSPy programs and robust.

By working with Signatures, you create modular, reusable, and testable components. This is essential for building a robust production where reliability and maintainability are critical. You can compose these signatures into larger programs, and DSPy's optimizers can then fine-tune the prompts, few-shot examples, and even the model weights to maximize performance on your specific data.

Quiz Questions 1/5

What is the primary purpose of a Signature in the DSPy framework?

Quiz Questions 2/5

Which of the following is a valid inline Signature for a retrieval-augmented generation (RAG) task?