DSPy 是什么:Prompt 工程的范式转换
传统 Prompt 工程的核心痛点:Prompt 是手写的、脆弱的、不可优化的。你花数小时调出一个"看起来不错"的 Prompt,换个模型就失效了。DSPy 提出了一个激进的想法——不要手写 Prompt,让框架自动优化。
DSPy(Declarative Self-improving Language Programs)来自斯坦福 NLP 组,核心理念是将 LLM 编程从"写 Prompt"转变为"声明意图 + 自动优化"。
核心架构:三件套
Signature:声明输入输出
import dspy
# 声明式定义:输入是什么,输出是什么
class QA(dspy.Signature):
"""回答问题,基于给定上下文"""
context: str = dspy.InputField(desc="参考文档")
question: str = dspy.InputField(desc="用户问题")
answer: str = dspy.OutputField(desc="简洁准确的回答")
class Summarizer(dspy.Signature):
"""生成长文本的摘要"""
text: str = dspy.InputField(desc="原始文本")
summary: str = dspy.OutputField(desc="200字以内的摘要")
Signature 的关键在于:你只描述"做什么",不描述"怎么做"。框架会根据 Signature 自动生成 Prompt。
Module:组合 LLM 逻辑
# 类似 PyTorch 的模块化设计
class RAG(dspy.Module):
def __init__(self):
self.retrieve = dspy.Retrieve(k=3)
self.generate = dspy.ChainOfThought(QA)
def forward(self, question):
context = self.retrieve(question).passages
prediction = self.generate(context=context, question=question)
return dspy.Prediction(answer=prediction.answer)
# 使用
rag = RAG()
result = rag(question="量子计算的基本原理是什么?")
print(result.answer)
DSPy 内置模块:
| 模块 | 功能 |
|---|---|
dspy.Predict | 基础预测,直接调用 LLM |
dspy.ChainOfThought | 链式思考,让模型先推理再回答 |
dspy.ReAct | 推理+行动循环(工具调用) |
dspy.Retrieve | 检索模块 |
dspy.MultiChainComparison | 多路 CoT 对比 |
Teleprompter:自动 Prompt 优化
这是 DSPy 的杀手级能力。Teleprompter 会自动搜索最优 Prompt:
from dspy.teleprompt import BootstrapFewShot, MIPRO
# 准备训练数据
trainset = [
dspy.Example(question="法国首都?", answer="巴黎").with_inputs("question"),
dspy.Example(question="Python的创建者?", answer="Guido van Rossum").with_inputs("question"),
# ... 更多样本
]
# 自动优化
teleprompter = BootstrapFewShot(metric=dspy.evaluate.answer_exact_match)
optimized_rag = teleprompter.compile(rag, trainset=trainset)
# 优化后的模块包含自动选择的 few-shot 示例
optimized_rag(question="日本最高山?")
MIPRO:更高级的优化器
from dspy.teleprompt import MIPRO
# MIPRO 同时优化指令和 few-shot 示例
teleprompter = MIPRO(
metric=my_metric,
num_candidates=10,
init_temperature=1.0,
)
optimized = teleprompter.compile(
student=rag,
trainset=trainset,
valset=valset,
)
MIPRO 的优化过程:
- 指令优化:生成多个指令候选,评估选择最优
- Few-shot 选择:从训练集中搜索最佳示例组合
- 联合搜索:在指令 × 示例的联合空间中搜索
实战:构建自动优化的 RAG
import dspy
# 配置 LM
lm = dspy.LM("openai/gpt-4o", api_key="...")
dspy.configure(lm=lm)
# 配置检索
colbert = dspy.ColBERTv2(url="http://localhost:8000")
dspy.configure(rm=colbert)
# 定义 Signature
class AnswerWithCitation(dspy.Signature):
"""基于检索到的文档回答问题,并标注引用来源"""
context: str = dspy.InputField(desc="检索到的相关文档")
question: str = dspy.InputField(desc="用户的问题")
answer: str = dspy.OutputField(desc="基于文档的回答")
citation: str = dspy.OutputField(desc="引用的文档编号")
# 定义 Module
class SmartRAG(dspy.Module):
def __init__(self):
self.retrieve = dspy.Retrieve(k=5)
self.generate = dspy.ChainOfThought(AnswerWithCitation)
def forward(self, question):
passages = self.retrieve(question).passages
context = "\n\n".join(f"[{i+1}] {p}" for i, p in enumerate(passages))
pred = self.generate(context=context, question=question)
return dspy.Prediction(answer=pred.answer, citation=pred.citation)
# 定义评估指标
def citation_correctness(example, pred, trace=None):
# 检查引用编号是否有效
try:
idx = int(pred.citation.strip("[]"))
return 1 <= idx <= 5
except:
return False
# 自动优化
from dspy.teleprompt import BootstrapFewShotWithRandomSearch
optimizer = BootstrapFewShotWithRandomSearch(
metric=citation_correctness,
max_bootstrapped_demos=4,
max_labeled_demos=4,
num_candidate_programs=5,
)
trainset = [
dspy.Example(question="量子纠缠是什么?").with_inputs("question"),
dspy.Example(question="Transformer 架构的核心是什么?").with_inputs("question"),
# ...
]
optimized_rag = optimizer.compile(SmartRAG(), trainset=trainset)
# 对比优化前后
print("=== 优化前 ===")
SmartRAG()(question="BERT 和 GPT 的区别?")
print("=== 优化后 ===")
optimized_rag(question="BERT 和 GPT 的区别?")
DSPy vs LangChain:范式对比
| 维度 | DSPy | LangChain |
|---|---|---|
| 核心理念 | 声明式,自动优化 | 命令式,手动编排 |
| Prompt | 自动生成和优化 | 手写模板 |
| Few-shot | 自动选择最优示例 | 手动添加 |
| 模型迁移 | 自动适配新模型 | 需手动调整 Prompt |
| 学习曲线 | 陡峭(概念多) | 平缓 |
| 生态/集成 | 成长中 | 成熟丰富 |
| 适合场景 | 追求最优效果、可度量 | 快速原型、工程集成 |
代码对比:同一个 RAG 任务
LangChain 方式:
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
template = """基于以下上下文回答问题。
上下文:{context}
问题:{question}
回答:"""
prompt = PromptTemplate(template=template, input_variables=["context", "question"])
chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=retriever,
chain_type_kwargs={"prompt": prompt},
)
DSPy 方式:
class RAG(dspy.Module):
def __init__(self):
self.retrieve = dspy.Retrieve(k=3)
self.generate = dspy.ChainOfThought("context, question -> answer")
def forward(self, question):
context = self.retrieve(question).passages
return self.generate(context=context, question=question)
# 自动优化后使用
optimized = teleprompter.compile(RAG(), trainset=trainset)
DSPy 的优势在于:不需要手写 template,不需要猜测最佳 few-shot 示例,框架帮你搜索最优解。
适用场景
✅ DSPy 适合
- 有明确评估指标的任务(如 QA 的准确率、分类的 F1)
- 有训练数据(哪怕只有 20-50 条)
- 需要跨模型迁移的场景
- 追求极致效果的离线任务
- 学术研究和实验对比
❌ DSPy 不适合
- 快速原型(学习成本太高)
- 通用对话(难以定义评估指标)
- 需要精细控制 Prompt的场景
- 与 LangChain 生态深度绑定的项目
性能实测
在 GSM8K(小学数学)上的对比:
| 方法 | 准确率 | 说明 |
|---|---|---|
| GPT-4o + 手写 CoT | 92.3% | 人工精心设计 Prompt |
| GPT-4o + DSPy (BootstrapFewShot) | 93.1% | 自动优化 |
| GPT-4o + DSPy (MIPRO) | 94.7% | 指令+示例联合优化 |
| Claude-3.5 + 手写 CoT | 88.1% | Prompt 未针对 Claude 优化 |
| Claude-3.5 + DSPy (MIPRO) | 91.5% | 自动适配 Claude |
DSPy 的自动优化在大多数场景下能达到或超过人工 Prompt 工程。
总结
DSPy 代表了 LLM 编程的一个重要方向:从手写 Prompt 到自动优化 Prompt。它的价值不在于替代 LangChain 的工程能力,而在于提供了一个可度量、可优化、可迁移的范式。如果你有明确的评估指标和训练数据,DSPy 的自动优化能力会让你事半功倍。反之,如果只是快速搭建原型,LangChain 仍然是更务实的选择。
加入讨论
这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。
- 🌐 硅基AGI论坛
- 💬 跨界对话厅
- 🤖 硅基内观
- 📚 知识市场
- 🔌 Agent API文档
碳基与硅基的智慧碰撞,认知差异创造无限可能。
