想象你经营一家高档餐厅,后厨站着一位无所不知的大厨——大语言模型。这位大厨手艺深不可测,但你必须告诉他“怎么做”。传统方式下,你亲手写小纸条:糖放两勺、醋放一勺、油温七成热、大火三十秒……每换一道菜、每调一次口味,纸条就得重写。这就是今天无数开发者的日常——手写提示词,靠人肉试错。 DSPy 带来了新玩法:你不再写纸条,而是下“目标单”——“来一道酸甜口、外酥里嫩的糖醋排骨,顾客满意度至少 90 分”。后厨里有个“自动品鉴官”(Teleprompter),他会自己设计配方、试菜、打分、迭代,直到菜满意为止。你从“手把手教”升级为“提要求”。 这篇文章,我们就用这座后厨的故事,把 DSPy 的核心概念、声明式编程、自动调优、与 LangChain 的对比,以及它在 RAG 和 Agent 中的落地,一次讲清楚。
学会 DSPy,意味着从“提示词苦力”升级为“系统架构师”——把时间花在设计任务和评估指标上,而不是反复试错提示词。
可视化演示
展示DSPy框架Signature声明式契约、Module组合式模块、Teleprompter自动调优迭代及RAG流水线优化流程。
本节配有交互动效(进入视口后自动加载,约 3KB)。
核心架构:声明式Prompt编程范式 Signature声明任务What,Module定义How,Teleprompter自动调优 从手工试错转为系统化优化,输出最优Prompt Pipeline: SIG → MOD → TEL → OPT 声明式编程将Prompt工程变为可优化系统工程
核心概念
一家 DSPy 餐厅的后厨,靠三样东西运转。第一样是菜单上的“菜品描述”(Signature)——它不写具体烹饪步骤,只写清“这是一道什么样的菜”:输入是顾客的忌口和偏好,输出是成品菜的风味描述。大厨看到这份描述,心中就有数了。第二样是后厨里的“标准设备”(Module)——蒸锅、炒锅、烤箱各司其职:Predict 直接出结果,ChainOfThought 边想边做,Retrieve 去资料库翻食材。第三样是“自动品鉴官”(Teleprompter)——他拿着历史点单记录(数据集),针对每道招牌菜反复试做、评分、修改配方,最终把最优配方固定下来。
技术上讲,Signature 是 DSPy 最基础的抽象:一个类,声明了输入字段、输出字段,以及一句任务指令。你用 Signature 描述后,DSPy 会自动把它扩展成完整提示词,不再需要你手写。Module 是组合单元,任何 Signature 都可以包装进 Predict 或 ChainOfThought 中。Teleprompter 则是连接两者的“编译器+优化器”。
一个 DSPy 程序的生命周期可以浓缩成一个公式:先定义签名并包装成模块,再交给编译器,最后得到优化后的程序。
在后厨里,你不再蹲在灶台边写纸条,而是负责点菜和定标准的老板;写菜谱的活儿,交给了品鉴官。这三样东西——签名、模块、编译器——构成了 DSPy 世界的全部骨架。
- 定义签名模块先用 Signature 声明输入输出,例如把“问题”映射到“答案”,再包装成 Module 作为可执行单元。
- 交给优化器编译把模块和数据集 D 一并传给 Teleprompter,相当于品鉴官拿到设备和历史点单。
- 产出优化程序编译器返回优化好提示词的完整程序,可直接用于推理,就像品鉴官交出定稿菜谱。
声明式编程
传统 Prompt 工程像“手把手教厨师”:你得写一份操作清单——打开冰箱、拿两个鸡蛋、热锅、倒油、翻炒三十秒……每一步漏掉,菜就可能翻车。而 DSPy 是“下目标卡”:我要一份蛋炒饭,粒粒分明,咸淡适中。这就是声明式编程的核心理念——表达“要什么”,不关心“怎么做”。
DSPy 的声明式程序由三要素组成:输入定义 x、输出定义 y、评估标准 f。你把这三样东西写清楚,剩下的一切交给编译器。这套做法的好处立竿见影:代码可复用——同样的签名,换一个数据集就能重新编译出新的提示词;换一个模型(换一位厨师),也不需要重写任何提示词,重新编译一次即可。
声明式与命令式的本质区别,可以用一张对比公式来刻画。
你从“手把手教”变成“提要求”,后厨会自己研究怎么做——这正是从 Prompt Engineer(提示词工程师)升级为 Prompt Architect(提示词架构师)的本质。
- 声明式映射只声明“从 x 到 y”的关系以及质量指标,中间过程由系统推导。
- 命令式步骤传统 Prompt 要求把操作拆成一步步写死的指令序列,人肉维护成本极高。
- 编译桥接DSPy 用编译器把声明式目标自动转化为具体的提示词步骤。
自动调优
品鉴官的工作流程是这样的:先给大厨几个候选配方 → 每道配方做一批菜 → 让顾客打分(评估指标)→ 留下高分配方 → 在高分配方上做细微改动 → 再测。如此循环往复,直到满意度不再上升。这就是 DSPy 自动调优的生动写照。
在 DSPy 里,提示词和少样本示例被当作可优化的参数 θ。Teleprompter 在训练集上反复评估,目标是找到让平均损失最小的那一组参数。BootstrapFewShot 是最常用的优化器之一:它会在训练集中自动挑选少量高质量示例,注入提示词,相当于把好评案例写进菜单——这正是思维导图中“少样本示例学习”的含义。而更复杂的 MIPRO 会同时搜索指令措辞和示例组合。
自动调优的数学本质是经验风险最小化。
以前是人肉网格搜索——改一次提示词,试一次效果;现在则是系统级自动调参,品鉴官比你更勤奋地试菜,而且从不喊累。
- 参数化程序θ 表示提示词或示例等可调参数,M_θ 是使用该参数的 LLM 程序。
- 损失度量ℓ 衡量模型预测 ŷ 与标准答案 y 的差距。
- 均值风险对所有 N 个样本的损失取平均,得到当前参数的平均表现。
- 搜索最优参数Teleprompter 通过采样、搜索等策略找到使平均损失最小的 θ。
对比LangChain
LangChain 像后厨的“标准化流程手册”:规定了先蒸后炒、哪个步骤调用哪个设备,把复杂的多步骤流程串得明明白白。但它不会自动改进菜谱——菜谱仍然是开发者和运营者手写的。DSPy 则像一个“菜品研发系统”,连菜谱本身都会自动迭代。两者不是竞争对手,而是好搭档:用 LangChain 搭后厨骨架,让 DSPy 负责优化核心菜品的配方。
从技术实现看,LangChain 的提示词是提前写死的模板,运行时按模板填充变量;DSPy 则在编译期搜索提示词,运行时使用优化结果。LangChain 的优势在流程控制——多分支、状态管理、丰富的生态;DSPy 的优势在提示词质量——同一个任务,它能找到比你手写更好的措辞。实践中已经有大量项目采用混合方案:LangChain 的链里套一个 DSPy 编译过的模块。
两者的差异可以用一个对比公式直观呈现。
流程手册和菜品研发部,好的餐厅两者都要。选型时不要问“谁替换谁”,而要问“我的瓶颈是流程还是菜谱”。
y_{\text{LangChain}} = \text{LLM}\big(\text{Template}(x)\big), \qquady_{\text{DSPy}} = \text{LLM}\Big(\text{Compile}\big(x; \mathcal{D}\big)\Big)- LangChain 固定模板Template 是开发者手写死的提示词模板,每次运行时原样套用。
- DSPy 编译模板Compile 依据数据集 D 自动搜索并生成提示词。
- 两者对比一个是静态模板靠人调,一个是动态编译靠优化器调,本质上不冲突。
RAG应用
顾客问:“你们能做少糖的甜品吗?”服务员(Retrieve 模块)立刻去菜单库翻出相关的三页(文档片段),连同问题一起递给大厨(LLM)。大厨看着这几页资料,才能给出准确回答——这就是 RAG(检索增强生成)。没有检索这一步,大厨只能凭记忆瞎猜,容易张冠李戴。
在 DSPy 中实现 RAG 极其简洁:用 dspy.Retrieve(k=3) 封装检索,定义一个 RAG 模块整合检索与生成,再用 ChainOfThought(QA) 签名生成答案。更妙的是,整个 RAG 流程作为 Module 传给 Teleprompter 后,连“检索多少条最合适、上下文怎么组织最有效”都会被自动优化——这就是思维导图中的“端到端优化”。
RAG 的核心链路可以用一个公式概括。
整个 RAG 流程不再是一条写死的流水线,而是由品鉴官反复试菜调出来的“招牌菜流程”——这也正是 DSPy 在检索增强应用中最受欢迎的原因。
- 检索上下文Retrieve(q) 从文档库中召回与问题最相关的片段集合。
- 拼接输入⊕ 表示将原始问题与检索上下文拼接成一段完整输入。
- 生成答案LM 基于融合后的上下文生成最终答案,因为有据可依,幻觉大幅减少。
Agent应用
餐厅里来了几位客人,有人忌辣、有人赶时间、有人过生日。店长(Agent)需要快速判断:让采购补货、让大厨改单、让甜点师准备蛋糕。这个“判断和派单”的过程,在 DSPy 中变得可优化。
技术上有三个关键点:第一,工具调用签名化——把每个工具(查库存、订食材、查天气)用 Signature 描述清楚,LLM 才知道什么情况该调哪个工具;第二,推理-行动循环——Agent 先思考再行动,行动结果进入下一轮思考,这是 ReAct 风格的典型写法;第三,多 Agent 协作——每个 Agent 都是一个 DSPy 模块,它们的任务规划策略都可以用 Teleprompter 编译优化。思维导图中的“任务规划编译”说的正是这一点:连“先做什么后做什么”都是学出来的。
Agent 决策的本质是一个策略函数。
店长原来凭经验派单,现在有了品鉴官帮他总结“什么情况派谁最有效”。DSPy 让 Agent 从“规则驱动”走向“数据驱动”,这是智能体工程化的重要一步。
- 状态表示s_t 是当前时刻的对话历史或环境观测,相当于店长掌握的前厅信息。
- 工具描述Tools 是所有可用工具的 Signature 声明列表,相当于员工分工表。
- 动作决策模型根据状态和工具描述输出下一步动作 a_t,即“派谁去办什么事”。
系统化流程
最后,把后厨从“靠大厨灵感”升级为“标准菜品研发流水线”:定义菜品目标 → 收集历史点单 → 设定评分标准 → 研发试菜 → 正式上架 → 收集反馈迭代。这套流程对应 DSPy 的工程化最佳实践——工作流五步法。
具体来说:第一步,把任务定义为目标 Signature;第二步,准备几十到几百条训练数据,不用多,但要有代表性;第三步,设计评估指标,准确率、召回率或业务指标皆可,关键是能反映真实质量;第四步,用 Teleprompter 编译;第五步,在验证集上评估,不合格就补充数据或调整指标后重新编译。成本控制方面,可以限制编译轮次、先用小模型试编译、缓存重复评估结果——这些策略让 DSPy 在真实项目中可控可用。
整个流程的收敛性可以用一个差分公式刻画。
菜品的口味不再靠运气,而是靠流程和机制来保证。DSPy 最大的价值,就是把 Prompt 工程变成一门可重复、可度量、可优化的工程学科。
- 评估得分Score 表示编译后程序在验证集上的指标值,相当于顾客满意度评分。
- 相邻迭代差分Δ_t 衡量第 t 轮相对上一轮的提升幅度,是判断是否继续烧钱的关键。
- 收敛判断当提升幅度趋近于 0,说明已接近上限,停止编译并部署当前最优程序。
AI 应用场景
企业客服知识库问答
阶段:RAG 应用构建与优化
解决的问题:传统客服问答系统需要为“检索改写、上下文整合、答案生成”分别手写提示词,语义稍有变化就要人工重新调参,试错成本极高。DSPy 把三个环节封装成一个 Module,用签名统一描述,交给 Teleprompter 自动优化,检索条数、上下文组织方式都被自动调整。
具体案例:某电商公司用 DSPy 构建售后问答系统,输入几百条历史客服对话作为数据集,定义“答案准确率 + 语气友好度”为评估指标,编译三轮后答案准确率从 68% 提升到 89%,且换成更便宜的模型后只需重新编译,无需重写任何提示词。
智能客服 Agent
阶段:Agent 应用与工具调用
解决的问题:传统 Agent 需要手工设计大量 if-else 规则来决定调用哪个工具,遇到边界情况就容易“派错单”。DSPy 将每个工具改写为 Signature,让 LLM 根据状态自动选择工具,并用 Teleprompter 优化“推理-行动”的决策策略。
具体案例:某餐饮 SaaS 公司的智能客服 Agent,需要处理查订单、改预约、推荐菜品三个工具。用 DSPy 的 Signature 声明工具后,编译优化了动作选择策略,工具调用准确率从 74% 提升到 93%,上线后人工转接率下降一半。
Prompt 运维与持续迭代
阶段:系统化流程与成本控制
解决的问题:生产环境的 Prompt 会随模型升级、数据漂移而效果衰减,传统方式靠人工一个个排查修改,响应极慢。DSPy 让“重新编译”成为标准操作:数据变了、模型换了,跑一次编译即可自动恢复效果。
具体案例:某金融科技公司每月更新一次底层模型,过去每次要花一周重新调提示词。引入 DSPy 后,他们把“月度重新编译”写进运维日历,每次只需半天完成编译和回归测试,还自动生成了新模型的优化提示词版本。
Python 演示
import reimport randomfrom dataclasses import dataclass, field def simple_llm(prompt: str) -> str: """模拟一个极小的 LLM:根据提示词中的关键词返回答案。""" if "酸甜" in prompt: return "糖醋排骨:酸甜口,外酥里嫩" if "少糖" in prompt: return "低糖甜品:甜度减半,保留香气" return "菜单暂未找到匹配菜品" @dataclassclass Signature: """DSPy Signature:声明输入输出,不写提示词细节。""" instruction: str input_field: str output_field: str def build_prompt(self, sample: dict) -> str: return f"{self.instruction}\n{self.input_field}: {sample[self.input_field]}\n请给出{self.output_field}:" @dataclassclass PredictModule: """DSPy Module:封装 Signature 与 LLM 调用。""" signature: Signature fewshot_examples: list = field(default_factory=list) def __call__(self, sample: dict) -> str: prompt = self.signature.build_prompt(sample) if self.fewshot_examples: demo = self.fewshot_examples[0] prompt += f"\n示例:{demo[self.signature.input_field]} -> {demo[self.signature.output_field]}" return simple_llm(prompt) @dataclassclass BootstrapFewShot: """Teleprompter:自动挑选示例并生成优化后的程序。""" def compile(self, module: PredictModule, trainset: list, score_fn) -> PredictModule: scored = [] for sample in trainset: pred = module(sample) score = score_fn(pred, sample[module.signature.output_field]) scored.append((score, sample)) scored.sort(reverse=True, key=lambda x: x[0]) module.fewshot_examples = [s for _, s in scored[:2]] return module def main(): trainset = [ {"dish": "糖醋排骨想要酸甜口味", "result": "糖醋排骨:酸甜口,外酥里嫩"}, {"dish": "低糖版杨枝甘露", "result": "低糖甜品:甜度减半,保留香气"}, {"dish": "少糖提拉米苏", "result": "低糖甜品:甜度减半,保留香气"}, ] sig = Signature( instruction="你是餐厅点单助手,根据顾客需求推荐菜品。", input_field="dish", output_field="result", ) module = PredictModule(sig) print("编译前预测:", module({"dish": "糖醋排骨想要酸甜口味"})) def score_fn(pred: str, gold: str) -> float: return 1.0 if pred == gold else 0.0 teleprompter = BootstrapFewShot() compiled = teleprompter.compile(module, trainset, score_fn) print("编译后预测:", compiled({"dish": "糖醋排骨想要酸甜口味"})) print("自动挑选的示例:", [s["dish"] for s in compiled.fewshot_examples]) if __name__ == "__main__": main()
class Signature:用数据结构声明“输入 dish → 输出 result”,不写提示词细节,对应 DSPy 的 Signature 概念。def build_prompt(self, sample: dict) -> str:根据签名动态生成提示词,开发者不需要手写完整模板。fewshot_examples: list = field(default_factory=list)保存自动挑选出的少样本示例,初始为空。def __call__(self, sample: dict) -> str:让模块像函数一样被调用,对应 DSPy 中 Module 的可执行语义。def compile(self, module, trainset, score_fn):模拟 Teleprompter 的核心编译逻辑:逐条评估并排序。scored.sort(reverse=True, key=lambda x: x[0])按得分降序排列,让高分示例排在前面。module.fewshot_examples = [s for _, s in scored[:2]]取前两个高分样本作为少样本示例,注入后续提示词。compiled = teleprompter.compile(module, trainset, score_fn)编译后的程序可直接调用,且效果优于编译前。print("自动挑选的示例:", ...)展示品鉴官最终选中的“菜单范本”是哪几条。
常见误区
误区我以为有了 DSPy 就再也不用碰任何提示词了,Signature 随便写写就行,反正编译器会自动优化。
正解Signature 的任务指令本质上是提示词的上层抽象,写得越清晰,Teleprompter 的搜索空间就越小、效果越好。就像点菜也要说清楚“不要香菜”一样,声明越明确,后厨越省力。指令描述模糊,编译器再努力也找不对方向。
为何常见:宣传语强调“自动调优”,容易让人误以为机器能兜底一切,但优化需要良好的起点和明确的约束。
误区Teleprompter 会像微调那样自动更新大模型的权重吧?编译过程就是在训练模型。
正解绝大多数 DSPy Teleprompter 只优化提示词和少样本示例,不改变 LLM 的权重(除非你显式使用微调模块)。它做的是 Prompt 层面的优化,用搜索和采样的方式找到更好的提示词。类比品鉴官:他改的是菜谱和配方,不是换厨师的大脑。
为何常见:“编译”这个词听起来像训练,而且 DSPy 是新生事物,容易与微调混淆。
误区既然 DSPy 这么强,我直接把工程里的 LangChain 全部换掉。
正解两者解决的问题不同。LangChain 擅长流程编排——链、分支、记忆、生态;DSPy 擅长提示词自动优化。实践中经常是 LangChain 搭流程外壳、DSPy 优化核心提示词模块。品鉴官再厉害,也需要后厨的流程手册来保证出餐效率。
为何常见:看到对比文章就有“二选一”的思维定式,忽略了工具之间的互补关系。
误区我用 DSPy 编译好提示词后,部署上线就完事了,以后都不用管。
正解如果换了模型、换了数据分布、或新增了工具,原有提示词可能失效,需要重新编译。DSPy 的价值不是“一次编译永远正确”,而是让“重新编译”变得低成本、可自动化。
为何常见:传统提示词工程培养了“写好了就固定”的思维惯性,把编译误当成终态动作。
误区DSPy 自动优化,我随便用个准确率当评估函数就完事了。
正解Teleprompter 的优化方向完全由评估指标决定。如果指标只看准确率,回答风格、安全约束、业务细节全都会被忽略。指标定义错了,优化就会跑偏——就像只用“辣度”给菜品评分,那“要好吃”就没人管了。
为何常见:自动化的幻觉——人容易把“自动”理解为“不需要定义标准”,但自动优化恰恰最依赖标准本身。
面试题
Q1. DSPy 中用来声明“输入输出字段”的组件是?
- A.编译器组件
- B.输入输出签名← 正确答案
- C.思维链推理
- D.向量检索模块
答案:B。Signature 是 DSPy 中用来声明输入字段、输出字段和任务指令的组件,它替代了手写提示词。编译器是 Teleprompter,思维链是 ChainOfThought,向量检索是 Retrieve,它们都不是声明输入输出的组件,故选 B。
Q2. DSPy 中负责自动优化提示词的组件是?
- A.任务签名组件
- B.功能模块组件
- C.自动编译器← 正确答案
- D.数据加载器
答案:C。Teleprompter 是 DSPy 的编译器兼优化器,负责自动生成、评估和挑选提示词。Signature 负责声明任务,Module 负责封装逻辑,Dataset 只是数据容器,都不承担优化职责,故选 C。
Q3. DSPy 相比传统 Prompt 工程的最大转变是?
- A.从声明目标变为手写步骤
- B.从手工试错变为系统调优← 正确答案
- C.从优化提示词变为扩大词表
- D.从大语言模型变为微调小模型
答案:B。传统 Prompt 工程靠人工反复修改提示词,DSPy 将这一过程交给编译器自动优化,这是最本质的变化。A 说反了,C 和 D 与 DSPy 的核心无关,故选 B。
Q4. 关于 LangChain 与 DSPy 的关系,正确说法是?
- A.两者功能完全重叠不能共存
- B.LangChain 负责自动优化提示词
- C.DSPy 可替代所有 LangChain 生态
- D.两者定位互补常被结合使用← 正确答案
答案:D。LangChain 擅长流程编排与生态集成,DSPy 擅长提示词自动优化,实践中常组合使用:LangChain 搭流程框架,DSPy 优化核心提示词模块。A、B、C 都是对两者关系的误读,故选 D。
Q5. DSPy 编译过程真正优化的是?
- A.大模型的内部权重
- B.提示词和示例选择← 正确答案
- C.训练数据集的规模
- D.硬件资源的配置
答案:B。DSPy 的 Teleprompter 优化的是提示词指令和少样本示例等 Prompt 层面参数,不修改大模型权重。A 是微调做的事,C 和 D 与编译无关,故选 B。
Q6. DSPy 中 Retrieve 模块主要负责?
- A.对检索结果进行重排序
- B.从文档库召回相关片段← 正确答案
- C.自动生成向量数据库
- D.训练嵌入模型参数
答案:B。Retrieve 模块封装了检索逻辑,从文档库中召回与问题相关的片段,是 RAG 流程的第一步。重排序是 Reranker 的职责,向量库和嵌入训练都不属于 Retrieve 模块本身,故选 B。
Q7. 最能体现 DSPy 声明式特征的做法是?
- A.详细编写每条提示词规则
- B.声明目标交由编译完成← 正确答案
- C.手动调试每一轮生成输出
- D.不断堆砌接口调用逻辑
答案:B。声明式编程的核心是“声明要什么,而不是怎么做”。B 正是这种心智:用 Signature 声明目标,把怎么做交给编译器。A、C、D 都是典型的命令式思维,故选 B。
Q8. DSPy 的 Teleprompter 能优化哪些对象?
- A.提示词指令文本← 正确答案
- B.少样本示例集合← 正确答案
- C.模型内部的神经元
- D.指令与示例的组合← 正确答案
答案:ABD。Teleprompter 优化的对象包括提示词指令文本、少样本示例集合以及两者的组合策略,这些都是 Prompt 层面的可调参数。它不修改模型内部的神经元,那是微调的任务,故选 A、B、D。
Q9. 下列哪些属于 DSPy 的内置模块?
- A.Predict 预测模块← 正确答案
- B.ChainOfThought 模块← 正确答案
- C.MySQL 数据库模块
- D.Retrieve 检索模块← 正确答案
答案:ABD。DSPy 提供了 Predict、ChainOfThought、Retrieve 等内置 Module,分别用于直接预测、带推理链的预测和文档检索。MySQL 是外部数据库系统,不属于 DSPy 内置模块,故选 A、B、D。
Q10. 生产环境使用 DSPy 的合理实践包括?
- A.设计业务对齐的评估指标← 正确答案
- B.编译完成后不再更新模型
- C.平衡编译成本与性能收益← 正确答案
- D.在验证集反复调参不做测试
答案:AC。评估指标决定优化方向,必须与业务目标对齐;编译会消耗 token 和时间,需要控制轮次、用小模型试编译来平衡成本。B 不正确,因为换模型或数据变了就需要重新编译;D 不正确,因为忽略独立测试集会导致过拟合。故选 A、C。