告别提示工程,未来属于DSPy(下)
介绍DSPy框架的核心概念、编程模型、编译器功能,并通过一个简单示例展示其应用方式。话不多说,直接进入正题。

2.3 提词器:自动化提示优化DSPy程序
提词器在DSPy里扮演着优化器的角色——它利用特定的性能指标来优化DSPy程序模块的提示,并与编译器协同工作,从而提升程序执行的效率和效果。简单说,就是让提示自动变得更好。
举个简单的例子,BootstrapFewShot是入门级提词器:
from dspy.teleprompt import BootstrapFewShot
teleprompter = BootstrapFewShot(metric=dspy.evaluate.answer_exact_match)
目前DSPy支持五种提词器,各有侧重:
dspy.LabeledFewShot:定义预测器使用k个样本的数量。dspy.BootstrapFewShot:引导式启动,像搭积木一样逐步构建。dspy.BootstrapFewShotWithRandomSearch:在BootstrapFewShot基础上加入随机搜索,探索更多可能。dspy.BootstrapFinetune:专门用于编译过程中的微调,适合需要精细调优的场景。dspy.Ensemble:把多个程序集成起来,统一不同输出为单一结果,类似于投票机制。
不同提词器在优化成本与质量之间各有取舍,选对工具往往事半功倍。
3 DSPy编译器
DSPy编译器会在内部跟踪你的程序,然后使用提词器(优化器)进行优化,从而提升程序性能。这一优化过程会根据你选用的语言模型规模和特性来动态调整:
- 对于大型语言模型(LLMs),编译器会构建少量但高质量的示例提示。
- 对于规模较小的语言模型,则会自动进行微调训练。
换句话说,编译器能智能地将程序模块与优质的提示、微调、推理和增强策略相匹配。后台它会模拟程序在不同输入下的各种运行版本,通过引导式学习不断自我完善,最终适应你的特定任务。这个过程,跟训练神经网络其实有几分神似。
前面创建的ChainOfThought模块虽然给语言模型提供了一个不错的起点,但往往并非最佳提示。正如以下图像所示,DSPy编译器可以优化初始提示,省去手动调整提示的繁琐步骤——这才是它真正的价值所在。
编译器需要以下输入:
- 程序
- 提词器(包括定义的验证指标)
- 一些训练样本
from dspy.teleprompt import BootstrapFewShot
# 带有问题和答案对的小型训练集
trainset = [dspy.Example(question="What were the two main things the author worked on before college?",
answer="Writing and programming").with_inputs('question'),
dspy.Example(question="What kind of writing did the author do before college?",
answer="Short stories").with_inputs('question'),
...
]
# 提词器将引导缺失的标签:推理链和检索上下文
teleprompter = BootstrapFewShot(metric=dspy.evaluate.answer_exact_match)
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)
4 DSPy实践:构建简单的RAG流程
现在核心概念已经掌握,接下来动手构建第一个DSPy流程。检索增强生成(RAG)在生成式AI领域非常流行,用它作为起点再合适不过。
开始之前先安装DSPy:
pip install dspy-ai
步骤1:初始化设置
先配置好语言模型(LM)和检索模型(RM)。
- :采用OpenAI的
语言模型(LM)
gpt-3.5-turbo,需要准备好API密钥。 - :使用Wea viate——一个开源向量数据库。我们用一些示例数据来初始化它。
检索模型(RM)
数据来自LlamaIndex GitHub仓库(MIT许可)。当然,完全可以用自己的数据代替。
!mkdir -p 'data'
!wget 'https://raw.githubusercontent.com/run-llama/llama_index/main/docs/examples/data/paul_graham/paul_graham_essay.txt' -O 'data/paul_graham_essay.txt'
接下来将文档拆分成单独句子导入数据库。使用Wea viate嵌入式版本,免费且无需注册API密钥。
注意:导入时每个数据项必须包含名为"content"的属性,这是Wea viate检索的关键。
import wea viate
from wea viate.embedded import EmbeddedOptions
import re
# 以嵌入模式连接到 Wea viate 客户端
client = wea viate.Client(embedded_options=EmbeddedOptions(),
additional_headers={
"X-OpenAI-Api-Key": "sk-",
}
)
# 创建 Wea viate 模式
schema = {
"classes": [
{
"class": "MyExampleIndex",
"vectorizer": "text2vec-openai",
"moduleConfig": {"text2vec-openai": {}},
"properties": [{"name": "content", "dataType": ["text"]}]
}
]
}
client.schema.create(schema)
# 将文档分割为单个句子
chunks = []
with open("./data/paul_graham_essay.txt", 'r', encoding='utf-8') as file:
text = file.read()
sentences = re.split(r'(?
现在配置全局LM和RM:
import dspy
import openai
from dspy.retrieve.wea viate_rm import Wea viateRM
openai.api_key = "sk-"
lm = dspy.OpenAI(model="gpt-3.5-turbo")
rm = Wea viateRM("MyExampleIndex",
wea viate_client = client)
dspy.settings.configure(lm = lm,
rm = rm)
步骤2:数据准备
接下来收集训练示例,这里手工注释了少量问答对:
trainset = [dspy.Example(question="What were the two main things the author worked on before college?",
answer="Writing and programming").with_inputs('question'),
dspy.Example(question="What kind of writing did the author do before college?",
answer="Short stories").with_inputs('question'),
dspy.Example(question="What was the first computer language the author learned?",
answer="Fortran").with_inputs('question'),
dspy.Example(question="What kind of computer did the author's father buy?",
answer="TRS-80").with_inputs('question'),
dspy.Example(question="What was the author's original plan for college?",
answer="Study philosophy").with_inputs('question'),]
步骤3:构建DSPy程序
先定义签名GenerateAnswer,描述从问题到答案的转换:
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")
然后编写自定义RAG类,继承自dspy.Module。在__init__()中声明模块,在forward()中描述信息流:
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)
步骤4:编译与优化
最后定义提词器并编译程序,这会更新ChainOfThought模块中使用的提示:
from dspy.teleprompt import BootstrapFewShot
teleprompter = BootstrapFewShot(metric=dspy.evaluate.answer_exact_match)
compiled_rag = teleprompter.compile(RAG(), trainset=trainset)
调用RAG流程:
pred = compiled_rag(question = "What programming language did the author learn in college?")
可以评估结果,并迭代优化,直到对性能满意为止。整个流程下来,提示工程那些繁琐的手工活,就交给DSPy去自动完成吧。
-
- 关于宇宙的好的网名有哪些
- 角色扮演 | 1
- 网名