跳到主要内容

DSPy — 架构与原理

30 秒导读: DSPy 把「写 prompt」变成「写程序」。你只声明每一步的输入输出(叫 签名 Signature),用可组合的模块 Module 把它们拼成管线,然后交给一个优化器 (teleprompter)自动去调那些原本要你手搓的东西——few-shot 样例、指令文字。核心口号: Programming, not prompting(编程,而非提示)。

1. 这是什么(零基础也能懂)

一句话定义: DSPy 是一个让你用 Python 声明式地搭 LLM 管线、再用算法自动优化 prompt 和权重的框架(名字 = Declarative Self-improving Python)。

它解决谁的什么问题。 假设你在搭一个问答系统:先检索资料、再让模型推理、最后给答案。 传统做法是手写一大段 prompt,反复试参数、调措辞、塞 few-shot 例子——脆、难维护、换个模型就得重调。

DSPy 让你换一种写法:

  • 你只说每一步要什么输入、产出什么输出question, context -> answer),不写措辞;
  • 措辞、格式、few-shot 例子由框架的 Adapter优化器负责;
  • 想让它更准?跑一个优化器,喂训练集和一个打分函数,它自动把 prompt 调好。

它能做什么。

  • 用一行字符串或一个类声明一个 LLM 任务(签名)。
  • 把多个任务组合成程序(RAG、agent、分类器、多跳推理……)。
  • 自动给每个步骤的 prompt 注入 few-shot 样例、改写指令。
  • 在任意 LiteLLM 支持的模型上跑同一份程序(OpenAI / Anthropic / 本地)。
  • Evaluate 在数据集上并行打分。

用起来什么样。 一个最小的真实例子:

import dspy

# 1) 配一个语言模型(全局默认)
dspy.configure(lm=dspy.LM("openai/gpt-4o-mini"))

# 2) 声明一个任务:输入 question,输出 answer——不写任何 prompt 措辞
qa = dspy.Predict("question -> answer")

# 3) 直接调用,像调一个函数
pred = qa(question="法国的首都是哪里?")
print(pred.answer) # -> "巴黎"

你从没写过 "You are a helpful assistant..." 这种 prompt。"question -> answer" 这串字符串 就是签名,DSPy 在背后把它渲染成一个带格式约定的 prompt,调用模型,再把模型的文字解析回 pred.answer 这个字段。

一句话直觉/类比。 把 DSPy 想成 PyTorch + 编译器,但作用对象是 prompt

  • Signature ≈ 函数签名 / 类型声明(这一步吃什么、吐什么)。
  • Modulenn.Module(可组合、可嵌套、有「参数」)。
  • Predictdemos(few-shot 样例)和签名的 instructions(指令文字)≈ 可学习的参数
  • teleprompter(优化器)≈ 优化器/编译器:拿训练集和一个 metric,把那些「参数」调好。

本节不碰底层。记住一句话就够:你声明意图,DSPy 负责把它编译成能跑、且被调优过的 prompt。

2. 顶层全景(它大概怎么转)

2.1 五个核心部件

DSPy 的代码量很大,但价值集中在五类部件上:

部件一句话职责在哪个文件
Signature声明一步任务的输入/输出字段 + 指令(类型声明)dspy/signatures/signature.py
Predict / Module把签名变成一次可调用的 LM 步骤;模块可组合成程序dspy/predict/predict.pydspy/primitives/module.py
Adapter把「签名+输入」渲染成 prompt 消息,再把 LM 文本解析回字段dspy/adapters/base.pydspy/adapters/chat_adapter.py
LM (client)实际打模型 API(默认走 LiteLLM),返回文本/工具调用dspy/clients/base_lm.pydspy/clients/lm.py
Teleprompter优化器:拿训练集+metric,自动调 demos / instructionsdspy/teleprompt/*.py

2.2 一次调用的数据流(推理时)

怎么读这张图:从上往下是一次 qa(question=...) 的生命周期,左侧是「你写的东西」, 右侧是「DSPy 在背后做的事」。

你的代码 DSPy 内部
───────── ─────────
qa = dspy.Predict("q -> a") ──► 解析签名字符串 → Signature 类(字段+类型+指令)
qa(question="...") ──► Module.__call__ → Predict.forward


选 LM + Adapter (默认 ChatAdapter)

▼ format()
把签名+demos+输入渲染成 chat messages
(system: 字段说明+格式约定; user: 你的输入)

▼ 调 LM (LiteLLM)
模型返回纯文本

▼ parse()
按 [[ ## field ## ]] 标记切出每个输出字段


pred.answer ◄────────────────── Prediction(answer=...) 回传

关键点:你只碰最上和最下两行(声明 + 拿结果)。中间「渲染 prompt → 调模型 → 解析」 全由 Adapter 封装,所以你换模型、换格式(Chat / JSON / XML)都不用改业务代码。

2.3 优化时多了一圈(编译)

推理只是「跑一遍」。DSPy 的招牌是优化(compile)——把上面那个程序当成有「参数」的对象去调:

trainset + metric 优化器 (teleprompter)
──────────────── ────────────────────
┌──► 在训练集上跑学生程序,收集成功的执行轨迹
optimizer.compile( │ (BootstrapFewShot: 把成功轨迹变成 few-shot demos)
student=program, │ │
trainset=..., ─────┤ ▼ 或
metric=...) │ 搜索更好的指令文字 (MIPROv2 / GEPA)
│ │
└──◄ 返回一个「参数被调好」的新程序

同一个 program,编译前后接口完全一样,只是内部每个 Predictdemos 和签名 instructions 被换成了更好的。这就是「Self-improving」。

2.4 主线走一遍(不进代码)

  1. 你写 dspy.Predict("question -> answer") 或一个 dspy.Module 子类。
  2. dspy.configure(lm=...) 设好模型。
  3. 调用程序 → Adapter 渲染 prompt → LM 出文本 → Adapter 解析 → 得到 Prediction。 4.(可选)拿一个优化器 + 训练集 + metric,compile 出一个更准的程序。
  4. program.save(...) 存下被优化的状态,下次 load 回来直接用。

3. 阅读地图(建议顺序)

按由浅入深读:

  1. 01-signatures-and-predict.md — 从最小原子开始:签名怎么 声明任务、"a, b -> c" 字符串怎么被解析成 pydantic 模型、Predict 如何把签名变成一次 LM 调用。读完你能写出并调用任意单步任务。
  2. 02-adapters-format-parse.md — DSPy 工程含量最高的一层: Adapter 如何把签名渲染成 prompt([[ ## field ## ]] 协议),如何把 LM 的纯文本切回 结构化字段,以及 ChatAdapter 失败时回退 JSONAdapter 的设计。
  3. 03-modules-and-agents.md — Module/Parameter 体系怎么递归发现 子模块和「参数」、ChainOfThought 怎么只是「在签名前插一个 reasoning 字段」、ReAct 智能体 怎么用签名多态搭出一个工具调用循环。
  4. 04-optimizers-teleprompt.md — 招牌功能:BootstrapFewShot 如何用「教师跑成功轨迹」自举 few-shot 样例、MIPROv2 如何联合搜索指令+样例、GEPA 如何用 反思式 prompt 进化。

如果你只想懂「DSPy 凭什么不一样」,读 §1 + §2 + 第 4 章开头即可。

4. 代码地图(导航索引)

主题文件关键符号
签名元类 / 字段顺序dspy/signatures/signature.pySignatureMetamake_signature_parse_signature
输入/输出字段dspy/signatures/field.pyInputFieldOutputFieldmove_kwargs
单步预测dspy/predict/predict.pyPredictPredict.forward_forward_preprocess
模块基类dspy/primitives/module.pyModuleProgramMetanamed_predictors
参数遍历dspy/primitives/base_module.pynamed_parametersdeepcopysaveload
Adapter 主流程dspy/adapters/base.pyAdapter.__call__format_call_postprocess
Chat 协议dspy/adapters/chat_adapter.pyChatAdapterparsefield_header_pattern
JSON 回退dspy/adapters/json_adapter.pyJSONAdapterJSONAdapter.parse
LM 客户端dspy/clients/base_lm.pyBaseLMBaseLM.__call__forward
CoT / ReActdspy/predict/chain_of_thought.pydspy/predict/react.pyChainOfThoughtReActReAct.forward
数据容器dspy/primitives/example.pydspy/primitives/prediction.pyExamplePredictionwith_inputs
优化器基类dspy/teleprompt/teleprompt.pyTelepromptercompile
自举样例dspy/teleprompt/bootstrap.pyBootstrapFewShot_bootstrap_one_example
指令+样例联合搜索dspy/teleprompt/mipro_optimizer_v2.pyMIPROv2AUTO_RUN_SETTINGS_propose_instructions
反思式进化dspy/teleprompt/gepa/gepa.pyGEPAGEPA.compile
评估dspy/evaluate/evaluate.pyEvaluateEvaluationResult