跳到主要内容

优化器(Teleprompter)

这是 DSPy 区别于普通「prompt 模板库」的核心。前三章你学会了声明运行程序;本章 讲 DSPy 怎么自动把程序调得更准——把 demos 和 instructions 当参数去优化。

1. 优化器是什么、统一接口

所有优化器继承 Teleprompterdspy/teleprompt/teleprompt.py:6),核心是一个方法:

def compile(self, student, *, trainset, teacher=None, valset=None, **kwargs) -> Module:
"""优化 student 程序,返回一个更好的程序。"""

输入一个未优化的 student 程序、一个训练集、一个 metric(打分函数),输出一个 接口完全相同、但内部参数被调好的新程序。三大主力优化器:

优化器调什么怎么调文件
BootstrapFewShotdemos(few-shot 样例)让「教师」跑训练集,把成功的执行轨迹变成样例dspy/teleprompt/bootstrap.py
MIPROv2instructions + demos贝叶斯搜索(Optuna)联合优化指令措辞和样例组合dspy/teleprompt/mipro_optimizer_v2.py
GEPAinstructionsLLM 反思失败案例、进化 prompt,按 Pareto 前沿选候选dspy/teleprompt/gepa/gepa.py

2. BootstrapFewShot:自举 few-shot 样例

2.1 它要解决的小问题

few-shot 例子很有用,但人工挑例子又烦又不一定挑得好。BootstrapFewShot 的洞见: 让一个「教师」程序去跑训练题,把它做对的那些题的完整执行过程,自动收集成 few-shot 例子。 教师默认就是 student 自己(可以是更强的模型)。

2.2 三步走

compiledspy/teleprompt/bootstrap.py:84)三步:

def compile(self, student, *, teacher=None, trainset):
self._prepare_student_and_teacher(student, teacher) # 1) 准备师生
self._prepare_predictor_mappings() # 2) 对齐师生的 predictor
self._bootstrap() # 3) 跑轨迹、收集 demos
self.student = self._train()
return self.student

师生必须结构一致——同样数量、同名、同签名的 predictor(bootstrap.py:112-133 用 assert 严格校验),否则收集到的轨迹没法对应回 student 的某个步骤。

2.3 核心:从 trace 提取 demo

_bootstrap_one_examplebootstrap.py:182)是精华。它在一个带 trace 的上下文里跑教师:

with dspy.context(trace=[], **self.teacher_settings):
prediction = teacher(**example.inputs()) # 跑这道训练题
trace = dspy.settings.trace # 拿到逐步执行轨迹(见第 1 章 §2.4)

if self.metric:
success = self.metric(example, prediction, trace) # 教师做对了吗?

如果教师做对了(metric 通过),就把 trace 里每一步拆成一个 demo:

for step in trace:
predictor, inputs, outputs = step
demo = dspy.Example(augmented=True, **inputs, **outputs) # 这步的输入+输出 = 一个样例
name2traces[self.predictor2name[id(predictor)]].append(demo)

这就是关键魔法(bootstrap.py:223-244):一次成功的端到端运行,会给程序里每个 predictor 各贡献一个「输入→输出」样例。多跳程序的每一跳都拿到针对自己的演示。

2.4 多样性:换 rollout 绕过缓存

若第一轮没成功,max_rounds > 1 时会用 lm.copy(rollout_id=round_idx, temperature=1.0) 复制一个 LM(bootstrap.py:191)。rollout_id 改变会让缓存 miss、temperature=1.0 让输出 多样,于是能换一批轨迹再试——避免反复采到同一个错误答案。

2.5 最后拼装

_trainbootstrap.py:259)给 student 每个 predictor 装上 demos = 自举来的样例 (最多 max_bootstrapped_demos)+ 训练集里的原始标注样例(补到 max_labeled_demos)。

3. MIPROv2:联合搜索指令 + 样例

3.1 思路

BootstrapFewShot 只调样例,不改指令文字。MIPROv2(dspy/teleprompt/mipro_optimizer_v2.py:60) 更进一步——同时搜索「该用哪段指令措辞」和「该配哪几个 few-shot 样例」,用贝叶斯优化 (Optuna 的 TPE)在有限预算内找好组合。

3.2 三个阶段

compilemipro_optimizer_v2.py:109)大致分三阶段:

  1. 自举样例候选:用 BootstrapFewShot 那套先收集一批 few-shot 样例候选 (_bootstrap_fewshot_examplesmipro_optimizer_v2.py:401)。
  2. 提案指令候选:用 GroundedProposerdspy/propose/mipro_optimizer_v2.py:471)让 一个 prompt 模型生成多个候选指令——它能「看程序结构、看数据、看自举样例、加技巧」来写 指令(受 program_aware_proposerdata_aware_proposertip_aware_proposer 等开关控制)。
  3. 贝叶斯搜索:在「指令候选 × 样例候选」的组合空间里,用 Optuna 跑若干 trial,每个 trial 选一组配置、在验证集(或 minibatch)上评估,TPE 据历史得分引导下一次选更可能好的组合 (_optimize_prompt_parametersmipro_optimizer_v2.py:507)。

3.3 预算档位与省钱技巧

auto="light"/"medium"/"heavy" 一键设预算(AUTO_RUN_SETTINGSmipro_optimizer_v2.py:46):

档位候选数 n验证集采样 val_size
light6100
medium12300
heavy181000

省钱关键是 minibatchcompile 默认 minibatch=Truemipro_optimizer_v2.py:120): 大多数 trial 只在一个小批量(默认 35 个)上评估,每隔 minibatch_full_eval_steps 步才在 完整验证集上做一次全量评估来确认。这样能用少得多的 LM 调用扫描更多组合。

4. GEPA:反思式 prompt 进化

4.1 思路

GEPA(Genetic-Pareto,dspy/teleprompt/gepa/gepa.py:156)走的是另一条路:不靠贝叶斯搜参, 而是让一个强「反思模型」读失败案例、用自然语言反馈来改写 prompt,像进化算法一样迭代, 并用 Pareto 前沿 维护一组各有所长的候选程序。论文是 2025 年 GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning(见仓库 README 引用)。

4.2 DSPy 侧的接线

GEPA 的核心进化循环实现在外部包 gepagepa[dspy]==0.1.1,见 pyproject.toml)。 DSPy 这一层主要负责把 DSPy 程序适配给 gepa 引擎

  • reflection_lm 是用于反思改写的强模型,默认必填——除非你提供了自定义的 instruction_proposergepa.py:398 的断言正是 assert reflection_lm is not None or instruction_proposer is not None,二者至少有一个; docstring 也说明「reflection_lm is optional when using a custom instruction_proposer」 (gepa.py:251-252)。
  • metric 不只返回分数,最好返回带文字反馈的结果(GEPAFeedbackMetric 协议, gepa.py:28)——反馈文字正是反思模型改进 prompt 的素材。
  • candidate_selection_strategy="pareto"(默认):从所有验证任务得分构成的 Pareto 前沿里 随机选候选,保留「在不同子集上各自最强」的多个程序,而非只盯一个总分最高的 (gepa.py:218-220)。
  • use_merge=True(默认):允许把两个成功的程序变体合并gepa.py:263)。

compilegepa.py:476)把 student、trainset、valset 和这些配置打包,构造一个 gepa adapter (reflection_lm=(lambda x: adapter.stripped_lm_call(x)[0])gepa.py:583)交给外部引擎跑。

4.3 用 valset 追踪 Pareto,用 trainset 反思

文档明确分工(gepa.py:485-491):trainset 用来做反思式更新(看哪些题做错、怎么改 prompt),valset 用来追踪 Pareto 分数(衡量候选好坏)。源码还建议 valset 取「刚好够代表 下游分布的最小集」,把更多预算留给探索。

5. 评估:优化的「尺子」

所有优化器都靠一个 metric 打分,而批量打分由 Evaluatedspy/evaluate/evaluate.py:64)做:

evaluator = dspy.Evaluate(devset=devset, metric=my_metric, num_threads=16)
result = evaluator(program)
print(result.score) # 百分制平均分

它用 ParallelExecutor 多线程跑整个数据集(evaluate.py:162),对每条算 metric(example, prediction),汇总成 EvaluationResult(含 score + 每条的 (example, prediction, score) 明细,evaluate.py:48)。失败的样例计 failure_score(默认 0) 而不让整批崩。

6. 巧妙之处

  • trace = 免费的训练数据Predict 在 forward 时把每步执行写进 settings.trace (第 1 章 §2.4),优化器只需在 trace 上下文里跑一遍就能拆出每个 predictor 的样例 (dspy/teleprompt/bootstrap.py:223)。
  • rollout_id 绕缓存换多样性:不靠改 prompt,靠改缓存键 + 升温采到不同轨迹 (bootstrap.py:191)。
  • minibatch + 间歇全量评估:MIPROv2 用小批量扫描候选、定期全量确认,大幅压低搜索成本 (mipro_optimizer_v2.py:381-392)。
  • Pareto 前沿保多样:GEPA 不只留总分冠军,留「各有所长」的一组,避免过早收敛 (gepa.py:218)。

7. 边界与局限

  • BootstrapFewShot 严格要求师生程序结构、签名完全一致bootstrap.py:112-133),否则 直接 assert 失败。
  • MIPROv2 依赖可选依赖 Optuna_import_optunamipro_optimizer_v2.py:28,没装会报错 让你 pip install dspy[optuna])。
  • GEPA 的进化引擎核心在外部 gepa里,DSPy 仓库只看得到适配/接线层;本章未追踪外部 包的反思/合并算法细节。
  • GEPA 必须提供一个强 reflection_lm除非你提供自定义 instruction_proposer—— gepa.py:398 的断言是 reflection_lm is not None or instruction_proposer is not None, 两者皆缺才会失败。

8. 横向对比

同属本 shelf 的 prompt-authority / evals 方向,三种优化器代表三条技术路线:

路线代表调的对象搜索方式
样例自举BootstrapFewShotdemos教师跑成功轨迹(无搜索)
贝叶斯联合搜索MIPROv2instructions + demosOptuna TPE 在组合空间搜
LLM 反思进化GEPAinstructions反思反馈 + Pareto 进化

9. 代码地图

主题文件符号
优化器基类dspy/teleprompt/teleprompt.pyTelepromptercompile
自举编译dspy/teleprompt/bootstrap.pyBootstrapFewShotcompile_bootstrap
轨迹提取 demodspy/teleprompt/bootstrap.py_bootstrap_one_example_train
指令+样例搜索dspy/teleprompt/mipro_optimizer_v2.pyMIPROv2AUTO_RUN_SETTINGS_propose_instructions_optimize_prompt_parameters
指令提案dspy/propose/GroundedProposer
反思进化dspy/teleprompt/gepa/gepa.pyGEPAcompileGEPAFeedbackMetric
批量评估dspy/evaluate/evaluate.pyEvaluateEvaluationResult