跳过内容

本页已过时,在 DSPy 2.5 和 2.6 版本中可能并非完全准确

常见问题

DSPy 适合我吗?DSPy 与其他框架的对比

DSPy 的理念和抽象与其他库和框架显著不同,因此通常很容易决定 DSPy 是否适合你的用例。如果你是 NLP/AI 研究人员(或正在探索新的管道或新任务的实践者),答案通常是肯定的,毫无疑问。如果你是从事其他工作的实践者,请继续阅读。

DSPy 与提示词的轻量级包装器(OpenAI API、MiniChain、基本模板)的对比 换句话说:为什么我不能直接将提示词写成字符串模板? 嗯,对于极其简单的设置,这可能完全没问题。(如果你熟悉神经网络,这就像用 Python 的 for 循环表达一个微小的两层神经网络。某种程度上可行。)然而,当你需要更高质量(或可控成本)时,你需要迭代地探索多阶段分解、改进提示词、数据引导、仔细微调、检索增强,以及/或使用更小(或更便宜、或本地)的模型。使用基础模型构建的真正表达能力在于这些部分之间的交互。但每次你改变其中一部分时,很可能会破坏(或削弱)多个其他组件。**DSPy** 清晰地抽象出(强大地优化)这些交互中与你的实际系统设计无关的部分。它让你专注于设计模块级别的交互:用 10 或 20 行 **DSPy** 表达的同一程序可以轻松地编译成 GPT-4 的多阶段指令、Llama2-13b 的详细提示词,或 T5-base 的微调。哦,而且你不再需要维护项目中那些冗长、脆弱、针对特定模型的字符串了。

DSPy 与 LangChain、LlamaIndex 等应用开发库的对比 LangChain 和 LlamaIndex 专注于高级应用开发;它们提供开箱即用的预构建应用程序模块,可以与你的数据或配置对接。如果你乐于使用通用的、现成的提示词来回答 PDF 问题或标准文本转 SQL,你会发现这些库中有一个丰富的生态系统。**DSPy** 内部不包含针对特定应用程序的手工制作的提示词。相反,**DSPy** 引入了一小组功能更强大、更通用的模块,这些模块可以在你的管道中根据你的数据学习如何生成提示词(或进行微调)。当你更改数据、调整程序的控制流或更改目标 LM 时,**DSPy 编译器**可以将你的程序映射为一套针对该管道专门优化的新提示词(或微调)。因此,你可能会发现 **DSPy** 以最少的努力为你的任务获得最高质量,前提是你愿意实现(或扩展)你自己的简短程序。简而言之,**DSPy** 适用于你需要一个轻量级但自动优化的编程模型,而不是一个预定义提示词和集成的库。如果你熟悉神经网络:这就像 PyTorch(代表 **DSPy**)和 HuggingFace Transformers(代表更高级的库)之间的区别。

DSPy 与生成控制库的对比,例如 Guidance、LMQL、RELM、Outlines 这些都是令人兴奋的新库,用于控制 LM 的个体完成,例如,如果你想强制 JSON 输出模式或将采样约束到特定的正则表达式。这在许多设置中非常有用,但它通常专注于单个 LM 调用的低级、结构化控制。它无助于确保你获得的 JSON(或结构化输出)对于你的任务来说是正确或有用的。相比之下,**DSPy** 自动优化程序中的提示词,使其与各种任务需求对齐,这也可能包括生成有效的结构化输出。话虽如此,我们正在考虑允许 **DSPy** 中的**签名**表达由这些库实现的类似正则表达式的约束。

基本用法

我如何将 DSPy 用于我的任务? 我们为此编写了一个八步指南。简而言之,使用 DSPy 是一个迭代过程。首先,你定义你的任务以及想要最大化的指标,并准备一些示例输入——通常不带标签(如果你的指标需要,则只带有最终输出的标签)。然后,你通过选择内置层(modules)来构建你的管道,为每一层提供一个signature(输入/输出规范),然后在你的 Python 代码中自由调用你的模块。最后,你使用一个 DSPy optimizer 将你的代码编译成高质量的指令、自动生成的少量样本示例,或为你选择的 LM 更新 LM 权重。

我如何将复杂的提示词转换为 DSPy 管道? 请参阅上面的相同答案。

DSPy 优化器优化什么? 或者,编译到底做了什么? 每个优化器都不同,但它们都旨在通过更新提示词或 LM 权重来最大化程序上的指标。当前的 DSPy optimizers 可以检查你的数据,模拟程序跟踪以生成每个步骤的好的/坏的示例,根据过去的结果为每个步骤提出或细化指令,根据自生成的示例微调 LM 的权重,或结合其中的几种方法来提高质量或降低成本。我们乐于合并探索更丰富空间的新优化器:你目前在提示词工程、“合成数据”生成或自我改进中经历的大多数手动步骤,很可能可以泛化为作用于任意 LM 程序的 DSPy 优化器。

其他常见问题。我们欢迎通过 PR 在此处添加对这些问题的正式解答。你可以在现有问题、教程或论文中找到所有或大多数这些问题的答案。

  • 如何获取多个输出?

你可以指定多个输出字段。对于简写形式的签名,你可以在“->”指示符后面列出多个输出,用逗号分隔(例如,“inputs -> output1, output2”)。对于完整形式的签名,你可以包含多个 dspy.OutputField

  • 如何定义自己的指标?指标可以返回浮点数吗?

你可以简单地将指标定义为 Python 函数,这些函数处理模型生成结果,并根据用户定义的约束进行评估。指标可以将现有数据(例如,黄金标签)与模型预测进行比较,或者可以使用来自 LM 的验证反馈(例如,以 LLM 作为裁判)来评估输出的各个组成部分。指标可以返回 boolintfloat 类型的分数。请查看官方的指标文档,了解如何定义自定义指标以及如何使用 AI 反馈和/或 DSPy 程序进行高级评估。

  • 编译有多昂贵或缓慢?

为了反映编译指标,我们重点介绍一个实验作为参考:使用 dspy.BootstrapFewShotWithRandomSearch 优化器,在 gpt-3.5-turbo-1106 模型上针对 7 个候选程序和 10 个线程编译 SimplifiedBaleen。我们报告称,编译此程序大约需要 6 分钟,涉及 3200 次 API 调用,270 万输入 token 和 15.6 万输出 token,总成本为 3 美元(按当前 OpenAI 模型定价计算)。

编译 DSPy optimizers 自然会产生额外的 LM 调用,但我们通过以最大化性能为目标的极简执行来证实这种开销。这为通过使用更大的模型编译 DSPy 程序来提升小型模型的性能提供了途径,可以在编译时学习增强的行为,并在推理时将这种行为传播到被测试的小型模型。

部署或可复现性考量

  • 如何保存已编译程序的检查点?

这里是保存/加载已编译模块的示例

cot_compiled = teleprompter.compile(CoT(), trainset=trainset, valset=devset)

#Saving
cot_compiled.save('compiled_cot_gsm8k.json')

#Loading:
cot = CoT()
cot.load('compiled_cot_gsm8k.json')
  • 如何导出以进行部署?

导出 DSPy 程序就像上面突出显示的那样简单,只需保存它们!

  • 如何搜索我自己的数据?

RAGautouille 这样的开源库使你能够通过 ColBERT 等高级检索模型搜索自己的数据,并提供嵌入和索引文档的工具。在开发 DSPy 程序时,请随意集成这些库来创建可搜索的数据集!

  • 如何关闭缓存?如何导出缓存?

从 v2.5 版本开始,你可以通过将 dspy.LM 中的 cache 参数设置为 False 来关闭缓存。

dspy.LM('openai/gpt-4o-mini',  cache=False)

你的本地缓存将保存到全局环境变量目录 os.environ["DSP_CACHEDIR"] 或对于 notebook 则是 os.environ["DSP_NOTEBOOK_CACHEDIR"]。你通常可以将缓存目录设置为 os.path.join(repo_path, 'cache') 并从这里导出该缓存。

os.environ["DSP_NOTEBOOK_CACHEDIR"] = os.path.join(os.getcwd(), 'cache')

重要提示

DSP_CACHEDIR 负责旧客户端(包括 dspy.OpenAI、dspy.ColBERTv2 等),而 DSPY_CACHEDIR 负责新的 dspy.LM 客户端。

在 AWS lambda 部署中,你应该禁用 DSP_* 和 DSPY_*。

高级用法

  • 如何并行化? 你可以在编译和评估 DSPy 程序时通过在相应的 DSPy optimizersdspy.Evaluate 工具函数中指定多个线程设置来实现并行化。

  • 如何冻结模块?

模块可以通过将其 ._compiled 属性设置为 True 来冻结,表示该模块已通过优化器编译,并且不应调整其参数。这在像 dspy.BootstrapFewShot 这样的优化器中内部处理,其中学生程序在教师在引导过程中传播收集的少量样本示例之前确保被冻结。

  • 如何使用 DSPy 断言?

    a) 如何向程序添加断言: - 定义约束:使用 dspy.Assert 和/或 dspy.Suggest 在 DSPy 程序中定义约束。这些基于你想要强制执行的结果的布尔验证检查,可以简单地使用 Python 函数来验证模型输出。 - 集成断言:将断言语句放在模型生成之后(提示:放在模块层之后)

    b) 如何激活断言: 1. 使用 assert_transform_module: - 使用 assert_transform_module 函数以及 backtrack_handler 将你的 DSPy 模块与断言封装起来。此函数会将你的程序转换为包含内部断言回溯和重试逻辑,这也可以自定义:program_with_assertions = assert_transform_module(ProgramWithAssertions(), backtrack_handler) 2. 激活断言: - 直接调用包含断言的 DSPy 程序的 activate_assertions 方法:program_with_assertions = ProgramWithAssertions().activate_assertions()

    注意:要正确使用断言,你必须通过上述任一方法激活一个包含 dspy.Assertdspy.Suggest 语句的 DSPy 程序。

错误

  • 如何处理“上下文过长”错误?

如果你在 DSPy 中遇到“上下文过长”错误,很可能是因为你使用 DSPy 优化器在提示词中包含了示例,而这超出了你当前的上下文窗口。尝试减少这些参数(例如 max_bootstrapped_demosmax_labeled_demos)。此外,你还可以减少检索到的段落/文档/嵌入数量,以确保你的提示词符合模型的上下文长度。

一个更通用的解决方案是简单地增加发送给 LM 请求指定的 max_tokens 数量(例如 lm = dspy.OpenAI(model = ..., max_tokens = ...)。

设置详细级别

DSPy 利用 logging 库来打印日志。如果你想调试你的 DSPy 代码,可以使用下面的示例代码将日志级别设置为 DEBUG。

import logging
logging.getLogger("dspy").setLevel(logging.DEBUG)

或者,如果你想减少日志量,可以将日志级别设置为 WARNING 或 ERROR。

import logging
logging.getLogger("dspy").setLevel(logging.WARNING)