A DSPy module can invoke another module as part of its computation. The inner module is a submodule; the outer module coordinates its inputs, outputs, and any surrounding logic. This is useful when one language-model call cannot complete the job—for example, when a question must be clarified before it is classified. A signature specifies the inputs and outputs of a call, while a module determines how that call is carried out. section 7.1 section 2.6
To compose a custom module, subclass dspy.Module. In __init__(), call super().__init__() and usually create the submodules. In forward(), pass inputs to them, use intermediate results as needed, and return a final Prediction. Call an instance like a function; DSPy invokes forward() behind the scenes. An optional aforward() supports asynchronous execution. section 7.2 section 2.6
Listing 7.4 Pseudocode for a standard custom module
class MoreComplexModule(dspy.Module):
def __init__(self, **kwargs):
super().__init__()
self.module1 = dspy.Predict(signature1)
self.module2 = dspy.Predict(signature2)
def forward(self, input1, input2):
x1 = preprocess(input1)
x2 = preprocess(input2)
x = self.module1(input1=x1, input2=x2)
x = process_intermediate_results(x)
x = self.module2(input=x)
x = final_processing(x)
return dspy.Prediction(x=x)
mod = MoreComplexModule()
mod(input1="this is the first input", input2="this is the second input")
The two-stage pseudocode shows the composition pattern: preprocess inputs, call a first Predict submodule, process its result, call a second, and package the final output. section 7.2
Clarify, then classify
A classifier first calls a submodule that rewrites a user message as a clear question. It passes that result to a second submodule for classification. BootstrapFewShot can optimize the enclosing program: when the full result satisfies the metric, intermediate inputs and outputs can become demonstrations for both submodules. section 7.6.1
Composition can also control whether a call happens at all. An intent classifier can check a message with a toxicity submodule and skip classification if the check flags it. Alternatively, a module can receive a submodule as a parameter: BestOfN repeatedly runs the supplied module and selects a result using a reward function. Submodules need not be limited to Predict; custom modules can themselves serve as submodules. section 7.4.3 section 7.3.2
Put a repeated multistep pattern inside one module to avoid duplicating orchestration and to optimize the full workflow. Keep programs separate when intermediate results are needed elsewhere, or when specialized, relatively unrelated steps are simpler to optimize independently. Modules may also combine submodule calls with Python logic and tools. section 7.7 section 7.9
