0Pricing
AI Prompt Engineering · Lesson

Defining Signatures and Modules

Signature syntax, ChainOfThought, ReAct, and custom DSPy modules.

Defining Signatures and Modules is a free AI Prompt Engineering lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the AI Prompt Engineering learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Signatures Are Typed Contracts

A DSPy signature is a Python class that declares the inputs and outputs of a reasoning step. Think of it as a typed function contract for your LLM call.

The docstring becomes the task description. Field annotations tell DSPy what to produce. You never write the actual prompt text — DSPy derives it from this declaration.

Defining a Basic Signature

A minimal signature subclasses dspy.Signature and annotates fields as InputField or OutputField. The class docstring provides the task instruction.

import dspy

class QASignature(dspy.Signature):
    """Answer the question based on the given context."""
    context: str = dspy.InputField(desc='Relevant background text')
    question: str = dspy.InputField(desc='The question to answer')
    answer: str = dspy.OutputField(desc='A concise answer')

# Inspect what DSPy sees
print(QASignature.instructions)  # The docstring
print(list(QASignature.input_fields.keys()))   # ['context', 'question']
print(list(QASignature.output_fields.keys()))  # ['answer']

Multiple Output Fields

Signatures can have multiple output fields. DSPy will prompt the model to produce all of them in a single call. This is useful for extracting structured information.

import dspy

class EntityExtraction(dspy.Signature):
    """Extract named entities from the text."""
    text: str = dspy.InputField()
    people: list[str] = dspy.OutputField(desc='List of person names mentioned')
    organizations: list[str] = dspy.OutputField(desc='List of organization names')
    locations: list[str] = dspy.OutputField(desc='List of place names')

extractor = dspy.Predict(EntityExtraction)
result = extractor(text='Elon Musk founded SpaceX in Hawthorne, California.')
print(result.people, result.organizations, result.locations)

The Predict Module

dspy.Predict is the simplest module. It takes a signature and directly asks the LM to produce the output. No reasoning scaffolding is added — just a structured prompt matching the signature.

Use Predict when the task is straightforward and doesn't require explicit reasoning steps.

import dspy

class Classify(dspy.Signature):
    """Classify the sentiment of the review."""
    review: str = dspy.InputField()
    sentiment: str = dspy.OutputField(desc='positive, negative, or neutral')

# Predict wraps the signature with a direct prompt
classifier = dspy.Predict(Classify)
result = classifier(review='The food was amazing and the service was excellent!')
print(result.sentiment)  # positive

The ChainOfThought Module

dspy.ChainOfThought augments the signature with an intermediate reasoning field. The model first writes out its reasoning, then produces the final answer.

This consistently improves accuracy on multi-step problems without you writing any chain-of-thought prompt instructions.

import dspy

class MathSolver(dspy.Signature):
    """Solve the math problem."""
    problem: str = dspy.InputField()
    answer: str = dspy.OutputField(desc='The numerical answer')

# ChainOfThought adds a 'reasoning' step automatically
solver = dspy.ChainOfThought(MathSolver)
result = solver(problem='If a train travels 60 mph for 2.5 hours, how far does it go?')
print(result.reasoning)  # Step-by-step reasoning
print(result.answer)     # 150 miles

The ReAct Module

dspy.ReAct implements the Reason + Act loop. The model alternates between reasoning steps and tool calls, making it ideal for agents that need to search, calculate, or fetch data.

You provide a list of tools (Python functions with docstrings), and DSPy handles the interleaving automatically.

import dspy

def search_web(query: str) -> str:
    """Search the web and return relevant results."""
    # In production, call a real search API
    return f'Search results for: {query}'

def calculate(expression: str) -> str:
    """Evaluate a mathematical expression."""
    return str(eval(expression))

class ResearchQA(dspy.Signature):
    """Answer the question using web search and calculation as needed."""
    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

agent = dspy.ReAct(ResearchQA, tools=[search_web, calculate])
result = agent(question='What is 15% of 847?')
print(result.answer)

Composing Modules into Programs

Real power comes from composing modules into multi-step programs. Subclass dspy.Module, define sub-modules in __init__, and implement forward() to wire them together.

import dspy

class RetrieveAndAnswer(dspy.Module):
    def __init__(self):
        super().__init__()
        self.retrieve = dspy.Retrieve(k=3)  # Retrieves top-3 passages
        self.generate = dspy.ChainOfThought('context, question -> answer')

    def forward(self, question):
        passages = self.retrieve(question).passages
        context = '\n'.join(passages)
        return self.generate(context=context, question=question)

# This is a complete RAG pipeline in ~10 lines
rag = RetrieveAndAnswer()
result = rag(question='What are the main causes of climate change?')
print(result.answer)

Inline Signature Shorthand

For simple cases, DSPy accepts an inline string signature: 'input1, input2 -> output1, output2'. This is convenient for quick prototyping without defining a full class.

import dspy

# Full class signature
class Translate(dspy.Signature):
    """Translate text to French."""
    text: str = dspy.InputField()
    translation: str = dspy.OutputField()

# Equivalent inline shorthand
translator_v1 = dspy.Predict(Translate)
translator_v2 = dspy.Predict('text -> translation')  # Less metadata

# Both work the same way
result = translator_v1(text='Hello world')
print(result.translation)

Field Descriptions Matter

The desc parameter in InputField and OutputField is included in the generated prompt. Good descriptions guide the model precisely.

Think of desc as the field-level documentation that appears in the prompt alongside the field name.

import dspy

class Summarize(dspy.Signature):
    """Summarize the article for a busy executive."""
    article: str = dspy.InputField(
        desc='The full article text to summarize'
    )
    summary: str = dspy.OutputField(
        desc='3-5 bullet points highlighting key decisions and numbers'
    )
    confidence: float = dspy.OutputField(
        desc='Your confidence in the summary accuracy from 0.0 to 1.0'
    )

summarizer = dspy.Predict(Summarize)
# DSPy constructs a prompt using all the desc values automatically

Saving and Loading Programs

After optimization, you save the compiled program so you don't need to re-run optimization every time. DSPy serializes the optimized state to a JSON file.

import dspy

class QA(dspy.Signature):
    """Answer questions accurately."""
    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

program = dspy.ChainOfThought(QA)

# After optimization, save the compiled state
program.save('optimized_qa.json')

# Load it later without re-running optimization
loaded_program = dspy.ChainOfThought(QA)
loaded_program.load('optimized_qa.json')

result = loaded_program(question='What is the speed of light?')
print(result.answer)

Assertions for Output Constraints

DSPy Assertions let you declare constraints on outputs. If the model violates the constraint, DSPy automatically retries with corrective feedback — no manual retry logic needed.

import dspy

class ShortAnswer(dspy.Signature):
    """Answer in at most 10 words."""
    question: str = dspy.InputField()
    answer: str = dspy.OutputField()

class ConstrainedQA(dspy.Module):
    def __init__(self):
        super().__init__()
        self.predict = dspy.Predict(ShortAnswer)

    def forward(self, question):
        result = self.predict(question=question)
        # Assert: answer must be at most 10 words
        dspy.Assert(
            len(result.answer.split()) <= 10,
            'The answer must be 10 words or fewer.'
        )
        return result

Knowledge Check: ChainOfThought

What does dspy.ChainOfThought add compared to dspy.Predict when using the same signature?

Recap: Signatures and Modules

DSPy signatures are typed class declarations that describe what an LLM step should do — inputs, outputs, and a docstring instruction. Modules like Predict, ChainOfThought, and ReAct implement different reasoning strategies around a signature. You compose multiple modules inside a dspy.Module subclass to build multi-step pipelines. Field desc values guide the model within the auto-generated prompt, and Assertions enforce output constraints with automatic retries.

Frequently asked questions

Is the “Defining Signatures and Modules” lesson free?

Yes — the full text of “Defining Signatures and Modules” is free to read here on the web, and the AI Prompt Engineering course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the AI Prompt Engineering course, upgrade to CoddyKit PRO.

What will I learn in “Defining Signatures and Modules”?

Signature syntax, ChainOfThought, ReAct, and custom DSPy modules. You practise AI Prompt Engineering with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start AI Prompt Engineering?

No prior experience is required. AI Prompt Engineering on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Defining Signatures and Modules” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this AI Prompt Engineering lesson?

Yes. Every AI Prompt Engineering lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Introduction to DSPy Framework
  2. Defining Signatures and Modules
  3. Compiling and Optimizing Prompts
  4. Evaluating DSPy Pipelines
← Back to AI Prompt Engineering