DSPy 实战:把提示词当程序写
「DSPy:编程语言模型的框架,而不是提示语言模型的框架。」这是 Stanford NLP 团队在 GitHub 上对 DSPy 的官方定义,也是 2026 年提示工程分水岭的宣言。PE Collective 在年度开发者工具评测里把 DSPy 称为「反主流选择」:当别人还在手写提示词模板时,DSPy 把提示词当成可编译的程序——你定义输入输出契约(Signature)、组装模块(Module),优化器(Optimizer)自动搜索更好的提示结构与示例。本文用四个可运行示例,讲清楚评估驱动开发(eval-driven development)如何在 2026 年取代手工调提示词。
编程而非提示:评估驱动开发
一、从提示词到手写程序
传统提示工程的问题在 2026 年已经无法回避:提示词是字符串,无法测试、无法版本对比、无法自动改进。DSPy 的核心思想是「编程而非提示」——你不写 prompt 字符串,而是声明 Signature(输入输出契约),让框架负责把契约翻译成模型能理解的形式。代码示例1 是一个完整的 DSPy 分类器:TicketTriage 签名声明输入 ticket、输出 label,Module 包装成可调用程序。整个文件里没有一个手写的提示词,但模型照样准确分类。
# dspy_basics.py — the hello world of programming language models
# pip install dspy
import dspy
# Pick a model: OpenAI, Anthropic, or a local endpoint
lm = dspy.LM("openai/gpt-5.6-luna", temperature=0.2)
dspy.configure(lm=lm)
# A Signature declares INPUT -> OUTPUT contract. No prompt string needed.
class TicketTriage(dspy.Signature):
"""Classify a support ticket into bug, feature, or question."""
ticket: str = dspy.InputField()
label: str = dspy.OutputField(desc="one of: bug, feature, question")
# A Module wraps the signature into a callable program
class Triage(dspy.Module):
def __init__(self):
super().__init__()
self.classify = dspy.Predict(TicketTriage)
def forward(self, ticket: str) -> str:
return self.classify(ticket=ticket).label
triage = Triage()
print(triage("The app crashes when I upload a PNG larger than 5MB."))
# -> bug二、ChainOfThought:推理模块化
2026 年推理模型(reasoning models)成为标配,但手写「Let's think step by step」的时代结束了。代码示例2 展示了 DSPy 的 ChainOfThought 模块:框架在内部注入推理结构,你只需要声明输入输出。PRSummarizer 同时返回 summary 和 risks 两个结构化字段,直接喂给 CI bot。PE Collective 的评测特别提到:DSPy 的编程模型(signatures、modules、optimizers)干净且可组合,评估驱动开发内建在工作流里——这正是它与传统提示词模板的本质区别。
# chain_of_thought.py — reasoning modules, no hand-written CoT prompt
import dspy
class SummarizeWithReasons(dspy.Signature):
"""Summarize a PR description and list the three riskiest changes."""
description: str = dspy.InputField()
summary: str = dspy.OutputField()
risks: list[str] = dspy.OutputField()
class PRSummarizer(dspy.Module):
def __init__(self):
super().__init__()
# ChainOfThought injects "Let's think step by step" style reasoning
# internally — you never paste a reasoning prompt yourself.
self.reason = dspy.ChainOfThought(SummarizeWithReasons)
def forward(self, description: str):
out = self.reason(description=description)
return out.summary, out.risks
summarizer = PRSummarizer()
summary, risks = summarizer("Adds OAuth login, bumps the API SDK, refactors auth.ts")
# summary and risks come back as structured fields, ready for your CI bot.三、Optimizer:让框架替你调提示词
DSPy 最反直觉的能力是编译(compile):你提供评估集和指标,优化器自动搜索提示结构、指令措辞与 few-shot 示例。代码示例3 用 MIPROv2 在 GSM8K 数学数据集上优化 MathSolver——先加载训练集,定义 exact-match 指标,然后一行 compile 启动搜索。团队在困难任务上报告 10-30 个百分点的提升,超过手工调提示词的效果。这是 2026 年「提示工程师」角色演变的缩影:写提示词本身正在被 AI 自动化。
# optimize.py — let the framework write better prompts than you can
import dspy
from dspy.datasets import gsm8k
class MathSolver(dspy.Module):
def __init__(self):
super().__init__()
self.solve = dspy.ChainOfThought("question -> answer")
def forward(self, question: str) -> str:
return self.solve(question=question).answer
# 1. Load an evaluation set (GSM8K math problems)
trainset, devset = gsm8k.load()[:200], gsm8k.load()[200:400]
# 2. Define the metric: exact-match on the answer
def metric(gold, pred, trace=None):
return gold.answer.strip() == pred.answer.strip()
# 3. Compile: the optimizer searches prompt structure + few-shot demos
optimizer = dspy.MIPROv2(metric=metric, auto="light")
program = optimizer.compile(MathSolver(), trainset=trainset, max_bootstrapped_demos=8)
# 4. Evaluate on held-out dev set
score = dspy.evaluate(program, devset=devset, metric=metric)
print(f"Dev accuracy: {score:.2%}")
# Teams report 10-30 point gains vs hand-written prompts on hard tasks.四、评估集:提示词的新单元测试
代码示例4 把评估集变成类似单元测试的存在:带标签的数据集放在代码旁边,每次提示词改动都是一次回归测试。换模型、换优化器、换提示结构,全部先过评估再上线。2026 年的工程纪律是:没有评估集的提示词改动不允许合入。DSPy 的 dspy.evaluate 让这条纪律可以写进 CI——准确率从 0.88 掉到 0.90 的变化,在发布前就被拦下来。
# eval_harness.py — evaluation sets are the new unit tests
import dspy
import json
# Keep a labeled dataset next to your code, like tests
DATASET = json.load(open("triage_eval.json")) # [{"ticket": ..., "label": ...}]
def triage_metric(gold, pred, trace=None):
return gold.label == pred.label
examples = [dspy.Example(ticket=d["ticket"], label=d["label"]) for d in DATASET]
examples = [e.with_inputs("ticket") for e in examples]
# Every prompt change becomes a regression test:
# - Before optimization: 0.88
# - After MIPROv2 compile: 0.93
# - After swapping the LM: 0.90 <- caught before production!
score = dspy.evaluate(triage, devset=examples, metric=triage_metric)
print(f"Triage accuracy: {score:.2%}")五、什么时候该用 DSPy
DSPy 不是银弹。简单的一次性调用、纯聊天场景、没有评估集的探索性任务,直接调 API 更合适。但一旦你的 LLM 调用进入生产:有固定输入输出契约、需要持续维护、会被多次改动——DSPy 的编程模型就碾压字符串提示词。PE Collective 的框架选型指南把它列为「面向优化型团队」的选择:有 ML 背景、愿意接受学习曲线,DSPy 的回报是独特的。学习曲线陡峭是真实的,但 2026 年大多数团队已经开始为它买单。
六、总结
2026 年的提示工程已经分成两个阵营:手写字符串派与编程派。DSPy 代表的编程派把提示词变成可测试、可优化、可版本化的程序,用评估集作为质量闸门。从 Signature 开始,接上 ChainOfThought,用 Optimizer 编译,最后把评估写进 CI——这套工作流让提示词不再是玄学,而是工程。
Signature → Module → Optimizer → Evaluate
📌 常见问题 FAQ
DSPy 是什么?
DSPy 是 Stanford NLP 团队开源的框架,官方定位是「programming—not prompting—language models」。你声明输入输出契约(Signature)与模块(Module),优化器自动搜索更好的提示结构与示例。
DSPy 和普通提示词模板有什么区别?
提示词模板是字符串,无法测试与自动优化;DSPy 把提示词当作可编译程序,支持评估集回归测试与 MIPROv2 等优化器自动调优,团队报告在困难任务上提升 10-30 个百分点。
DSPy 支持哪些模型?
通过 dspy.LM 可以接入 OpenAI、Anthropic、Google 等商业 API,也支持本地端点与开源模型。切换模型时只需改一行配置,评估集会自动告诉你质量变化。
DSPy 的学习曲线真的很陡吗?
是的。编程模型(signature、module、optimizer)与传统提示工程心智不同,文档也仍在完善。PE Collective 建议有 ML 背景的团队优先尝试,简单场景不必强上。
什么时候不应该用 DSPy?
一次性调用、纯聊天场景、没有评估集的探索性任务。先写字符串提示词快速验证,一旦进入生产并需要持续维护,再迁移到 DSPy 的编程模型。