5.2 高级签名与模块设计


5.2 高级签名与模块设计

本节摘要:任务做深之后,签名与模块的设计质量开始直接决定上限。本节给出一组经过实战检验的设计方法:签名如何拆粒度、契约如何写边界、模块如何布校验位、长链路程序如何规范中间产物。它们不是新 API,而是把既有三层抽象用出水平的手艺。

签名组合的反模式先行

先看病,再开方。签名设计有三种高频反模式。反模式一,一签多职:在一个签名里同时要求"抽取实体并判断文本相关性并给出摘要"——三种能力挤在一个输出空间里,模型的注意力被摊薄,任何一项都做不精;编译器也无法为三种能力分别找到合适的示范。拆法是把一个签名拆成三个预测器,串联或并联由 Module 组织。反模式二,契约含糊:输出字段只写名字不写 desc,或者 desc 写成"合理的答案"这类空话——前面讲过 desc 是编译器的需求文档,空话文档等于把搜索交给掷骰子。反模式三,隐式依赖:签名字段之间有生成顺序依赖却不声明(先有结论才能写理由),导致模型输出顺序混乱、解析失败。治法是把依赖顺序显式化为字段声明顺序,让适配器按序约束生成。

三钟反模式的共同根源是把签名当"表格"而非"契约"。契约思维的具体化检查:每个字段能独立回答"它满足什么性质才算好"吗?把两个预测器的字段摆在一起,能画出清晰的依赖方向吗?任务边界情况(信息不足、输入异常)在契约里有位置吗?三问都过,签名才算立住。

粒度拆分的实操判断

拆签名最难的判断是"拆到多细"。过细的拆分让链路冗长、误差逐级传递;过粗的拆分回到一签多职。实用的判断锚点是"中间产物是否可验收":一步的输出如果可以用指标独立验收(这段摘要覆盖了原文要点吗、这条查询词指向明确吗),它就值得独立成签名;如果它只是"顺手产出",就并进相邻步骤。以会议纪要任务为例:"提取讨论要点"与"按负责人归组行动项"两步都各自可验收,拆开;"提取要点"内部的"分句、去重、合并"不可独立验收,就让它留在一步之内由模型自己完成。

拆完之后,结构上的经验法则:能并行的步骤别串行(多份证据的摘要可以并行生成再聚合,省时延);能复用的签名别复制(同一份"查询改写"签名被两个链路复用,编译时示范池还能互通余缺——示范按签名分组,复用签名的两个预测器可能共享高质量轨迹);必须串行的相邻步骤,前一步的输出字段直接作为后一步的输入字段,别在 forward 里做字符串加工——加工会破坏字段的类型信息,让编译器丢失接缝的语义。

模块的校验位设计

长链路程序需要校验位:在关键节点对中间产物做独立检查,不合格的当场处理。校验位有轻、重两种形态。轻校验是字段级检查,直接在 forward 里用代码完成——查询词长度、枚举值合法性、数值范围:

def forward(self, question): query = self.rewrite(question=question).query if len(query.split()) > 12: # 轻校验:畸形查询就地截断 query = " ".join(query.split()[:12]) passages = self.retrieve(query).passages ...

重校验是模型级检查——用一个校验签名让模型判断中间产物质量(3.2 节 FaithfulRAG 的 check 预测器就是重校验)。轻重搭配的原则:能用代码确定判定的,绝不动用模型调用(校验也是要花钱的);必须语义理解的,才上模型校验。校验位与 5.3 节的断言机制是上下游关系:本节的校验位是"结构里写死的检查",断言是"框架托管的检查与回溯",后者带自动重试与编译期信号,代价是多一层抽象。

中间校验位的一个完整案例

把轻重校验的搭配放进一个完整的链路看效果。背景是工单自动分类系统:先从工单文本抽特征,再按特征路由到处理团队,最后生成回复草稿。上线初期最大的故障源是路由错误——特征抽取偶发产出与工单无关的关键词,路由器忠实地把工单派错队。修复方案没有动编译,而是加了两道校验位:特征抽取之后加轻校验(关键词必须出现在原文,代码级检查零成本),路由之后加重校验(用一个路由合理性签名判断"这组关键词是否支撑这个路由选择",模型级检查拦住语义错配)。上线后错派率降了一个数量级,代价是每单多一次小型模型调用——轻重搭配的账就是这样算的:便宜检查拦大头,昂贵检查拦残余。

这个案例的通用结论:校验位设计的第一步不是写检查代码,而是画出链路的"错误放大图"——标出每个交接点上,上游什么样的错误会被下游放大成事故。放大倍数越高的交接点,校验位的级别就该越高。校验不是越多越好:每个校验都是时延与成本的税,只在该收税的关口收。

中间产物的规范化

深度程序的最后一个设计要点是中间产物的规范化:链路上流动的每个中间结果,都应当有稳定的字段名、类型与格式约定。反例是 forward 里随手拼字符串——context = "\n".join(passages) 之后再塞进一个写死的编号格式,下一个预测器看到的是"一份没有契约的文本",解析与调试都困难。正例是给中间产物一个轻量签名(哪怕只有描述没有模型调用):

class EvidenceBundle(dspy.Signature): """聚合并编号检索证据,作为下游作答的统一证据格式。""" passages = dspy.InputField(desc="原始文段列表") question = dspy.InputField() context = dspy.OutputField(desc="编号后的证据块:每行以 P1、P2 开头") coverage = dspy.OutputField(desc="对问题相关性的简短评估:足够或不足")

规范化的直接收益在调试与审计:5.1 节的拆链单测、5.5 节的产物审计,都依赖中间产物可独立复现——字段稳定、格式可解析,链路才能被拆开重放。规范化的间接收益在编译:字段描述进入编译器的语义空间,规范的中间契约让跨预测器的示范迁移(复用签名)成为可能。

本节要点回顾

  • 三种反模式:一签多职摊薄能力、契约含糊放跑搜索、隐式依赖打乱生成顺序;共同根源是把签名当表格不当契约。
  • 拆分锚点:中间产物可独立验收则拆,不可验收则并;并行优先、签名复用、拒绝 forward 里的字符串加工。
  • 校验位轻重:代码可判定的用轻校验零成本拦截,语义判断的用重校验模型把关,按需布点不滥用。
  • 中间产物规范化:稳定字段、类型与格式,让链路可拆、可重放、可审计,也让示范跨预测器流动。
  • 通向下一节:校验位布好之后,框架级的断言机制能把检查与回溯统一接管——那是 5.3 的机制课。

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