1. 前言:Prompt Engineering 还是"Prompt Alchemy"?
做过大模型开发的同学,一定经历过这种崩溃时刻:
● 为了让模型输出 JSON,你在 Prompt 里写了三遍 "Do not output markdown"。
● 为了提高准确率,你手动写了 10 个 Few-shot 示例,结果换了个模型版本,效果全崩了。
● 你改了 Prompt 里的一个形容词,整个推理逻辑突然就不通了。
我们自嘲是"Prompt Engineer",但实际上更像是"炼丹师"------在黑盒子里不断试错,全靠运气(玄学)。
斯坦福大学 NLP 组说:够了。
他们推出了 DSPy (Declarative Self-improving Language Programs) 框架。它的核心理念非常激进:不要手写 Prompt,要写代码。让系统自动去"编译"出最佳的 Prompt。
如果说手写 Prompt 是在写汇编语言(Assembly),那么 DSPy 就是大模型时代的 PyTorch。
2. 核心概念:DSPy 是如何工作的?
DSPy 将 LLM 的应用开发抽象为了三个核心概念,这与 PyTorch 非常相似:
2.1 Signatures(签名):定义"做什么"
这就像函数的类型定义。你只需要告诉 DSPy 输入是什么,输出是什么,不用管怎么问。
● 传统 Prompt: "请你作为一个数学专家,计算下面这个问题的答案,并一步步思考..."
● DSPy Signature: Question -> Answer
2.2 Modules(模块):定义"怎么做"
这是处理逻辑的架构。你可以把 LLM 看作一个层(Layer)。
● dspy.Predict: 基本的问答。
● dspy.ChainOfThought: 自动加上"Let's think step by step"的逻辑。
● dspy.Retrieve: 自动调用检索器(RAG)。
2.3 Teleprompters(优化器):自动"炼丹"
这是 DSPy 最魔法的地方。类似于 PyTorch 的 Optimizer(优化器)。
你给它一些训练数据(输入+正确答案),DSPy 会自动去跑测试,自动挑选最佳的 Few-shot 示例,甚至自动重写 Instruction,直到在这个数据集上得分最高。
3. 实战:用 DSPy 解决一个逻辑推理问题
光说不练假把式。我们来做一个简单的数学推理任务(GSM8K 风格),看看 DSPy 如何自动优化。
3.1 环境准备
bash
pip install dspy-ai
3.2 配置模型
这里我们使用 OpenAI 的模型,当然你也可以换成 Claude 或本地的 Llama 3。
python
import dspy
# 配置语言模型 (LM)
turbo = dspy.OpenAI(model='gpt-3.5-turbo')
dspy.settings.configure(lm=turbo)
3.3 定义你的"程序" (Module)
我们不写 Prompt,我们写一个 Python 类。
python
class CoT(dspy.Module):
def __init__(self):
super().__init__()
# 定义一个思维链模块,输入是 question,输出是 answer
self.prog = dspy.ChainOfThought("question -> answer")
def forward(self, question):
# 前向传播
return self.prog(question=question)
3.4 零样本(Zero-shot)尝试
这时候我们还没训练,它就是个普通的 API 调用。
python
cot = CoT()
q = "如果我有 5 个苹果,吃掉了 2 个,又买了 3 个,现在我有几个?"
response = cot(q)
print("推理过程:", response.rationale) # DSPy 自动生成的思维链
print("最终答案:", response.answer)
输出可能是对的,但在复杂问题上可能不稳定。接下来是见证奇迹的时刻。
3.5 编译与优化(Compile & Optimize)
我们需要一点点"训练数据"(哪怕只有 5-10 条)。DSPy 的 BootstrapFewShot 优化器会利用这些数据,让一个强模型(Teacher)去教你的模型(Student),自动生成最佳的 Few-shot 示例。
python
from dspy.teleprompt import BootstrapFewShot
# 1. 准备少量训练集 (Input, Output)
trainset = [
dspy.Example(question="2+2等于几?", answer="4").with_inputs('question'),
dspy.Example(question="大卫有3个球,丢了1个,剩几个?", answer="2").with_inputs('question'),
# ... 假设这里有更多稍微复杂的例子
]
# 2. 定义评估指标 (Metric)
# 这里简单判断答案是否完全匹配,实际中可以用更复杂的逻辑
def validate_answer(example, pred, trace=None):
return example.answer == pred.answer
# 3. 初始化优化器
# max_bootstrapped_demos=4 表示我们希望它自动生成 4 个最佳的 Few-shot 示例
teleprompter = BootstrapFewShot(metric=validate_answer, max_bootstrapped_demos=4)
# 4. 编译!(这会花一点时间,因为它在疯狂尝试和评估)
compiled_cot = teleprompter.compile(CoT(), trainset=trainset)
3.6 发生了什么?
当你运行 compile 时,DSPy 在后台做了这些事:
-
用你的模型尝试回答训练集的问题。
-
如果回答对了,它把这个问题、推理过程(Rationale)和答案记录下来。
-
它把这些成功的案例,作为 Few-shot 示例,自动塞进 Prompt 里。
-
最终,
compiled_cot变成了一个自带最佳 Few-shot 示例的高级对象。
现在,你再用 compiled_cot 去回答新问题,准确率会显著提升,而且你一行 Prompt 也没写。
4. 为什么 DSPy 是未来?
1. 模块化与复用
以前你的 Prompt 是一个巨大的字符串,牵一发而动全身。现在你的 Prompt 是模块化的代码。你想把 GPT-3.5 换成 Llama-3?改一行配置就行,DSPy 会重新编译出适合 Llama-3 的 Prompt。
2. 真正的"数据驱动"
Prompt Engineering 变成了 Software Engineering。你不再通过"猜"来优化,而是通过"增加高质量数据"和"改进 Metric"来优化系统。这才是工程师该干的事。
3. 管道(Pipeline)编排
在复杂的 RAG 系统中,你可能需要:检索 -> 过滤 -> 总结 -> 回答。
在 DSPy 中,这就是把几个 Module 串起来,然后端到端(End-to-End)地优化整个流程。
5. 总结
DSPy 目前还处于快速迭代期(Alpha/Beta 阶段),文档有时比较晦涩,但它代表了 AI 工程化的正确方向:从"自然语言调教"回归到"编程与算法优化"。
如果你受够了手动维护几千字的 Prompt 文本,不妨试试 DSPy。把玄学变成科学,从今天开始。
参考资料:
● DSPy GitHub: stanfordnlp/dspy
DSPy Paper: DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines*