本节摘要:Signature 是 DSPy 三层抽象的契约层:用类声明的方式写清每一步"交换什么信息、要做成什么样"。本节讲签名的语法形态、字段描述如何影响编译质量、简单签名与结构化签名的适用边界,以及"契约思维"如何替代手工时代的"措辞思维"。
「Signature」这个术语借自函数签名——函数签名告诉你参数与返回值,不告诉你函数体怎么写。DSPy 把这个隐喻用到了提示词上:签名声明这一步的输入字段、输出字段与任务描述,至于发给模型的提示词长什么样,那不是签名的事,是编译器的事。理解了这个隐喻,签名的所有语法就都不神秘了。
import dspy class BasicQA(dspy.Signature): """回答问题,给出简短的事实型答案。""" question = dspy.InputField() answer = dspy.OutputField(desc="通常不超过五个词的短答案")
三行就是一个完整契约:docstring 是任务描述,InputField 声明输入,OutputField 声明输出。初学者最常见的疑问是:这也太简单了,模型怎么知道输出格式?答案是:运行期的格式约束由框架负责——Predict 等适配器会把字段名、类型与描述组装成结构化提示,并把模型返回解析回字段。你负责声明"是什么",框架负责"怎么问"。desc 参数容易被轻视,但它会直接进入编译器产出的指令里:编译器提案新指令时以字段描述为语义锚点,描述含糊(比如只写"答案"),编译器搜索空间就大而发散;描述精确("通常不超过五个词的短事实答案"),搜索就容易收敛。可以把 desc 理解为"写给编译器看的需求文档"。
任务复杂时,签名可以带类型注解,甚至声明"输出必须按顺序生成":
from typing import List import dspy class Outline(dspy.Signature): """为一篇技术短文拟大纲,先给标题再给三到五个要点。""" topic = dspy.InputField(desc="文章主题,如 DSPy 的演化历史") title = dspy.OutputField(desc="不超过二十字的标题") headings = dspy.OutputField(desc="要点列表,依重要性排序") section_counts = dspy.OutputField(desc="每个要点计划展开的段落数") class ExtractEntities(dspy.Signature): """从合同文本中抽取关键实体。""" contract_text = dspy.InputField(desc="合同全文") parties: List[str] = dspy.OutputField(desc="签约方全称列表") effective_date: str = dspy.OutputField(desc="生效日期,格式 YYYY-MM-DD") amount: float = dspy.OutputField(desc="合同金额,纯数字")
两个写法值得注意。其一,Outline 的多个输出字段存在逻辑顺序(先有标题才有要点),DSPy 的适配器会按字段声明顺序要求模型依次生成——把依赖关系的字段排在前面,是结构化签名的实用技巧。其二,类型注解 List[str]、float 不只是文档装饰:适配器会按类型构造解析规则与重试提示,amount: float 意味着解析失败时框架会带着错误信息让模型重试,而不是把"约 120 万元"这样的文本原样塞给你。

签名作为"接口"的定位,在团队协作中会兑现成实际的经济学。第一个兑现点是复用:一套写好的签名(比如"查询改写""证据引用作答""置信评估")可以跨项目搬运——签名不绑定任何具体提示词,换项目不用改一个字。第二个兑现点是并行开发:签名定下来,写模块的人与写指标的人就能并行开工,双方只认字段契约;这沿用的正是软件工程"面向接口编程"的老纪律,只是接口的实现方从类变成了编译器。第三个兑现点最隐蔽也最值钱:签名为评估提供了天然的切片单位——每个签名对应一个可独立验收的能力,评估报告按签名分项(5.1 节的分项切片),哪个能力拖后腿一目了然。
一个来自实践的签名资产管理经验:把团队常用的签名集中维护成内部库,每次新任务先翻库存再动手。常见任务族(问答、改写、抽取、分类)的签名经过几个项目打磨后趋近稳定,新任务的签名工作从"从头设计"降级为"选型加微调"。这也反向解释了为什么签名值得花心思写好——它们是复用周期最长的 DSPy 资产,比模块稳定(模块绑定行为形态),比指标通用(指标绑定验收标准)。
签名还有一个在文档里很少被强调、实践中却极有用的属性:它是天然的测试边界。传统代码的单元测试针对函数签名构造输入断言输出,DSPy 程序的"单元测试"同样可以按签名展开——为每个签名准备一个小型验收集(十几条带标准输出的样例),任何改动之后按签名逐个验收。因为签名字段稳定,这些验收集不随模块重构、不随编译产物更替而失效,是比"对整个程序跑端到端"细得多、也稳得多的质量探针。第 5 章的评估体系会把这个思路展开成完整的分层评估方法,而它的根基就是本节的契约:签名字段有多稳定,评估就能做多细。
手工时代调提示词,注意力全部在"这句话怎么措辞"上;签名迫使你把注意力转向三个更稳定的问题:这一步的真实输入有哪些、输出需要满足什么性质、任务描述能否一句话说清边界。做过一次完整迁移的团队普遍反映,写签名的过程中最常发现的不是"提示词写得不好",而是"任务定义不清"——比如想在一步里同时完成"抽取与判断相关性",写签名时才发现这是两个契约,硬捏在一起谁也做不好。契约思维的价值在此:它把任务拆解的毛刺提前暴露,而不是留到线上事故。
校验清单:写完签名问自己四个问题——每个字段的 desc 是否说清了"好输出"的性质;输出字段的声明顺序是否与生成逻辑一致;类型注解是否用上了框架的解析与重试;这个契约换个模型还成立吗(如果描述里出现了模型专属话术,就说明措辞思维又溜回来了)。