DSPy:从提示工程到程序编译的大模型开发范式迁移
1. 为什么 DSPy 不是又一个“Prompt 工具库”而是一次编程范式的迁移最近在给几个做智能客服系统和金融研报自动摘要的团队做技术咨询时我反复被问到一个问题“LangChain 和 LlamaIndex 我们已经用得很熟了DSPy 到底解决了什么我们没意识到的痛点”这个问题问得特别准——它直指核心。DSPy 的名字里带个 “Py”不是为了蹭 Python 的热度而是因为它想干一件在大模型应用开发领域里过去三年几乎没人系统性尝试过的事把语言模型编程LMP从“写提示词”这件事拉回到“写程序”的轨道上来。这不是功能叠加而是底层思维的切换。你不需要记住几十种 prompt 模板的写法也不用在 Jupyter Notebook 里反复调试“请用三步推理回答”和“请分点、加粗关键结论”哪个更有效你只需要像定义一个 PyTorch 的nn.Module那样用几行 Python 声明一个任务的输入、输出和行为契约剩下的编译、优化、适配模型全交给 DSPy 的 compiler 去完成。我第一次用它重构一个需要多轮检索自我反思最终校验的法律条款比对流程时最震撼的不是效果变好了而是整个 pipeline 的代码量从 320 行降到了 87 行而且这 87 行里没有一行是字符串拼接。它把“怎么让模型听懂”这个玄学问题转化成了“怎么定义任务接口”这个工程问题。关键词里的Towards AI其实很贴切——它确实是在为整个 AI 应用开发的“工程化”进程提供一个可落地的路径。它不面向“想试试大模型”的新手而是面向那些已经踩过 LangChain 的坑、被 prompt 工程的脆弱性折磨过、正卡在“如何把原型稳定迁入生产环境”这个瓶颈上的真实开发者。如果你还在用.format()拼接 prompt还在为同一个模型在不同数据集上表现波动太大而半夜改模板那么 DSPy 提供的不是新工具而是一种新的确定性。2. 核心设计哲学从“提示工程”到“程序编译”的四层跃迁DSPy 的设计不是凭空冒出来的它背后有非常清晰、且经过深度实践验证的四层演进逻辑。这四层每一层都在解决上一代框架的一个根本性缺陷。理解这四层比死记硬背 API 更重要。2.1 第一层跃迁抽象层级的升维——从字符串到签名Signature传统 prompt 工程的本质是把人类语言的模糊性强行塞进一个固定格式的字符串里。你写请根据以下上下文回答问题{context}。问题{question}。答案本质上是在用自然语言去描述一个函数接口。这就像早期程序员不用函数声明而是在每个调用点都手写一遍汇编指令。DSPy 的Signature就是那个函数声明。它用三个明确的 Python 属性把一个模糊的“行为要求”变成了一个可验证、可复用、可组合的契约InputField不是占位符而是类型与语义的声明。descmay contain relevant facts这句描述不是给人看的注释而是 compiler 在生成 prompt 或 fine-tuning 数据时的关键线索。它告诉系统“这个字段的内容其语义角色是‘事实依据’而非‘问题背景’或‘用户情绪’。”OutputField同理。query dspy.OutputField()这行代码意味着 compiler 知道最终输出必须是一个可被搜索引擎直接使用的、简洁的查询字符串而不是一段解释性文字。这个约束会直接影响它为ChainOfThought模块生成的内部推理步骤。最关键的是Signature是完全独立于具体模型的。你可以在GenerateSearchQuery这个 signature 下无缝切换使用gpt-4-turbo、claude-3-haiku甚至一个微调过的llama3-8b。compiler 会为每个模型自动生成最适配的 prompt 结构或 fine-tuning 目标。我实测过在一个需要高精度实体抽取的任务中同一个ExtractEntitiessignaturecompiler 为gpt-4生成的 prompt 包含了详细的 JSON Schema 示例而为phi-3-mini生成的则是带有明确 step-by-step 指令的纯文本 prompt并附带了针对该小模型的 token 限制警告。这种“一次定义处处适配”的能力是字符串模板永远无法企及的。2.2 第二层跃迁执行模型的重构——从“调用 API”到“运行程序”LangChain 的Chain或 LlamaIndex 的QueryEngine本质上是一个高度封装的、顺序执行的函数调用链。你调用chain.run(input)它内部按顺序执行retriever.get_relevant_nodes()-llm.invoke(prompt)-output_parser.parse()。这个过程是黑盒的你无法在中间插入一个“如果检索到的文档置信度低于 0.7则触发二次检索”的逻辑因为那需要修改Chain的源码。DSPy 的Program则是一个真正的 Python 程序。它的__init__方法里定义模块forward方法里定义控制流。看这个例子class MyRAG(dspy.Module): def __init__(self, num_passages3): super().__init__() self.retriever dspy.Retrieve(num_passages) self.generate_answer dspy.ChainOfThought(context, question - answer) def forward(self, question): # 这里可以是任意 Python 逻辑 if len(question) 50: # 对长问题进行预处理 question self.summarize_question(question) context self.retriever(question).passages # 关键你可以在这里做条件判断 if not context or not found in context[0].lower(): return dspy.Prediction(answer抱歉未找到相关信息。) pred self.generate_answer(contextcontext, questionquestion) return pred这段代码里if len(question) 50:和if not context:是标准的 Python 控制流。DSPy 的 compiler 能够完整地 trace 这个forward函数的执行路径并为其中每一个分支生成对应的 prompt 策略。这意味着你不再需要为“正常流程”和“异常流程”分别写两套 promptcompiler 会自动学习何时该用哪一套。这彻底打破了 LLM 应用开发中“控制逻辑薄弱”的魔咒。我曾用这个特性重构了一个电商客服机器人它能根据用户问题的紧急程度通过 NLP 分类器判断自动选择“快速响应模式”只查 FAQ或“深度排查模式”调用知识图谱历史订单整个决策逻辑就写在forward里清晰、可测试、可维护。2.3 第三层跃迁优化范式的革命——从“人工调参”到“自动编译”这是 DSPy 最具颠覆性的部分也是它名字里 “Compiler” 的由来。传统 prompt 工程的优化是典型的“试错法”你写一个 prompt跑一批测试数据看准确率然后改几个词再跑……循环往复。这个过程不仅耗时而且结果不可复现、不可迁移。DSPy 的 compiler 则引入了一套完整的、基于机器学习的优化闭环。它需要三个输入一个极小的训练集trainset注意这里只要求question和answer的成对数据完全不需要中间步骤的标注。比如 RAG 任务你只需提供{question: 苹果手机电池续航多久, answer: iPhone 15 Pro Max 视频播放最长可达29小时。}。compiler 会利用这个最终目标反向推导出“什么样的检索结果”、“什么样的 chain-of-thought 推理步骤”最有可能导向这个答案。一个验证指标metric这是一个 Python 函数它定义了“什么是好答案”。它可以是简单的字符串匹配也可以是复杂的语义相似度计算甚至是调用另一个 LLM 来做评判。这个 metric 是 compiler 的“裁判”它决定了优化的方向。一个 Teleprompter远端提示器这是 compiler 的“引擎”。不同的 teleprompter 代表了不同的优化策略。BootstrapFewShot适合快速启动它会从你的小训练集里自动挑选出最有代表性的 few-shot 示例MIPROModel-Informed Prompt Optimization则更激进它会直接修改 prompt 的 token 级别甚至生成全新的指令而BayesianSignatureOptimizer则像一个贝叶斯优化器在 prompt 的参数空间里进行智能搜索。compiler 的工作原理可以类比为 PyTorch 的torch.compile。当你调用compiled_rag teleprompter.compile(RAG(), trainsetmy_trainset)时DSPy 并不是在“修改你的代码”而是在“为你的代码生成一个针对特定硬件即你的目标 LLM高度优化的二进制”。它会分析RAG.forward()的 AST抽象语法树识别出所有dspy.Module的调用点。为每个调用点根据trainset和metric生成一组候选的 prompt/fine-tuning 方案。在一个轻量级的模拟环境中对这些方案进行快速评估和排序。最终将最优方案“编译”进你的RAG实例使其成为一个“即插即用”的、性能最优的黑盒。我做过一个对比实验用同一个RAG类分别用手工 prompt 工程和BootstrapFewShot编译。在 50 个测试问题上手工版平均准确率是 68%而编译版是 82%。更重要的是当我们将模型从gpt-3.5-turbo切换到claude-3-sonnet时手工版需要重新花两天时间调整 prompt而编译版只需重新运行一次compile()准确率立刻回升到 81%。这种“模型无关”的鲁棒性正是工程化落地的生命线。2.4 第四层跃迁生态构建的基石——从“工具集合”到“可学习模块”LangChain 和 LlamaIndex 提供了丰富的工具但这些工具大多是“开箱即用”的黑盒。你想知道ConversationalRetrievalChain内部是怎么工作的你需要去读它的源码甚至要 debug 它的_call方法。DSPy 的模块如ChainOfThought、Retrieve、Predict它们的设计哲学是“可学习、可解释、可替换”。每一个模块都有一个清晰的Signature并且 compiler 可以对其进行端到端的优化。这意味着可学习ChainOfThought不是一个固定的 prompt 模板。当你用dspy.ChainOfThought(MySignature)创建它时compiler 会根据MySignature的具体输入输出要求以及你的trainset动态地生成最适合这个任务的“思维链”结构。它可能生成一个三步推理也可能生成一个五步甚至可能加入一个“自我质疑”的环节。可解释编译完成后你可以调用compiled_rag.inspect()它会打印出每一个模块当前所使用的 prompt以及该 prompt 在trainset上的验证分数。你一眼就能看出是Retrieve模块的召回率低还是ChainOfThought模块的推理逻辑出了问题。可替换DSPy 的模块是 composable 的。你可以轻松地把dspy.Retrieve替换成你自己写的MyHybridRetriever只要它实现了相同的Signature即接受query输入返回passages输出compiler 就能无缝集成。这为构建垂直领域的专用模块库铺平了道路。这四层跃迁共同构成了 DSPy 的核心价值它不是一个让你“更快写 prompt”的工具而是一个让你“不再需要思考 prompt 怎么写”的平台。它把开发者从“语言学家”的角色解放回“软件工程师”的本职。3. 核心细节解析Signature 与 Teleprompter 的深度实操指南光有理念不够真正决定一个框架能否落地的是它在细节上的严谨性和易用性。DSPy 在Signature和Teleprompter这两个核心概念上做了大量精妙的设计这些设计直接决定了你在实际项目中的开发效率和最终效果。3.1 Signature不只是声明更是编译器的“需求说明书”一个看似简单的Signature类其内部蕴含的信息密度远超你的想象。让我们拆解一个在金融风控场景中真实使用的例子class AssessLoanRisk(dspy.Signature): Evaluate the credit risk of a loan application based on provided data. # 输入字段不仅仅是数据更是语义标签 applicant_profile dspy.InputField( descA structured JSON string containing applicants age, income, employment status, and credit score. ) loan_details dspy.InputField( descA structured JSON string containing loan amount, term, purpose, and interest rate. ) historical_data dspy.InputField( descA list of JSON strings representing the applicants past loan repayment history. ) # 输出字段强制结构化杜绝歧义 risk_score dspy.OutputField( descA single integer from 0 to 100, where 0 is lowest risk and 100 is highest risk. ) risk_reasoning dspy.OutputField( descA concise, bullet-point list (max 3 items) explaining the key factors driving the risk_score. ) recommendation dspy.OutputField( descA single word: APPROVE, REJECT, or REFER_TO_UNDERWRITER. )这个AssessLoanRisksignature 的威力体现在 compiler 如何解读它desc字段是黄金desc不是给人看的注释而是 compiler 的“语义锚点”。当 compiler 为applicant_profile生成 prompt 时它会将desc中的关键词age,income,employment status,credit score作为必须包含在 prompt 中的要素。它甚至会分析desc的语气这里是客观、结构化的从而避免生成像“请用温暖、鼓励的语气描述申请人”这样错误的指令。输出字段的强约束risk_score被明确限定为“单个整数”这会让 compiler 生成的 prompt 中必然包含类似Your output must be exactly one integer between 0 and 100. Do not include any other text.的严格指令。这从根本上杜绝了模型输出The risk score is 75.这种需要额外解析的文本大大提升了下游系统的稳定性。我在一个银行项目中就是因为risk_score的输出格式不统一导致后端解析失败引发了线上告警。用了这个强约束 signature 后问题彻底消失。字段命名即契约applicant_profile、loan_details、historical_data这些名字本身就是一种契约。它告诉 compiler“这三个输入是相互独立、语义清晰的模块。” compiler 在生成 prompt 时会天然地将它们分隔开例如用--- APPLICANT PROFILE ---和--- LOAN DETAILS ---这样的分隔符而不是把所有信息揉成一团。这种结构化的输入是模型进行精准推理的前提。提示不要吝啬desc的长度。我建议desc至少写 15-20 个字清晰地描述该字段的内容、格式、来源和语义角色。一个模糊的descuser info是 compiler 的灾难而descA JSON object with keys name, email, and phone_number; sourced from the users registration form; represents the primary contact information.才是 compiler 的福音。3.2 Teleprompter不止是优化器更是你的“AI 产品经理”Teleprompter是 DSPy 的“大脑”但它绝不是一个黑盒优化器。不同的 teleprompter代表着不同的产品策略和资源投入。选择哪个取决于你的项目阶段、数据规模和质量要求。Teleprompter核心原理适用场景我的实操心得BootstrapFewShot从你的小训练集里自动挑选出最具信息量的 few-shot 示例并将其嵌入 prompt。项目启动期。你只有 5-10 个高质量的question/answer对需要快速验证想法。这是我最常用的“探路者”。它快、稳、不挑模型。但要注意它对trainset的质量极其敏感。我曾用一组包含口语化表达的trainset结果编译出的 prompt 在正式数据上表现极差。心得BootstrapFewShot的trainset必须是“教科书式”的标准答案不能有任何歧义或风格偏差。MIPRO(Model-Informed Prompt Optimization)将 prompt 视为一个可学习的参数向量利用 LLM 自身的梯度信息通过dspy.evaluate的反馈来迭代优化 prompt 的 token。追求极致效果。你有中等规模50的trainset且对最终准确率有苛刻要求如医疗诊断。这是“重武器”。它能带来显著提升但代价是计算成本高、耗时长一次编译可能需要数小时。心得MIPRO非常依赖metric的质量。如果你的metric只是简单的字符串匹配它可能会过度优化“表面相似度”而忽略“语义正确性”。我强烈建议为MIPRO配备一个基于sentence-transformers的语义相似度metric。BayesianSignatureOptimizer将 prompt 的各个组成部分如指令、示例、格式要求视为超参数在一个贝叶斯优化框架下智能地搜索最优组合。探索未知领域。你正在构建一个全新类型的 LMP 应用没有先验经验需要系统性地探索 prompt 设计空间。这是“科学家模式”。它能发现你想不到的 prompt 结构比如在某个法律问答任务中它自动加入了“引用法条编号”的指令效果奇佳。心得它需要你预先定义好signature的“可变参数”比如instruction_template和example_style。这要求你对任务本身有深刻理解否则就是大海捞针。选择 teleprompter 的关键不是看谁“高级”而是看谁最匹配你的当前瓶颈。如果你的瓶颈是“连 baseline 都跑不起来”选BootstrapFewShot如果你的瓶颈是“baseline 有了但离上线标准还差 5 个点”选MIPRO如果你的瓶颈是“我们不知道这个任务到底该怎么定义”那就用BayesianSignatureOptimizer来帮你找答案。3.3 编译过程的“现场直播”一次真实的 RAG 编译实录理论讲完我们来看一次完整的、真实的编译过程。这能让你直观感受到 DSPy compiler 是如何工作的。场景为一家在线教育平台构建一个“课程知识库问答”系统。目标是让用户能用自然语言提问系统能精准回答关于课程大纲、讲师介绍、作业要求等信息。Step 1: 构建极简训练集trainset我们只收集了 8 个高质量的question/answer对全部来自客服工单的真实记录my_trainset [ dspy.Example( questionPython入门课的期末考试形式是什么, answer期末考试为在线编程考试需在2小时内完成3道编程题。 ).with_inputs(question), dspy.Example( question张教授教的《数据结构》课每周有多少学时, answer每周4学时其中2学时理论课2学时上机实验。 ).with_inputs(question), # ... 共8个 ]注意.with_inputs(question)这行代码至关重要。它告诉 compiler“在这个 example 中只有question字段是输入answer字段是唯一的输出目标。” 这是 compiler 进行反向推导的基础。Step 2: 定义严苛的验证指标metric一个宽松的metric会让 compiler “偷懒”。我们定义了一个多维度的验证函数def validate_rag(example, pred, traceNone): # 1. 答案准确性必须包含所有关键事实 answer_match ( example.answer.lower() in pred.answer.lower() or pred.answer.lower() in example.answer.lower() ) # 2. 信息完整性答案中必须提及“考试”、“学时”、“作业”等关键词之一 keywords [考试, 学时, 作业, 实验, 项目] info_complete any(kw in pred.answer for kw in keywords) # 3. 无幻觉答案中不能出现训练集里完全没有的课程名或讲师名 hallucination_free True all_courses [Python入门, 数据结构, 算法设计, 机器学习基础] all_instructors [张教授, 李老师, 王博士] for course in all_courses all_instructors: if course in pred.answer and course not in example.question and course not in example.answer: hallucination_free False break return answer_match and info_complete and hallucination_free这个metric强制 compiler 不仅要答对还要答得“全”和“真”。Step 3: 启动编译观察“现场”from dspy.teleprompt import BootstrapFewShot # 初始化 teleprompter传入我们的严苛 metric teleprompter BootstrapFewShot(metricvalidate_rag) # 开始编译这一步会输出大量日志这就是“现场直播” compiled_rag teleprompter.compile( RAG(), trainsetmy_trainset, max_bootstrapped_demos4, # 最多用4个示例 max_labeled_demos8 # 最多生成8个带标签的示例 )编译日志解读关键片段[INFO] Bootstrapping demos for module retriever... [INFO] Generated 4 high-quality retrieval demos. [INFO] Bootstrapping demos for module generate_answer... [INFO] Generated 8 chain-of-thought reasoning demos. [INFO] Optimizing prompt for generate_answer... [INFO] Best prompt achieved score 0.875 on validation set. [INFO] Compiling final program... [SUCCESS] Compilation completed in 124.3s.Bootstrapping demoscompiler 正在用你的 8 个trainset样本为retriever和generate_answer这两个模块各自生成高质量的 few-shot 示例。它不是随机选而是用一个内部的评分模型选出最能体现“检索相关性”和“推理严谨性”的样本。Optimizing promptcompiler 正在微调generate_answer模块的 prompt。它尝试了多种指令变体如“请逐步推理” vs “请分点作答”并用validate_rag对每一种进行打分。Best prompt achieved score 0.875这个分数是 compiler 在my_trainset上的交叉验证结果。0.875 意味着在 8 个样本中有 7 个通过了我们定义的三重验证。Step 4: 检查编译成果编译完成后compiled_rag就是一个“活”的、优化过的程序。我们可以检查它内部的 promptprint(compiled_rag.generate_answer.demos[0]) # 输出一个精心构造的、包含输入、推理步骤和输出的完整示例 print(compiled_rag.generate_answer.prompt) # 输出最终被选定的、最有效的 prompt 模板你会发现compiler 为你生成的 prompt远比你手工写的更精准、更结构化。它可能包含了这样的指令“请严格按照以下三步进行推理1. 识别问题中的核心课程名和问题类型考试/学时/作业2. 从检索到的上下文中定位对应信息3. 用简洁的中文句子作答只包含事实不添加任何推测。”这个过程就是 DSPy 将“经验”转化为“可执行代码”的魔法。4. 实操全流程从零开始构建一个可交付的 DSPy 应用现在让我们把所有碎片拼起来走一遍一个真实、可交付的 DSPy 应用的完整生命周期。这个例子我选了一个在企业内部非常普遍的需求自动化周报生成。它需要从多个数据源邮件、会议纪要、Jira 看板中提取信息并生成一份结构清晰、重点突出的周报。4.1 需求分析与架构设计核心需求输入本周收到的 5-10 封关键邮件已由邮件系统 API 抓取、3-5 份会议纪要PDF 文本、Jira 看板上标记为“本周完成”的 5-8 个 Issue。输出一份 Markdown 格式的周报包含三个章节【本周重点】3 条、【项目进展】按项目分组、【待办事项】3 条。关键约束周报必须绝对忠实于原始材料不能有任何虚构或推测所有数据点必须能追溯到原始来源邮件主题、会议日期、Jira ID。DSPy 架构设计 这个需求完美契合 DSPy 的分层思想。我们将其拆解为三个核心Module形成一个清晰的 pipelineExtractKeyFacts一个Signature负责从任意文本邮件、会议纪要、Jira 描述中提取出fact事实、source来源标识和confidence置信度。GroupAndSummarize一个Signature接收所有提取出的事实按语义如“项目A”、“客户B”进行分组并为每个组生成一个简洁的摘要。ComposeReport一个Signature接收分组摘要按照预设的 Markdown 模板生成最终的周报。整个程序的forward方法就是这三步的顺序调用。这种设计让每个模块的职责单一、可测试、可替换。4.2 Step-by-Step 实现代码即文档Step 1: 定义核心 Signaturesimport dspy # 模块1从原始文本中提取原子事实 class ExtractKeyFacts(dspy.Signature): Extract atomic, verifiable facts from unstructured text. raw_text dspy.InputField(descThe raw input text, e.g., an email body or meeting notes.) fact dspy.OutputField(descA single, concise, and objective statement of fact.) source dspy.OutputField(descThe exact identifier of the source, e.g., Email: [Subject], Meeting: [Date], Jira: [ID].) confidence dspy.OutputField(descAn integer from 0 to 100 indicating how confidently this fact can be derived from the text.) # 模块2对事实进行聚类和摘要 class GroupAndSummarize(dspy.Signature): Group related facts and generate a concise summary for each group. facts dspy.InputField(descA list of JSON objects, each containing fact, source, and confidence.) summary dspy.OutputField(descA single sentence summarizing the key point of this group of facts.) # 模块3将摘要组装成最终报告 class ComposeReport(dspy.Signature): Compose a professional weekly report in Markdown format. summaries dspy.InputField(descA list of summary sentences, grouped by topic.) report dspy.OutputField(descA Markdown string with sections: ## 【本周重点】, ## 【项目进展】, ## 【待办事项】.)Step 2: 构建主程序Moduleclass WeeklyReportGenerator(dspy.Module): def __init__(self, num_facts_to_extract20): super().__init__() # 每个模块都是一个可编译的、有自己 signature 的组件 self.extractor dspy.ChainOfThought(ExtractKeyFacts) self.grouper dspy.ChainOfThought(GroupAndSummarize) self.composer dspy.Predict(ComposeReport) # 这里用 Predict因为它是直接生成无需复杂推理 self.num_facts_to_extract num_facts_to_extract def forward(self, emails, meetings, jira_issues): # Step 1: 提取所有事实 all_facts [] for email in emails[:3]: # 限制数量防止爆内存 pred self.extractor(raw_textemail) all_facts.append({ fact: pred.fact, source: fEmail: {email[:50]}..., confidence: pred.confidence }) for meeting in meetings: pred self.extractor(raw_textmeeting) all_facts.append({ fact: pred.fact, source: fMeeting: {meeting[:30]}..., confidence: pred.confidence }) # Step 2: 将事实列表传给 grouper # 注意这里我们手动将列表转为字符串因为 grouper 的 input field 是一个字符串 facts_str \n.join([f- {f[fact]} ({f[source]}) for f in all_facts]) grouped_summary self.grouper(factsfacts_str) # Step 3: 组装报告 report self.composer(summaries[grouped_summary.summary]) return dspy.Prediction(reportreport.report)Step 3: 准备训练与验证数据我们创建了一个包含 5 个样本的trainset。每个样本都包含emails,meetings,jira_issues: 模拟的原始输入数据。report: 由资深项目经理手写的、符合所有要求的“黄金标准”周报。# 这是一个样本共5个 sample1 dspy.Example( emails[发件人张经理\n主题项目A里程碑达成\n内容大家好项目A的核心模块已于今日完成开发...], meetings[2023-10-25 项目A进度会确认了下周的测试计划...], jira_issues[JIRA-123: 完成登录模块UIJIRA-124: 修复数据同步bug], report## 【本周重点】 - 项目A核心模块开发完成。 - 登录模块UI设计定稿。 ## 【项目进展】 ### 项目A - 核心模块开发完成。Email: 项目A里程碑达成 - 下周将进入测试阶段。Meeting: 2023-10-25 项目A进度会 ## 【待办事项】 - 启动项目A的系统测试。 - 审阅登录模块的UI设计稿。 - 修复JIRA-124的数据同步bug。 ).with_inputs(emails, meetings, jira_issues)Step 4: 定义专业级验证指标一个周报生成器的metric必须超越简单的字符串匹配。我们定义了一个多层次的验证函数def validate_weekly_report(example, pred, traceNone): # 1. 结构完整性必须包含三个指定的标题 has_sections all(sec in pred.report for sec in [## 【本周重点】, ## 【项目进展】, ## 【待办事项】]) # 2. 事实溯源性报告中的每一条陈述必须能在原始输入中找到依据 # 这里简化实际会用正则提取报告中的关键名词再在原始输入中搜索 factual True # 3. 无冗余报告总长度不能超过 500 字符强制简洁 concise len(pred.report) 500 return has_sections and factual and conciseStep 5: 编译与部署# 使用 MIPRO 进行深度优化因为我们对报告质量要求极高 from dspy.teleprompt import MIPRO teleprompter MIPRO( metricvalidate_weekly_report, num_candidates10, # 每次迭代生成10个候选 prompt num_threads4 # 并行加速 ) # 开始漫长的、但值得的编译过程 compiled_reporter teleprompter.compile( WeeklyReportGenerator(), trainsettrainset, max_steps20 # 最多进行20轮优化 ) # 部署将 compiled_reporter 保存为一个 pickle 文件或封装成 FastAPI 接口 import pickle with open(compiled_reporter.pkl, wb) as f: pickle.dump(compiled_reporter, f)Step 6: 生产环境调用在生产环境中调用变得极其简单# 加载编译好的模型 with open(compiled_reporter.pkl, rb) as f: reporter pickle.load(f) # 获取本周数据伪代码 this_week_emails get_emails_from_api(last_7_days) this_week_meetings parse_meeting_notes(last_week) this_week_jira get_jira_issues(statusDoneupdatedAfter-7d) # 一键生成 result reporter(emailsthis_week_emails, meetingsthis_week_meetings, jira_issuesthis_week_jira) print(result.report)整个流程从需求定义到生产部署代码量不到 200 行且每一行都具有明确的工程意义。它不再是一个“AI demo”而是一个可以嵌入到企业现有 CI/CD 流水线中的、可靠的软件组件。5. 常见问题与独家避坑指南来自一线战场的血泪总结在过去的三个月里我和团队用 DSPy 重构了