Skip to content

签名

当我们向DSPy中的LMs分配任务时,我们将所需的行为指定为签名。

签名是DSPy模块输入/输出行为的声明式规范。 签名允许您告诉语言模型需要做什么,而不是指定如何要求语言模型去执行。

你可能熟悉函数签名,它们规定了输入和输出参数及其类型。DSPy签名类似,但有一些区别。虽然典型的函数签名只是描述事物,DSPy签名则声明并初始化模块的行为。此外,字段名称在DSPy签名中很重要。你可以用简单的英语表达语义角色:一个question不同于一个answer,一个sql_query不同于python_code

为什么我应该使用DSPy签名?

为了实现模块化和简洁的代码,其中LM调用可以被优化为高质量的提示(或自动微调)。大多数人通过编写冗长且脆弱的提示来强制LM完成任务,或者通过收集/生成数据进行微调。编写签名比修改提示或微调更加模块化、自适应和可重现。DSPy编译器将根据您的签名、数据和流程,为您的LM构建高度优化的提示(或微调您的小型LM)。在许多情况下,我们发现编译生成的提示比人工编写的更好。这并不是因为DSPy优化器比人类更有创造力,而仅仅是因为它们可以尝试更多方法并直接调整指标。

内联 DSPy 签名

签名可以定义为一个简短的字符串,包含参数名称和可选类型,用于定义输入/输出的语义角色。

  1. 问答: "question -> answer", 等同于 "question: str -> answer: str" 因为默认类型始终为 str

  2. 情感分类: "sentence -> sentiment: bool", 例如 True 表示积极

  3. 摘要生成: "document -> summary"

您的签名也可以拥有多个具有类型的输入/输出字段:

  1. 检索增强问答: "context: list[str], question: str -> answer: str"

  2. 多项选择题解答与推理:"question, choices: list[str] -> reasoning: str, selection: int"

提示: 对于字段,任何有效的变量名都可以使用!字段名称应具有语义含义,但一开始保持简单,不要过早优化关键词!将这类优化工作留给DSPy编译器。例如,对于摘要任务,使用"document -> summary""text -> gist""long_context -> tldr"这样的命名通常就足够了。

你也可以在内联签名中添加指令,这些指令可以在运行时使用变量。使用instructions关键字参数向你的签名添加指令。

toxicity = dspy.Predict(
    dspy.Signature(
        "comment -> toxic: bool",
        instructions="Mark as 'toxic' if the comment includes insults, harassment, or sarcastic derogatory remarks.",
    )
)
comment = "you are beautiful."
toxicity(comment=comment).toxic

输出:

False

示例A:情感分类

sentence = "这是一段迷人且常常触动心灵的旅程。"  # 来自SST-2数据集的示例

classify = dspy.Predict('sentence -> sentiment: bool')  # 稍后我们将看到使用Literal[]的示例
classify(sentence=sentence).sentiment
输出:
True

示例B:摘要生成

# 来自XSum数据集的示例。
document = """这位21岁的球员为西汉姆联出场七次,并在上赛季对阵安道尔球队FC Lustrains的欧联杯资格赛比赛中打入了他的唯一进球。李上赛季在英甲联赛有过两次租借经历,先后效力于布莱克浦和科尔切斯特联。他为U's队打入两球,但未能挽救球队免于降级。李与升级的泰克斯队签订的合同期限尚未公布。请在我们的专属页面上查找所有最新的足球转会信息。"""

summarize = dspy.ChainOfThought('document -> summary')
response = summarize(document=document)

print(response.summary)
可能的输出:
21岁的李上赛季为西汉姆联出场七次并打入一球。他曾在英甲联赛租借效力于布莱克浦和科尔切斯特联,为后者打入两球。他现在已与巴恩斯利签订合同,但合同期限尚未公布。

许多DSPy模块(除了dspy.Predict)会在底层通过扩展您的签名来返回辅助信息。

例如,dspy.ChainOfThought 还添加了一个 reasoning 字段,该字段包含LM在生成输出 summary 之前的推理过程。

print("Reasoning:", response.reasoning)
可能的输出:
Reasoning: 我们需要强调李为西汉姆的表现,他在英甲联赛的租借经历,以及他与巴恩斯利的新合同。我们还需要提及他的合同长度尚未披露。

基于类的DSPy签名

对于一些高级任务,您需要更详细的签名。这通常是为了:

  1. 明确任务性质的某些方面(以下表示为docstring)。

  2. 提供关于输入字段性质的提示,通过desc关键字参数在dspy.InputField中表达。

  3. 对输出字段的供应约束,表示为dspy.OutputFielddesc关键字参数。

示例C:分类

from typing import Literal

class Emotion(dspy.Signature):
    """分类情绪。"""

    sentence: str = dspy.InputField()
    sentiment: Literal['sadness', 'joy', 'love', 'anger', 'fear', 'surprise'] = dspy.OutputField()

sentence = "当巨大的聚光灯开始让我眼花时,我开始感到有点脆弱"  # 来自 dair-ai/emotion

classify = dspy.Predict(Emotion)
classify(sentence=sentence)
可能的输出:
Prediction(
    sentiment='fear'
)

提示: 向语言模型更清晰地指定你的请求没有任何问题。基于类的签名可以帮助你实现这一点。但是,不要过早手动调整签名的关键词。DSPy优化器可能会做得更好(并且在不同语言模型之间具有更好的迁移性)。

示例 D: 评估引用忠实度的指标

class CheckCitationFaithfulness(dspy.Signature):
    """验证文本是否基于提供的上下文。"""

    context: str = dspy.InputField(desc="此处的事实被假定为真实")
    text: str = dspy.InputField()
    faithfulness: bool = dspy.OutputField()
    evidence: dict[str, list[str]] = dspy.OutputField(desc="支持声明的证据")

context = "这位21岁的球员为西汉姆联出场7次,并在上赛季对阵安道尔球队FC Lustrains的欧联杯资格赛中打入唯一进球。李上赛季在英甲联赛有过两次租借经历,先后效力于布莱克浦和科尔切斯特联。他为U's队打入两球,但未能帮助球队避免降级。李与升班马泰克斯队的合同期限尚未公布。请在我们的专属页面查看所有最新足球转会信息。"

text = "李为科尔切斯特联打入3球。"

faithfulness = dspy.ChainOfThought(CheckCitationFaithfulness)
faithfulness(context=context, text=text)
可能的输出:
Prediction(
    reasoning="让我们根据上下文检查声明。文本称李为科尔切斯特联打入3球,但上下文明确指出'他为U's队打入两球'。这是直接矛盾。",
    faithfulness=False,
    evidence={'goal_count': ["为U's队打入两球"]}
)

示例E: 多模态图像分类

class DogPictureSignature(dspy.Signature):
    """Output the dog breed of the dog in the image."""
    image_1: dspy.Image = dspy.InputField(desc="An image of a dog")
    answer: str = dspy.OutputField(desc="The dog breed of the dog in the image")

image_url = "https://picsum.photos/id/237/200/300"
classify = dspy.Predict(DogPictureSignature)
classify(image_1=dspy.Image.from_url(image_url))

可能的输出:

Prediction(
    answer='Labrador Retriever'
)

签名中的类型解析

DSPy 签名支持多种注释类型:

  1. 基本类型str, int, bool
  2. Typing模块类型list[str], dict[str, int], Optional[float]. Union[str, int]
  3. 自定义类型 在你的代码中定义
  4. 点符号 用于嵌套类型的正确配置
  5. 特殊数据类型 例如 dspy.Image, dspy.History

使用自定义类型

# Simple custom type
class QueryResult(pydantic.BaseModel):
    text: str
    score: float

signature = dspy.Signature("query: str -> result: QueryResult")

class MyContainer:
    class Query(pydantic.BaseModel):
        text: str
    class Score(pydantic.BaseModel):
        score: float

signature = dspy.Signature("query: MyContainer.Query -> score: MyContainer.Score")

使用签名构建模块并编译它们

虽然签名对于使用结构化输入/输出进行原型设计很方便,但这并不是使用它们的唯一原因!

你应该将多个签名组合成更大的DSPy modules,并将这些模块编译成优化的提示和微调。

优云智算