开始之前
DSPy 的 README把它称作“编程而不是提示”语言模型的框架,还说这个名字代表 Declarative Self-improving Python,也就是声明式、能自我改进的 Python。你用代码描述任务,DSPy 据此生成提示词,优化器再对照你定义的分数去调整这条提示词。
第 1 到 3 课写了一条提示词,改进了它,又手动测试了各个版本。DSPy 替你跑这个循环:它尝试很多版本,用你的例子给每个版本打分,留下最好的。你需要 Python、一个你的 API 密钥能调用的模型,以及一些例子。PyPI 上的软件包记录显示,最新版本是 3.4.1,2026 年 10 月 12 日上传,支持 Python 3.10 到 3.16(不含 3.16)。用 pip install dspy 安装。
这是整门课里技术性最强的一课。如果你不写 Python,就读它的思路:第 3 课的测试集成了训练数据,你的通过规则成了指标。
三个阶段,按这个顺序来
DSPy 的概览写出了三个阶段,并建议按这个顺序学习和应用。它给出的理由是:在设计糟糕的程序或糟糕的指标上启动优化运行,是没有成效的。
编程
定义任务和约束,试几个例子,设计第一版流程。从简单开始,也许只用一个 ChainOfThought 模块。
评估
收集一份开发集,定义指标,给系统打分。这个分数是之后一切改动的基线。
优化
用优化器,对照你的指标去调整提示词或模型权重。
来源:DSPy docs on GitHub: Learning DSPy, an overview (2026年11月6日 查阅)
第 1 步:写签名,而不是写提示词
按 DSPy 的说法,签名是对模块输入输出行为的声明式说明。它告诉模型要做什么,不管怎么问。字段名是有含义的:question 不是 answer,sql_query 也不是 python_code。
最短的写法是一个字符串,比如 question -> answer 或 document -> summary。任务需要规则时,就加上指令。我们的建议是把你自己提示词里的规则放进去。任务更长时,写一个带文档字符串的类,并用 desc 描述某个字段。
文档建议一开始不要手动调签名里的关键词。优化器多半做得更好,结果也能换到别的模型上继续用。
import dspy
# An inline signature, with 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
# Output: False
# A class-based signature
from typing import Literal
class Emotion(dspy.Signature):
"""Classify emotion."""
sentence: str = dspy.InputField()
sentiment: Literal['sadness', 'joy', 'love', 'anger', 'fear', 'surprise'] = dspy.OutputField()| 任务 | 写成字符串的签名 |
|---|---|
| 问答 | question -> answer |
| 情感分类 | sentence -> sentiment: bool |
| 摘要 | document -> summary |
| 基于段落的问答 | context: list[str], question: str -> answer: str |
| 带推理的选择题 | question, choices: list[str] -> reasoning: str, selection: int |
第 2 步:选一个模块,设置模型
模块把签名变成一次模型调用,并对它施加某种技巧。dspy.Predict 是最基础的一个,模块页面说其他模块都是以它为基础搭起来的。用 dspy.LM 设置一次模型就行。文档里的 OpenAI 示例从 OPENAI_API_KEY 读取密钥,语言模型页面还给出了 gemini/ 和 anthropic/ 前缀,以及各自的密钥变量。
用签名声明模块,把输入字段当关键字参数去调用它,再从结果里读输出字段。把 dspy.Predict 换成 dspy.ChainOfThought,模型会先一步一步推理。文档说,在很多情况下仅这一步就能提高质量,结果里还会多出一个 reasoning 字段。
更大的程序是一个装着几个模块的类,用 __init__ 声明模块,用 forward 去调用它们。编程页面建议先用单个模块,只有当输出里看到的问题需要时,才加步骤。
import dspy
# Authenticate via the OPENAI_API_KEY environment variable
lm = dspy.LM('openai/gpt-4o-mini')
dspy.configure(lm=lm)
sentence = "it's a charming and often affecting journey." # example from the SST-2 dataset.
# 1) Declare with a signature.
classify = dspy.Predict('sentence -> sentiment: bool')
# 2) Call with input argument(s).
response = classify(sentence=sentence)
# 3) Access the output.
print(response.sentiment)
# Output: True| 模块 | 它做什么 |
|---|---|
| dspy.Predict | 基础预测器,不改动签名。 |
| dspy.ChainOfThought | 让模型在回答之前一步一步思考。 |
| dspy.ReAct | 可以调用工具来完成签名的智能体。 |
| dspy.MultiChainComparison | 比较多个 ChainOfThought 的输出,得出最终预测。 |
| dspy.RLM | 通过沙盒 Python REPL 和递归的子模型调用,去探索很大的上下文。 |
| dspy.majority | 一个投票函数,从多个预测里返回票数最多的那个。 |
第 3 步:收集例子,写指标
你的例子是 dspy.Example 对象,用起来像字典。数据页面里的 with_inputs 标出哪些字段是输入,其余的算标签。看指标是什么,你只需要输入,或者需要输入加最终输出。文档说,中间各步的标签你几乎用不上。
指标是一个函数,接收一个例子和程序的输出,返回一个分数。它还接受一个可选的第三个参数 trace,一开始可以不管。短答案用精确匹配就够了。长回复的话,文档建议写一个检查多项属性的小 DSPy 程序,必要时让模型当评判。文档还说,第一次就写对的可能性不大,所以先从简单的写起,再迭代。
先写指标,再去优化任何东西,因为你定义不出来的东西,很难持续改进。然后用 Evaluate 工具在开发集上给没优化的程序打分。它可以多线程运行,并打印一部分输入、输出和分数。这个分数就是你的基线。
import dspy
article_summary = dspy.Example(article="This is an article.", summary="This is a summary.").with_inputs("article")
def validate_answer(example, pred, trace=None):
return example.answer.lower() == pred.answer.lower()
from dspy.evaluate import Evaluate
# Set up the evaluator, which can be re-used in your code.
evaluator = Evaluate(devset=YOUR_DEVSET, num_threads=1, display_progress=True, display_table=5)
# Launch evaluation.
evaluator(YOUR_PROGRAM, metric=YOUR_METRIC)- 对大多数提示词优化器,文档推荐一种不寻常的划分:20% 用于训练,80% 用于验证,原因是提示词优化器常常在小训练集上过拟合。
- GEPA 是例外。它沿用通常的做法:训练集尽量大,验证集只要大到足以反映真实任务就行。
- 另外留一份测试集,和前两者分开,进入优化阶段后,文档建议这样做。
| 用途 | 文档说可以从这么多开始 | 文档说最好达到 |
|---|---|---|
| 给优化器的训练输入 | 5 或 10 个例子,只要输入 | 有更多就更好 |
| 给程序打分的开发集 | 20 个输入例子 | 200 个 |
| 用于优化的训练集 | 30 个例子 | 至少 300 个 |
第 4 步:选一个优化器
优化器接收你的程序、你的指标和一些训练输入,返回一个更好的程序。优化器页面说,它可以调整提示词、模型权重,或者两者都调。先用哪一个,取决于你有多少数据。最后一列是文档自己给的入门建议。
GEPA 不在那份建议里。它是一种反思式优化器:由一个模型读程序是怎么失败的,依据是你的指标连同分数一起返回的文字反馈,再提出更好的指令。
| 优化器 | 它调整什么 | 文档说在这些情况下先用它 |
|---|---|---|
| BootstrapFewShot | 少样本示例,通过运行你的程序生成,指标通过的才保留 | 例子很少,十个上下 |
| BootstrapFewShotWithRandomSearch | 同样的做法,用随机搜索重复多次 | 有 50 个或更多例子 |
| MIPROv2 | 指令和少样本示例一起调,或者只调指令 | 只想优化指令,或者能跑更长的运行(40 次或更多试验)并有 200 个或更多例子 |
| GEPA | 指令,通过对失败的反思和文字反馈来调 | 不在入门建议里,它的指标返回分数和反馈文字 |
| BootstrapFinetune | 模型的权重 | 大模型能用,而你需要一个高效的程序,于是微调一个小模型 |
第 5 步:在文档的示例上运行 MIPROv2
MIPROv2 页面有一个完整的示例,用来优化一个数学程序。它先设置模型,再用 auto="medium" 创建优化器,在 GSM8K 训练集上编译单个 dspy.ChainOfThought("question -> answer"),最后保存结果。auto 可以取 light、medium 或 heavy。
先弄懂 MIPROv2,因为它调的东西最多。页面把它的工作分成三步。
import dspy
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric
# Import the optimizer
from dspy.teleprompt import MIPROv2
# Initialize the LM
lm = dspy.LM('openai/gpt-4o-mini', api_key='YOUR_OPENAI_API_KEY')
dspy.configure(lm=lm)
# Initialize optimizer
teleprompter = MIPROv2(
metric=gsm8k_metric,
auto="medium", # Can choose between light, medium, and heavy optimization runs
)
# Optimize program
print(f"Optimizing program with MIPROv2...")
gsm8k = GSM8K()
optimized_program = teleprompter.compile(
dspy.ChainOfThought("question -> answer"),
trainset=gsm8k.train,
)
# Save optimize program for future use
optimized_program.save(f"optimized.json")- 自举少样本示例:把训练例子送进你的程序,留下输出正确的那些。
- 提出指令:由一个模型写出候选指令,依据是你数据的摘要、你程序代码的摘要、自举出来的示例,以及一条随机的提示,比如“发挥创意”或“简明扼要”。
- 搜索:贝叶斯优化决定哪些指令和示例搭配在一起。它在小批量上跑
num_trials次试验,再把最好的在完整验证集上检查一遍。
把它们合起来,用在你自己的提示词上
下面的脚本把上面的片段合成一次运行,用在你自己的任务上。它是我们为这一课把文档里出现过的调用拼在一起的,我们没有运行过。换上你自己的规则、例子和指标,再把模型名换成你的密钥能调用的模型。
脚本后面的编号步骤是工作的顺序。第一次运行用 light 模式,文档里那个便宜的示例就是这样用的。
import dspy
from dspy.evaluate import Evaluate
dspy.configure(lm=dspy.LM('openai/gpt-4o-mini'))
# 1. Your task as a program: your prompt's rules go in the instructions.
program = dspy.ChainOfThought(
dspy.Signature(
'question -> answer',
instructions='PASTE THE RULES FROM YOUR PROMPT HERE',
)
)
# 2. Your examples, with the input fields marked. Fill in your own rows.
trainset = [
dspy.Example(question='...', answer='...').with_inputs('question'),
]
devset = [
dspy.Example(question='...', answer='...').with_inputs('question'),
]
# 3. Your metric: the pass rule you wrote in lesson 3, as a function.
def validate_answer(example, pred, trace=None):
return example.answer.lower() == pred.answer.lower()
# 4. Score the program before you change anything.
evaluator = Evaluate(devset=devset, num_threads=1, display_progress=True, display_table=5)
evaluator(program, metric=validate_answer)
# 5. Optimize in the cheapest mode, then score again on the same devset.
optimizer = dspy.MIPROv2(metric=validate_answer, auto='light')
optimized = optimizer.compile(program, trainset=trainset)
evaluator(optimized, metric=validate_answer)
# 6. Save it. The file is plain JSON, so you can read what the optimizer wrote.
optimized.save('optimized.json')- 至少写 50 个你的任务例子,包含输入和你期望的输出。文档说,20 个例子作开发集就有用,30 个例子作训练集就能带来明显的价值,所以开发列表放 20 个,训练列表放 30 个,有条件再多加一些。
- 把你的提示词改成签名:输入输出字段照写,你的规则放进指令里。
- 写指标时,让它通过的,正是你在第 3 课里手动会判为通过的。
- 运行脚本。它会给程序打分,用 MIPROv2 的
light模式优化它,再打一次分,然后保存。 - 打开
optimized.json,读一读优化器选出的指令和示例。文档说这个文件包含每个参数和步骤,所以你能看到它生成了什么。 - 只有开发集的分数上升了,而且你在意的那些失败消失了,才保留这次改动。
文档报告了什么,你该期待什么
优化器页面给出了三次运行优化前后的分数,没有给运行日期。对 ReAct 和 Banking77 这两组,它说的是:在 DSPy 2.5.29 这个较早版本上,一次和上面那个示例类似的非正式运行,把分数提高了。这两个示例用的都是 GPT-4o-mini,Banking77 那一处页面还直接点了名。对 RAG 那一组,它只指向一篇教程,没有给出模型、版本或日期。把这些柱子当作改动的大致幅度来读,不要当成承诺。每一对对应不同的任务和指标,所以一根柱子只和紧挨着它的那根比。
关于时间和成本,文档把一次简单的优化运行说成大约十分钟,还说成本随模型、数据集和设置差别很大。它提醒你,对很大的模型或数据集要小心。
来源:DSPy docs on GitHub: Optimizers (2026年11月6日 查阅)
MIPROv2 的发布帖怎么说,以及 DSPy 清单里的一场讲座
MIPROv2 页面把 X 上的一个长帖当作可视化讲解。它由 Michael Ryan 在 2024 年 6 月 21 日发布,他把 MIPROv2 称作“我们新的、最先进的语言模型程序优化器”。他那句话的总结,正好用来检验你有没有弄懂:如果你能说出其中三个动词各做什么,思路你就有了。
如果你想先看再写代码,DSPy 的资源页面列出了关于这个项目的讲座、博客文章和播客。下面是其中一个,Databricks 的录像。
MIPROv2 会提出指令、自举示例,并优化它们的组合。

X 官方嵌入卡片的截图,拍摄于 2026 年 11 月 6 日,时间为 UTC。这条帖子是一个长帖的开头,后面的帖子不在截图里。点击截图可到 X 上打开原帖。
常见错误
- 指标还没跑通就启动优化器。文档称这样做没有成效,所以先拿几条你已经手动判过的输出,去检查指标。
- 用优化器训练过的例子去给优化后的程序打分。开发集或测试集要单独留出来。
- 一次都没运行就手动改签名里的措辞。让第一次优化运行去做这件事。
- 信任一个让模型来评判的指标,却不读几条它的判语。评判那一课讲了怎样检查。
- 指望在你的任务上得到文档里的数字。其中两个来自 DSPy 2.5.29 这个较早版本上的非正式运行,第三个来自一篇教程,用的都是文档自己的模型和数据。
- 一上来就在大模型上跑重量级的运行。先从
light开始。
动手试试
- 运行
pip install dspy,用你自己的密钥跑文档里的情感示例,先用dspy.Predict,再用dspy.ChainOfThought。 - 把你在第 2 课改进过的提示词,改成带指令的签名。
- 写出你的第一批例子和一个指标,改动之前先给程序打分。
- 用
light模式运行 MIPROv2,打开保存下来的 JSON,比较前后两个分数。
下一门课:上下文工程
现在,你能生成、改进、测试并自动调优一条提示词了。上下文工程课程把视野放宽,因为提示词只是 AI 智能体在每一步所看到内容的一部分。
自测一下
DSPy 的签名声明的是什么?
查看答案
B. 输入是什么、输出是什么,而不是怎么问 文档把签名称作对模块输入输出行为的声明式说明。它说明模型要做什么,不管怎么问。
DSPy 优化器需要哪三样东西?
查看答案
D. 一个程序、一个指标和一些训练输入 优化器页面说,典型的优化器需要你的程序、你的指标和少量训练输入,少到 5 或 10 个也行。
optimized_program.save(path) 写出的是什么?
查看答案
C. 你能读懂的纯文本 JSON 文档说,生成的文件是纯文本 JSON,包含所有参数和步骤,所以你能读到优化器生成了什么。
启动优化运行之前,应该先具备什么?
查看答案
A. 设计良好的程序和好的指标 概览说,在设计糟糕的程序或糟糕的指标上启动优化运行,是没有成效的。
常见问题
DSPy 用来做什么?
它用来把语言模型的调用搭成程序,而不用手写每一条提示词。它的 README 举了简单分类器、RAG 流程和智能体循环作例子,按我们的理解,它们的共同点是输出可以打分的任务。
DSPy 怎样优化提示词?
优化器在训练例子上运行你的程序,用你的指标给结果打分,留下得分最高的。按优化器不同,它会写新的指令、挑少样本示例,或者两者都做。MIPROv2 用贝叶斯优化来搜索各种组合。
DSPy 需要多少个例子?
先有一小把就够起步了,输入也不需要标签。文档给开发集和训练集定的目标数量则更大。
DSPy 比手写提示词更好吗?
项目自己说,往往是的。它的签名页面说,在很多情况下,编译得到的提示词比人写的更好,原因是优化器能尝试更多做法,并直接针对指标调整,而不是更有创意。请在你自己的开发集上验证。
用 DSPy 需要会 Python 吗?
需要,DSPy 是一个 Python 库,你要写一个简短的脚本。这一课里的代码大多是一个签名、一个指标函数和几次调用,都是从 DSPy 文档里照抄的。
DSPy 能用 Claude 和 Gemini 吗?
能。文档的语言模型页面展示了 OpenAI、Gemini 和 Anthropic 的模型,还有 Vertex AI、Azure OpenAI、Databricks,以及 GPU 服务器上的本地模型。
资料来源
- DSPy on GitHub: README https://github.com/stanfordnlp/dspy/blob/main/README.md
- DSPy docs on GitHub: Learning DSPy, an overview https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/index.md
- DSPy docs on GitHub: Programming in DSPy https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/programming/overview.md
- DSPy docs on GitHub: Signatures https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/programming/signatures.md
- DSPy docs on GitHub: Modules https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/programming/modules.md
- DSPy docs on GitHub: Language models https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/programming/language_models.md
- DSPy docs on GitHub: Evaluation in DSPy https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/evaluation/overview.md
- DSPy docs on GitHub: Data https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/evaluation/data.md
- DSPy docs on GitHub: Metrics https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/evaluation/metrics.md
- DSPy docs on GitHub: Optimization in DSPy https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/optimization/overview.md
- DSPy docs on GitHub: Optimizers https://github.com/stanfordnlp/dspy/blob/main/docs/docs/learn/optimization/optimizers.md
- DSPy docs on GitHub: MIPROv2 https://github.com/stanfordnlp/dspy/blob/main/docs/docs/api/optimizers/MIPROv2.md
- DSPy docs on GitHub: GEPA overview https://github.com/stanfordnlp/dspy/blob/main/docs/docs/api/optimizers/GEPA/overview.md
- DSPy docs on GitHub: Resources https://github.com/stanfordnlp/dspy/blob/main/docs/docs/community/community-resources.md
- PyPI: dspy package record https://pypi.org/pypi/dspy/json
- X: Michael Ryan on MIPROv2 https://x.com/michaelryan207/status/1804189184988713065
- YouTube: Databricks, DSPy 3.0 and DSPy at Databricks https://www.youtube.com/watch?v=grIuzesOwwU

