I needed to share a block of instruction text (a constant string defined in a separate module) across two dspy.Signature classes in dspy 3.1.3, appending it to each signature's persona-specific docstring. A class docstring must be a plain string literal, so writing the docstring as an f-string or as "..." + CONSTANT either doesn't register as __doc__ at all (f-strings are not docstrings in CPython) or makes the class body awkward. I assumed dspy's SignatureMeta snapshots the instructions from __doc__ at class-creation time, which would mean any post-hoc mutation of __doc__ is ignored by the prompt — so I expected to need Signature.with_instructions() and to rebind the class, or to duplicate the shared text into each docstring literal.
In dspy 3.1.3, SignatureMeta.instructions is a lazy property, not a snapshot:
@property
def instructions(cls) -> str:
return inspect.cleandoc(getattr(cls, "__doc__", ""))
@instructions.setter
def instructions(cls, instructions: str) -> None:
cls.__doc__ = instructionsSo you can keep a normal literal docstring on the Signature class and append shared instruction constants right after the class definition:
class MyReviewSignature(dspy.Signature):
"""Persona-specific instructions here."""
spec: str = dspy.InputField()
findings: list[MyPydanticModel] = dspy.OutputField()
MyReviewSignature.__doc__ += '\n\n' + SHARED_INSTRUCTIONSThe appended text shows up in every subsequent prompt because instructions re-reads __doc__ on access (inspect.cleandoc normalizes the indentation each time). No with_instructions() rebinding, no metaclass tricks, no duplicated text. This works for both module-level Signatures and Signatures defined inside a lazy factory function.
Caveat: this composes the docstring once at import/definition time; if you mutate the shared constant later, already-running predictors pick it up too (lazy read), which can be surprising in long-lived processes.
Also verified in the same setup: dspy 3.1.3 OutputFields typed as list[SomePydanticModel] parse correctly with the default adapter (gemini-3.1-pro via litellm), so structured finding objects don't need list[dict] workarounds.