告别提示工程,未来属于DSPy(上)
构建基于大型语言模型(LLMs)的应用程序,往往是一件让人又爱又怕的事。爱的是它强大的能力,怕的是它那种说不清道不明的脆弱性——应用程序高度依赖手工调整的提示,任何一个微小的改动都可能引发连锁反应,结果变得完全不可预测。这种情况让人头疼——好在,DSPy框架正是为了解决这个而来。它用一种编程化的方法,替代了传统的手工提示工程,从而让整个流程变得更加稳定可控。
这篇文章会和大家聊聊DSPy的核心概念、编程模型以及编译器功能,并结合一些简单的实例来展示它到底有多强大。
1 DSPy介绍
DSPy(读作“dee-es-pie”),这个由斯坦福NLP团队开发的框架,从一开始就把目光放在了“利用基础模型进行编程”上。这和传统的、依靠手工调整提示的开发方式完全不同。
传统方法下,开发者需要绞尽脑汁去设计提示模板,然后小心翼翼地观察模型的反应。而DSPy的思路则是:把程序的信息流和步骤参数(包括提示和模型权重)分离开来,让整个过程变得系统化。它能自动优化程序,针对特定任务进行调整,不管是改提示还是做微调,都不在话下。
DSPy的核心特性有三个:
- :替代了传统的手写提示和微调过程。开发者不再需要纠结于“我应该怎么提示模型”,而是聚焦在“我希望得到什么结果”。
签名(Signatures)
- :将链式思维(Chain of Thought)、ReAct等提示技术封装成标准的模块,开发者可以像搭积木一样使用它们,大大降低了操作难度。
模块(Modules)
- :这是最出彩的一部分。它把提示工程自动化了,开发人员只需要设定优化目标,剩下的交给编译器去搞定。
远程提示器(Teleprompters)与DSPy编译器
使用DSPy构建LLM应用的流程,其实和训练神经网络很相似:
- :准备好输入输出示例(比如问题和答案),这些是后续优化的基础。
收集数据集
- :利用签名和模块功能,定义清楚程序逻辑和组件间的信息流动。
编写DSPy程序
- :设定好验证指标和优化器(远程提示器),用来控制优化的方向和效果。
定义验证逻辑
- :编译器会整合训练数据、程序代码、优化器和验证指标,自动完成全面的优化。
编译DSPy程序
- :通过不断调整数据、程序或验证逻辑,反复循环,直到性能达到理想水平。
迭代改进
1.1 比较DSPy、LangChain和LlamaIndex
LangChain、LlamaIndex和DSPy,这三个框架都是用来辅助开发者构建LM应用的,但实现路径差异挺大。前两者重度依赖提示模板,意味着应用对组件的任何变化都非常敏感——改个模型,你可能就得重新调整一大堆提示。
DSPy的独特之处在于,它引入了编译器这个关键功能。当你想更换语言模型、调整数据源或者修改程序逻辑时,它不会像其他框架那样让你重新走一遍提示工程或者微调的流程。你只需要重新编译程序,优化会自动完成。这极大地减少了开发工作量,有时候性能甚至比LangChain或LlamaIndex还要出色。
可以说,虽然LangChain和LlamaIndex在社区中早已声名在外,但DSPy正以一种更有说服力的方式,迅速吸引着开发者的目光。
1.2 DSPy与PyTorch的联系
如果你有数据科学背景,看到DSPy的语法时,应该在很多地方都能找到PyTorch的影子。
在PyTorch中,我们可以自由组合各种通用的神经网络模块来构建模型。DSPy也一样,它允许开发者灵活组合模块,来搭建任何类型的LM应用。更关键的是,两者都强调自动化优化:PyTorch通过优化器自动调整模型参数,DSPy则通过编译器自动调整程序中的模块参数。
下面是PyTorch和DSPy之间的类比表:
| PyTorch | DSPy |
|---|---|
| 神经网络模块(nn.Module) | DSPy模块(dspy.Module) |
| 损失函数 | 验证指标 |
| 优化器(optimizer) | 远程提示器(teleprompter) |
| 前向传播 | 程序执行 |
| 训练循环 | 编译过程 |
2 DSPy编程模型详解
2.1 签名:抽象提示和微调
在DSPy中,每一次调用语言模型都离不开自然语言签名。它代替了传统的手工编写提示,本质上是一个简短的函数定义,告诉模型需要做什么转换任务,而不是告诉它怎么去做。比如,“consume questions and context and return answers”。
这种机制极大地简化了编程。签名本质上就是输入和输出字段的组合,最简单的结构甚至就只有这几个字段。
下面是一些常见的简写语法示例:
"question -> answer"
"long-document -> summary"
"context, question -> answer"
这些简写已经能满足大部分场景了。但如果你需要更精细的控制,也可以用更完整的签名定义方式,它包括三个部分:
- LM需要解决的子任务的描述
- 输入字段的详细描述
- 输出字段的详细描述
举个例子:
class GenerateAnswer(dspy.Signature):
"""Answer questions with short factoid answers."""
context = dspy.InputField(desc="may contain relevant facts")
question = dspy.InputField()
answer = dspy.OutputField(desc="often between 1 and 5 words")
关键点在于:与传统手工编写提示不同,DSPy的签名可以通过示例引导,自动编译成能够自我改进的提示或微调。这意味着程序的灵活性和效率都大大提高了。
2.2 模块:抽象提示技术
DSPy的模块化设计,将各种提示技术抽象成标准化的组件,大大简化了将签名落地到具体任务的过程。这些模块能处理提示、微调、增强和推理,让签名的能力得到充分发挥。
以ChainOfThought模块为例,它可以接受一个签名,并在处理输入时应用链式思维策略。有两种方式传递签名:
选项1:使用最小签名
generate_answer = dspy.ChainOfThought("context, question -> answer")
选项2:使用完整签名
generate_answer = dspy.ChainOfThought(GenerateAnswer)
在特定输入上调用模块,就像这样:
pred = generate_answer(context="Which meant learning Lisp, since in those days Lisp was regarded as the language of AI.",
question="What programming language did the author learn in college?")
DSPy还提供了其他几种常用模块:
- :处理输入输出字段,生成指令,为指定签名创建模板。
dspy.Predict
- :继承自Predict,增加链式思维处理。
dspy.ChainOfThought
- :提供推理提示选项,增强ChainOfThought。
dspy.ChainOfThoughtWithHint
- :添加多重链比较。
dspy.MultiChainComparison
- :从检索模块获取信息。
dspy.Retrieve
- :融合思考、行动和观察的循环。
dspy.ReAct
这些模块可以灵活组合。通过继承dspy.Module并定义__init__和forward方法,就能实现复杂的逻辑控制。
比如,一个经典的RAG类可以这样定义:
class RAG(dspy.Module):
def __init__(self, num_passages=3):
super().__init__()
self.retrieve = dspy.Retrieve(k=num_passages)
self.generate_answer = dspy.ChainOfThought(GenerateAnswer)
def forward(self, question):
context = self.retrieve(question).passages
prediction = self.generate_answer(context=context, question=question)
return dspy.Prediction(context=context, answer=prediction.answer)
这种设计,让代码既清晰又简洁,大大提升了可读性和可维护性,也让整个处理流程变得更加直观灵活。
推荐书单
《LANGCHAIN入门指南:构建高可复用、可扩展的LLM应用程序》
这本书专门为对自然语言处理技术感兴趣的读者打造,系统讲解了LLM应用开发的整个流程。全书分为11章,从LLM基础入手,以LangChain这个开源框架为主线贯穿始终。第1-2章概述了LLM技术的发展背景和LangChain的设计理念,从第3章开始,逐一深入介绍LangChain的6大模块,包括模型I/O、数据增强、链、记忆等,每个部分都提供了大量的代码示例。第9章通过构建PDF问答程序,帮助读者把前面学到的知识串联起来;第10章介绍集成,扩展LangChain的应用场景;第11章则为初学者梳理了LLM的基础理论,比如Transformer模型。
这本书非常适合大语言模型开发者、AI应用程序开发者,也适合希望系统掌握LLM应用开发流程的任何人。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名