3.1 Signature:输入输出契约


3.1 Signature:输入输出契约

本节摘要: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 是否说清了"好输出"的性质;输出字段的声明顺序是否与生成逻辑一致;类型注解是否用上了框架的解析与重试;这个契约换个模型还成立吗(如果描述里出现了模型专属话术,就说明措辞思维又溜回来了)。

本节要点回顾

  • 隐喻即本质:签名之于提示词,如函数签名之于函数体——声明契约,不写实现。
  • desc 是需求文档:字段描述是编译器搜索的语义锚点,含糊的描述直接放大搜索难度。
  • 类型与顺序是行为:类型注解触发解析与重试机制,输出字段顺序决定生成顺序。
  • 契约与产物分离:签名是跨模型稳定的源代码,提示词是随编译变化的产物,二者不可混淆。
  • 通向下一节:单个契约解决单步任务,多步任务需要把契约组装起来——那是 Module 的职责。

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U