DSPy: Stanford NLP 实验室,github.com/stanfordnlp/dspy
核心理念来自论文: "DSPy: Compiling Declarative LM Calls into Self-Improving Pipelines"
22.1 编程 vs 提示词 ------ DSPy 的设计哲学
传统 Prompt 工程的问题:
你写了一个 Agent:
prompt = "你是一个助手,请用中文回答..."
换一个模型(GPT-4 → Claude):
→ Prompt 要重新调整
→ 效果可能大幅下降
→ 这就是「Prompt 脆弱性」
DSPy 的解决方案:
把 Prompt 当作「代码的编译产物」而非手写字符串
类比:
传统 Prompt = 手动写汇编代码(每换一个 CPU 就要重写)
DSPy = 写 C 代码再编译(代码不变,编译器适配不同 CPU)
翻译成 DSPy 术语:
DSPy 程序(Python 代码)→ Optimizer(编译器)→ Prompt(机器码)
↑
自动适配不同的 LLM
22.2 Signature ------ 把 Prompt 变成「函数签名」
传统 Prompt:
prompt = "给定问题 {question},请给出答案" # 脆弱的字符串!
DSPy Signature:
class QA(dspy.Signature):
question = dspy.InputField(desc="问题")
answer = dspy.OutputField(desc="答案")
Signature 的优势:
- 声明式:描述「输入什么、输出什么」,不碰字符串
- 自文档化:desc 即文档,人类和 Optimizer 都能理解
- 可组合:Signature 可以拼接成复杂流水线
- 可验证:输出类型自动校验
📝 对应的代码实现
InputFieldOutputFielddescribeto_prompt_templateSignatureFieldDSPySignatureQA
import json
import time
import hashlib
from typing import Optional
from dataclasses import dataclass, field
# ===== 模拟 DSPy 核心概念(不 import dspy)=====
class="d">@dataclass
class SignatureField:
"""Signature 字段 ------ 带描述的类型标注。"""
desc: str = ""
def InputField(desc: str = "") -> SignatureField:
"""标记输入字段。"""
return SignatureField(desc=desc)
def OutputField(desc: str = "") -> SignatureField:
"""标记输出字段。"""
return SignatureField(desc=desc)
class DSPySignature:
"""模拟 DSPy 的 Signature 基类。
真实 DSPy 中:
class QA(dspy.Signature):
question = dspy.InputField()
answer = dspy.OutputField()
"""
class="d">@classmethod
def describe(cls) -> dict:
"""生成签名描述(给 Optimizer 用)。"""
fields = {}
for name, val in cls.__dict__.items():
if isinstance(val, SignatureField):
fields[name] = {
"role": "input" if isinstance(val, InputField.__class__)
else "output",
"desc": val.desc,
}
return {
"name": cls.__name__,
"fields": fields,
}
class="d">@classmethod
def to_prompt_template(cls) -> str:
"""将 Signature 编译为 Prompt 模板。
这是 DSPy Optimizer 的核心能力:
根据 Signature → 自动生成最优的 Prompt 结构。
"""
desc = cls.describe()
inputs = [f"{k}: {v['desc']}" for k, v in desc["fields"].items()
if v["role"] == "input"]
outputs = [f"{k}: {v['desc']}" for k, v in desc["fields"].items()
if v["role"] == "output"]
return (
f"## 任务: {desc['name']}\n\n"
f"### 输入\n" + "\n".join(f"- {i}" for i in inputs) +
f"\n\n### 输出\n" + "\n".join(f"- {o}" for o in outputs) +
f"\n\n请严格按输出格式返回 JSON。"
)
22.3 Module ------ 把 Agent 组件变成「函数」
DSPy Module 是可组合的 AI 组件。
内置 Module 类型:
📊 架构示意
┌──────────────────┬──────────────────────────────────┐
│ Module │ 作用 │
├──────────────────┼──────────────────────────────────┤
│ dspy.Predict │ 最基本的单步 LLM 调用 │
│ dspy.ChainOfThought│ 带推理链的 LM 调用 │
│ dspy.ReAct │ ReAct Agent 循环 │
│ dspy.MultiChain │ 多链并行 + 投票 │
│ dspy.ProgramOfThought│ 用代码辅助推理 │
└──────────────────┴──────────────────────────────────┘
类比 LangChain:
LangChain Chain ≈ DSPy Module
区别:DSPy Module 的参数(prompts/weights)可由 Optimizer 自动调优
Agent 开发者用 DSPy 的样子:
class AgentQA(dspy.Module):
def __init__(self):
self.search = dspy.ChainOfThought("query -> results")
self.answer = dspy.ChainOfThought("context, question -> answer")
def forward(self, question):
results = self.search(query=question)
return self.answer(context=results, question=question)
📝 对应的代码实现
DSPyModule
import json
import time
import hashlib
from typing import Optional
from dataclasses import dataclass, field
# ===== 模拟 DSPy 核心概念(不 import dspy)=====
class DSPyModule:
"""模拟 DSPy Module ------ 可组合的 AI 组件。"""
def __init__(self, name: str, signature: type):
self.name = name
self.signature = signature
self.prompt = signature.to_prompt_template()
self.optimized_prompt = self.prompt # Optimizer 会修改这个值
self.few_shot_examples = [] # Optimizer 会填充这个列表
def __call__(self, **inputs) -> dict:
"""执行模块(模拟 LLM 调用)。"""
# 组装最终 prompt
examples_text = ""
if self.few_shot_examples:
examples_text = "\n\n### 示例\n" + "\n---\n".join(
json.dumps(ex, ensure_ascii=False)
for ex in self.few_shot_examples[-3:]
)
final_prompt = self.optimized_prompt + examples_text
# 模拟 LLM 返回
return self._mock_llm(final_prompt, inputs)
def _mock_llm(self, prompt: str, inputs: dict) -> dict:
"""模拟 LLM 响应(真实实现调用 OpenAI/Claude)。"""
output_fields = [k for k, v in self.signature.describe()["fields"].items()
if v["role"] == "output"]
result = {}
for field in output_fields:
result[field] = f"[模拟] 基于 {len(prompt)} 字符模板生成"
return result
22.4 Optimizer (Teleprompter) ------ 自动优化 Prompt
这是 DSPy 最革命性的部分。
传统做法(手调 Prompt):
- 写一个 Prompt
- 在 5 个例子上测试
- 效果不好 → 修改 Prompt → 再测试
- 换模型 → 全部重来
DSPy 做法(自动优化):
- 定义 Metric(评分函数)
- 准备训练集(10 个标注样本就够)
- 运行 Optimizer → 自动调出最优 Prompt
DSPy 的 Optimizer 类型:
📊 架构示意
┌─────────────────────┬──────────────────────────────────┐
│ Optimizer │ 做什么 │
├─────────────────────┼──────────────────────────────────┤
│ BootstrapFewShot │ 自动找出最佳少样本示例 │
│ MIPROv2 │ 自动探索最优指令 + 示例组合 │
│ BootstrapFinetune │ 为每个 Module 自动微调小模型 │
│ BetterTogether │ 组合多个 Optimizer 级联优化 │
└─────────────────────┴──────────────────────────────────┘
工作流程(MIPROv2 为例):
- Bootstrapping 阶段:
运行未优化的程序多次 → 收集成功轨迹
→ 筛选出得分高的输入输出对 → 作为候选示例
- Grounded Proposal 阶段:
分析程序代码 + 数据 + 轨迹
→ 生成多个候选指令(instruction variants)
- Discrete Search 阶段:
在训练集上尝试不同「指令 + 示例」的组合
→ 用 Metric 评分 → 选出最优组合
📝 对应的代码实现
compileDSPyOptimizer
import json
import time
import hashlib
from typing import Optional
from dataclasses import dataclass, field
# ===== 模拟 DSPy 核心概念(不 import dspy)=====
class DSPyOptimizer:
"""模拟 DSPy Optimizer ------ 演示自动优化的工作流。
三样东西:
1. 你的 DSPy 程序(Module)
2. Metric(评分函数)
3. 训练输入(少量带标注的样本)
"""
def __init__(self, metric: callable):
"""
Args:
metric: 评分函数: (predicted, expected) -> score (0-1)
"""
self.metric = metric
self.candidates = []
def compile(self, module: DSPyModule,
trainset: list[dict],
max_candidates: int = 5) -> DSPyModule:
"""编译(优化)一个 Module。
流程:
1. 收集轨迹(多次运行,记录输入输出)
2. 筛选高分轨迹
3. 生成候选 Prompt 变体
4. 在训练集上评估,选最优
Args:
module: 要优化的 Module。
trainset: 训练数据(输入+期望输出)。
max_candidates: 生成的候选数。
Returns:
优化后的 Module。
"""
# 阶段 1:Bootstrapping(收集成功案例)
trajectories = []
for sample in trainset:
inputs = sample.get("inputs", {})
expected = sample.get("outputs", {})
result = module(**inputs)
score = self.metric(result, expected)
if score > 0.6:
trajectories.append({
"inputs": inputs,
"outputs": result,
"expected": expected,
"score": score,
})
# 阶段 2:筛选 best-shot 示例
trajectories.sort(key=lambda t: t["score"], reverse=True)
best_trajectories = trajectories[:max_candidates]
# 阶段 3:注入优化结果
module.few_shot_examples = [
{"Q": t["inputs"], "A": t["outputs"]}
for t in best_trajectories
]
# 阶段 4:生成优化后的指令(简化版)
if best_trajectories:
avg_score = sum(t["score"] for t in best_trajectories) / len(best_trajectories)
module.optimized_prompt = (
module.signature.to_prompt_template() +
f"\n\n[优化提示: 基于 {len(best_trajectories)} 个样本调优, "
f"平均得分 {avg_score:.1%}]"
)
self.candidates = best_trajectories
return module
22.5 DSPy + Agent 的实战价值
DSPy 不是 LangChain 的替代品,是补充:
DSPy 擅长:
✓ 自动优化 Agent 每一步的 Prompt
✓ 发现最佳的 few-shot 示例
✓ 结构化输出的质量保证
LangChain 擅长:
✓ Agent 编排(LangGraph 的图式工作流)
✓ 工具集成(MCP、向量数据库、API)
✓ 生态系统(社区、文档、第三方集成)
两者配合:
用 LangGraph 搭建 Agent 的执行流程
用 DSPy 优化每个 LLM 调用节点的 Prompt
面试中这样描述会非常加分:
"我们用 LangGraph 做 Agent 编排(控制流),
DSPy 做 LLM 调用优化(Prompt 层),
两者解耦,各司其职。"
22.6 DSPy 把 Agent 开发变成「数据驱动」
传统 Agent 开发流程**:Prompt 不好 → 看结果 → 凭直觉改 → 再试**
DSPy 的 Agent 开发流程:写代码(Module)→ 准备评测集 → 运行 Optimizer → 自动优化完成
类比:
传统方式 = 手动调参炼丹
DSPy = 自动超参搜索(类似 Optuna 之于深度学习)
面试金句:
"我不手动调 Prompt,我写评测指标,
让 DSPy Optimizer 自动找出最佳 Prompt。"
📝 对应的代码实现
make_metricmetricdemo_dspy_workflowSentimentSignature
import json
import time
import hashlib
from typing import Optional
from dataclasses import dataclass, field
# ===== 模拟 DSPy 核心概念(不 import dspy)=====
def make_metric(expected_key: str):
"""创建评分函数。"""
def metric(predicted: dict, expected: dict) -> float:
pred_val = str(predicted.get(expected_key, ""))
exp_val = str(expected.get(expected_key, ""))
if pred_val == exp_val:
return 1.0
if exp_val.lower() in pred_val.lower():
return 0.7
# Token 重叠率
pred_tokens = set(pred_val.lower().split())
exp_tokens = set(exp_val.lower().split())
if not exp_tokens:
return 0.0
overlap = len(pred_tokens & exp_tokens) / len(exp_tokens)
return min(overlap, 0.5)
return metric
def demo_dspy_workflow():
"""演示 DSPy 的完整工作流。"""
print("=" * 60)
print(" DSPy 工作流演示")
print("=" * 60)
# Step 1: 定义 Signature
class SentimentSignature(DSPySignature):
text = InputField(desc="待分析文本")
sentiment = OutputField(desc="情感: positive/negative/neutral")
confidence = OutputField(desc="置信度 0-1")
print("\n 📝 Signature 自动生成的 Prompt 模板:")
print("-" * 50)
print(SentimentSignature.to_prompt_template())
# Step 2: 创建 Module
module = DSPyModule("sentiment_analyzer", SentimentSignature)
print(f"\n 🧩 创建 Module: {module.name}")
# Step 3: 准备训练集
trainset = [
{
"inputs": {"text": "这个产品太棒了"},
"outputs": {"sentiment": "positive", "confidence": "0.95"},
},
{
"inputs": {"text": "服务非常差"},
"outputs": {"sentiment": "negative", "confidence": "0.90"},
},
{
"inputs": {"text": "还行吧,一般般"},
"outputs": {"sentiment": "neutral", "confidence": "0.80"},
},
{
"inputs": {"text": "I love this!"},
"outputs": {"sentiment": "positive", "confidence": "0.92"},
},
]
print(f" 📊 训练集: {len(trainset)} 个样本")
# Step 4: 运行 Optimizer
metric = make_metric("sentiment")
optimizer = DSPyOptimizer(metric=metric)
print(f"\n ⚙️ Optimizer 编译中...")
optimized = optimizer.compile(module, trainset, max_candidates=4)
print(f" ✅ 编译完成!")
print(f" 成功轨迹数: {len(optimizer.candidates)}")
print(f" 注入的 few-shot 示例数: {len(optimized.few_shot_examples)}")
# Step 5: 测试优化后的 Module
print(f"\n 🧪 测试优化后的 Module:")
tests = [
"这部电影真的很感人",
"无聊透顶的体验",
"价格不高不低",
]
for text in tests:
result = optimized(text=text)
print(f" 输入: {text[:20]}... → {result}")
# Step 6: 对比优化前后
print(f"\n 📊 优化前后对比:")
print(f" 优化前 Prompt 长度: {len(module.prompt)} 字符")
print(f" 优化后 Prompt 长度: {len(optimized.optimized_prompt)} 字符")
print(f" Few-shot 示例: {len(module.few_shot_examples)} → {len(optimized.few_shot_examples)}")
22.7 本章总结
核心要点回顾:
-
DSPy = Programming, Not Prompting
-
Prompt 是编译产物,不是手写字符串
-
代码不变,Optimizer 自动适配不同 LLM
-
三层抽象
-
Signature: 输入输出规范(替代 Prompt 字符串)
-
Module: 可组合的 AI 组件
-
Optimizer: 自动优化 Prompt 和 Few-Shot 示例
-
DSPy × Agent
-
DSPy 优化每个 LLM 调用节点
-
LangGraph 编排整体执行流程
-
两者解耦、各司其职
面试速记:
"DSPy 是什么?"
→ Programming, not Prompting
→ Signature 定义输入输出 → Module 组装 → Optimizer 自动优化
→ 代码不变,换模型自动适配
→ 和 LangChain 互补:DSPy 管 Prompt 优化,LangChain 管流程编排
📝 对应的代码实现
import json
import time
import hashlib
from typing import Optional
from dataclasses import dataclass, field
# ===== 模拟 DSPy 核心概念(不 import dspy)=====
if __name__ == "__main__":
print("╔══════════════════════════════════════════════════════╗")
print("║ 第22章:DSPy ------ Prompt 编译式 Agent 开发 ║")
print("║ Signature · Module · Optimizer · 数据驱动 ║")
print("╚══════════════════════════════════════════════════════╝")
demo_dspy_workflow()
print("\n▶ DSPy Optimizer 类型速查")
print("-" * 50)
opts = [
("BootstrapFewShot", "自动找最佳 few-shot 示例"),
("MIPROv2", "自动探索指令 + 示例组合(最推荐)"),
("BootstrapFinetune", "为每个 Module 自动微调模型"),
("BetterTogether", "组合多个 Optimizer 级联优化"),
]
for name, desc in opts:
print(f" {name:20s} → {desc}")
print("\n▶ DSPy vs LangChain 互补关系")
print("-" * 50)
print(" DSPy: 管 Prompt 优化(LLM 调用的「质量层」)")
print(" LangChain: 管流程编排(Agent 执行的「控制层」)")
print(" 最佳实践: LangGraph 编排 + DSPy 优化")
print("\n✅ 第22章完成!")