DSPy 3.4.0于2026年9月25日发布:LM层改用内置lm15引擎,3.3的实验类型被替换,messages=调用进入弃用期,3.5是移除截止。 前置部署 Forward Deployed Engineer(FDE)在本条里对应的是 Agent 与企业 RAG enterprise RAG 交付段:客户现场跑起来的多智能体编排与检索流水线,相当一部分建立在 DSPy 这类把提示、检索与决策封装成可编译程序的上层框架之上。前置部署 Forward Deployed Engineer(FDE)在客户环境里能拿到的升级窗口只有一个,判断依据也只有一条:这次改动会不会把已经交付的代码炸掉,以及能不能顺手换来更好的 LLM 评估 LLM evaluation 能力。 ## 一、3.4 是 LM 过渡版本,3.5 是移除截止 DSPy 3.4.0 的 wheel 与源码包于 2026-09-25T04:04:23Z 上传到 PyPI,GitHub 发布页记录的时间是 9 月 25 日 04:06,标签提交为 2413b67,该标签之后主线只多了 4 次提交。 原文明确定义了这次发布的性质:3.4 是 LM 过渡版本(LM transition release),3.5 是迁移截止(migration deadline)。3.3 引入的实验性 LM 类型在 3.4 已经被替换,而且是替换不是别名。 安装命令两条。基础安装是 pip install --upgrade "dspy==3.4.0";要用 Jev 决策后端则需要额外的 extras,执行 pip install --upgrade "dspy[typesafe]==3.4.0"。 依赖变化有两条硬线:Pydantic 最低版本提到 2.11.0(对应 PR #10271「Require Pydantic 2.11」);typesafe 这个 extra 要求 typesafe-sdk 大于等于 0.6.0 且小于 1.0.0,并且不会随基础安装装进来。PyPI 元数据写的 Python 支持范围是大于等于 3.10 且小于 3.15。 发布页的完整 PR 清单共 70 条、23 位贡献者,其中 11 条来自 dependabot 与 github-actions 自动化。分节数量是:Jev 与校准 2 条、LM 与引擎 9 条、Agent 与工具与解释器 11 条、优化与评估与检索 8 条、适配器与流式与缓存 8 条、文档 15 条、依赖与发布工程 17 条。 上一个版本 3.3.0 上传于 2026-08-03,3.3.1 上传于 2026-08-21,也就是说 3.4.0 是 3.3.0 之后第一个功能版本,与 3.3.1 相隔约 35 天。前置部署 Forward Deployed Engineer(FDE)通常把这类升级安排在两个交付项目的间隙,因此这个版本跨度本身就是排期输入。 ## 二、LM 层换成内置 lm15,3.3 的实验类型被整体替换 从 3.4 起,DSPy 的 LM 层改用随包内置的 lm15 请求、响应与流式类型,位置是 dspy.lm15,不需要单独安装。 原文给出的替换对照表有五组:dspy.LMRequest 与 dspy.LMResponse 换成 dspy.lm15.Request 与 dspy.lm15.Response;dspy.LMMessage 与 dspy.LMConfig 换成 dspy.lm15.Message 与 dspy.lm15.Config;dspy.System(text) 换成 Request(system=text);dspy.User(text) 与 dspy.Assistant(text) 换成 Message.user(text) 与 Message.assistant(text);response.outputs[0].parts 换成 response.message.parts。 迁移失败是硬失败而不是告警:导入 dspy.core.types 会直接抛迁移错误,forward_contract="typed_lm" 会被拒绝。新类型是 frozen dataclass 而不是 Pydantic 模型,校验规则与数据形状都变了;包含被移除实验类的旧 pickle 不会自动迁移,必须先在原环境里加载并导出。 更容易踩的是语义变化:experimental=True 不再让普通 prompt 调用返回类型化响应,必须显式构造 Request。原文给出的前后对照是,之前写 outputs = lm(messages=[{"role": "user", "content": "What is DSPy?"}]) 会拿到一个列表;之后要写成 response = lm(Request(model=lm.model, system="Be concise.", messages=(Message.user("What is DSPy?"),), config=Config(max_tokens=200))),再读 response.text。 两个细节必须记住:显式 Request 里的 model 必须与 LM 自身一致;生成参数要放进 Config,因为 LM 上的生成默认值不会被补进请求。Config.cache 控制的是供应商侧的 prompt 缓存,不是 DSPy 自己的响应缓存。 这一段对应的 PR 是 #10359(把 lm15 作为包内子树内置)与 #10366(LM 执行改走 lm15 引擎并退役 3.3 的实验类型)。 ## 三、引擎选择,以及给自建网关注册供应商 engine="auto" 是默认值:支持原生执行且输入可表示的路径优先走原生,合格的兼容回退会选 LiteLLM。LiteLLM 仍然随包安装并继续受支持,不是被移除的对象。 两条强制开关:engine="lm15" 要求必须原生执行,engine="litellm" 显式选择兼容后端。 关键的安全语义是:认证失败、超时与供应商错误永远不会触发后端切换。这一条对客户现场排查很重要,报错就是报错,框架不会静默换一条链路把问题掩盖掉;相应的代价是,某个供应商临时不可用时也不会自动兜底,兜底逻辑要自己写在调用方。 自建网关场景有专门入口:dspy.lm15.register_provider(...) 可以声明一个 OpenAI 或 Anthropic 兼容 HTTP 服务的端点、鉴权与能力,不必自己写引擎。需要注意每个 LM 只绑定它构造时已经存在的注册项,注册顺序会影响结果。原文同时说明,受支持的回退会保留声明的端点与凭据,不支持的兜底鉴权或显式的兼容拒绝会直接抛错,而不是静默改连接或丢设置。 其他后端仍可实现 complete(Request) -> Response 并提供 engine=,异步再给一个 async_engine=。DSPy 负责缓存、托管重试、回调、历史与用量记账,自定义引擎由调用方自己持有。相关 PR 是 #10440(按 LM 绑定声明式供应商)与 #10442。前置部署 Forward Deployed Engineer(FDE)在客户现场排查超时问题时,可以用 engine="lm15" 把「后端被静默换掉」这一类可能性直接排除。 ## 四、Jev 与 TypeSafe:带概率证据的三种决策类型 3.4 最大的新能力是把 Jev 通过 TypeSafe 客户端接进 DSPy,用法与其他后端一致:先 pip install dspy[typesafe],再设置 TYPESAFE_API_KEY 环境变量,然后配置 dspy.experimental.TypeSafe("jev-latest")。Predict 会自动把签名、输入、示例与字段标准翻译成 Jev 的决策请求,不需要单独的预测模块,也不需要选适配器。 三种实验类型各自带回证据。Noul 用于布尔决策,返回 .value、.probability 与 .confidence。Choice[...] 用于在若干被描述的备选里选一个,返回类型化的 .value、每个选项的 .probabilities 与 .confidence。Score[...] 用于按有序量表打分,返回连续的 .value、序数的 .level、量表各档的 .probabilities 与 .confidence。 原文示例的签名结构可以直接照搬:ticket 是 InputField 承载工单文本;urgent 是 Noul 输出,判断服务是否被阻塞;category 是 Choice[("billing", "Payment issue"), ("technical", "Product malfunction")];severity 是 Score["Minor", "Disruptive", "Blocking"]。配置后用 dspy.Predict(AssessTicket) 调用,再读 result.urgent.probability、result.category.probabilities 与 result.severity.level。 这些富类型也能配生成式 LM 使用:把兼容的 dspy.LM 绑到同一个 predictor 上,证据会通过配置好的 Chat 或 JSON 适配器取回。原文的两条硬性提醒是,只需要选中值时 Jev 也支持原生 bool 与 Literal[...] 输出;打分要用 Score[...] 而不是裸 float,否则拿不到 .level。 这一段对应 PR #10463,标题是「Add experimental decision types and System One clients to Predict」。 ## 五、阈值留在本地,同一份证据可以复用 决策规则属于 predictor,不属于后端。把 assess.fields["urgent"] 设成 {"threshold": 0.7},就能改变这个概率变成布尔值的方式。 原文强调的实现细节是:阈值、Score 切点与 Choice 权重都在本地应用,不会发给后端,因此相同请求可以复用缓存的证据。对按量计费的客户现场,这条直接决定评估成本;反过来,如果改成每次调阈值都重新请求后端,评估成本会随调参次数线性上涨。 还有一个容易混淆的点:Score 切点改的是 .level,不是连续的 .value。字段说明与字段标准也可以覆盖,并随程序一起保存,因此校准结果和提示词可以一起进版本库。前置部署 Forward Deployed Engineer(FDE)面对按调用量结算的评估场景时,阈值留在本地这一条直接决定成本。 ## 六、ReAnchor:按整个程序的指标校准阈值、切点与权重 dspy.experimental.ReAnchor 是一个实验性优化器,它只拟合 Predict 程序里的布尔阈值、Score 切点与 Choice 权重,不改写指令、标准或示例。用法是四步:optimizer = ReAnchor(metric=metric);tuned = optimizer.compile(program, trainset=trainset, valset=valset);print(optimizer.report);tuned.save("tuned.json")。 它按整个程序的指标评估候选设置,而不是只看单点输出。两道门槛必须同时过:一组设置要提升训练分数,还要通过一份 held-out fold 检查。可选的 valset 只用于给报告打分,绝不参与拟合。compile() 返回的是副本,原程序保持不变,拟合出的设置用普通的程序 save/load 存取。 在生成式 LM 上,ReAnchor 可以为零散 bool 与 Literal 输出请求概率证据,同时保留它们的 Python 返回类型;只有在同一次 fold 检查下校准结果优于原来的原生输出行为时,这个改动才会被保留。 三个已知约束要提前接受:缓存默认是必需的,它避免重复相同请求,但上游决策变化与原生产出提升仍可能产生新的后端调用,而且不保证固定的推理预算;RLM 决策输出不被支持;单选项 Choice 的校准目前会直接报错。这一段对应 PR #10475。 ## 七、RLM 的调用时重载改成关键字工厂 这是一处破坏性变更:在 rlm(...) 与 rlm.acall(...) 上重载运行时,必须用关键字参数 interpreter_factory= 传入一个零参数工厂。传活的解释器实例或位置参数工厂会被 TypeError 拒绝,调用方跨多次 RLM 调用复用同一个 session 也不再受支持。 前后对照是:之前写 with dspy.PythonInterpreter() as interpreter: result = rlm(interpreter, query=query);之后写 result = rlm(query=query, interpreter_factory=dspy.PythonInterpreter)。异步版本是 await rlm.acall(query=query, interpreter_factory=dspy.PythonInterpreter)。自定义运行时传类,或传一个返回全新解释器的零参数可调用对象,不要传返回共享实例的 lambda。 生命周期规则是:RLM 每次调用创建一个解释器,在该次调用的多轮迭代里保留它,成功、失败或异步取消时关闭。所选工厂还负责提供这次调用的运行时提示词指引,并且不会改写共享的 predictor 指令。 优先级规则是:调用时的工厂高于构造器与上下文或全局设置,包括显式传入的 dspy.PythonInterpreter;构造器的 interpreter_factory= 与默认 PythonInterpreter 没有变化。 最容易漏的一条是命名冲突:签名里叫 interpreter_factory 的输入字段必须改名,这个名字已被保留为运行时配置,把它声明为签名输入会在构造 RLM 时抛 ValueError。该变更只影响 RLM,CodeAct 与 ProgramOfThought 保持原有的调用时 API。对应 PR #10493。前置部署 Forward Deployed Engineer(FDE)升级前建议在仓库里全局搜索 interpreter_factory 这个字符串,把签名输入撞名的风险一次性清掉。 ## 八、LocalInterpreter 是持久 CPython,但不是安全沙箱 dspy.LocalInterpreter 用当前的 Python 可执行文件起一个持久子进程来跑生成的 Python 代码,不需要 Deno,可与 RLM 和 Flex 配合。 用法是把工厂传给 RLM:rlm = dspy.RLM("question: str -> answer: int", interpreter_factory=dspy.LocalInterpreter)。状态与导入在同一个解释器会话内保留;常规的工厂生命周期仍会为每次模块调用创建新解释器。 它支持同步与异步的宿主工具、类型化的 SUBMIT、解释器回调与执行超时。宿主工具的取消与中断会传播并关停被搁置的 worker。执行超时会终止 worker,但无法强制停止已经在运行中的宿主可调用对象。 原文写明的边界必须原样接受:LocalInterpreter 不是安全沙箱,生成的代码保留宿主用户的文件系统、环境变量、凭据、子进程与网络访问。处理不受信任的代码应继续使用默认的 PythonInterpreter 或合适的隔离远程解释器,而默认值在这次发布里没有改变。 默认解释器工厂也可以用 dspy.configure(interpreter_factory=...) 替换;显式传入的非默认工厂优先级更高。相关 PR 是 #10238、#10379、#10383。 ## 九、成本与缓存与流式的四个坑 第一个是计费:原生 n 参数会发出多次顺序请求,可能对输入 token 重复计费;需要该后端原生的 n 行为时要显式用 engine="litellm"。 第二个是连接归属:带自定义引擎时连接归引擎自己所有,api_key、api_base、timeout 等客户端设置必须放在引擎上,DSPy 会在 LM 的构造、复制与调用三个阶段拒绝这些参数;copy(engine=...) 会同时替换同步与异步引擎对。自定义引擎要能保存与恢复,类必须可导入,dump_state() 与 load_state() 必须产出 JSON 兼容数据,恢复时还要 allow_unsafe_lm_state=True,这个开关只应对受信任状态开启。 第三个是流式:一旦发出 chunk 就不再重试;未完成的响应不会作为成功结果写入缓存;提前关闭流可能丢失最终 usage,也不保证供应商停止生成或停止计费;类型化的 LiteLLM 流式只支持 Chat Completions,不支持 Responses。原生超时约束的是单次传输等待而不是总生成时间,httpx.Timeout 的某个分量为 None 时,auto 模式下会选 LiteLLM,强制 lm15 模式下直接抛错,不会静默换成有限超时。 第四个是记账与报错:未知成本保持未知,不会被记成 0,这一点会直接影响按次结算的对账;限流错误在可用时会保留重试提示、请求 ID 与限流响应头。前置部署 Forward Deployed Engineer(FDE)做对账时要注意「未知成本不记为 0」这一条,否则按次结算的差额会被掩盖。 ## 十、其余兼容性细节 ReActV2 的输出字段不能命名为 history 或 termination_reason(PR #9852)。在事件循环内同步调用异步工具会抛 ValueError,即使开了 async 与 sync 转换也要写成 await tool.acall(...)(PR #10146)。 同步流式现在会传播生产者失败而不是静默结束;streamify 会解包单个嵌套任务组的失败,多个失败仍然以任务组形式保留(PR #9864、#10451)。Evaluate 对空的开发集给出描述性的 ValueError(PR #9978)。由 Pydantic 模型派生的原生 strict schema 现在按 OpenAI 的期望要求所有属性出现,调用方自带的原始 schema 不受影响。 缓存实现只在对应层级启用时才加载,两层都关掉时不会构造基于文件系统的缓存存储(PR #10382);精确向量检索改为跨查询共享语料表以降低内存占用(PR #10398),FAISS 路径未变。 GEPA 新增 code_proposer= 用于自定义 Flex 源码提案,与 instruction_proposer= 并列;它的 trace-capture 评估现在会让失败样例与各自的分数、输出保持对齐,修的是 3.3.1 之后暴露的错位问题(PR #10212、#10305)。异步 ReActV2 支持 await agent.acall(...),包含异步预测、异步工具与最终提交;工具是顺序执行的,这次发布没有加入并行执行,也不会自动把阻塞的同步工具挪走(PR #10355、#10356)。 文档侧另有一笔:Material for MkDocs 进入维护模式并宣布 2026-11-05 终止支持,DSPy 把 Current 文档迁到其继任者 Zensical,同时上线版本化文档。新标签发布会在包发布成功后生成匹配的文档快照,快照用该版本的 wheel 做 API introspection;稳定版会推进它的次版本别名,预发布版保留自己的版本路径而不占用别名(PR #10315、#10316、#10317)。 ## 十一、谁该行动、怎么做、适用条件与回滚 该行动的是两类人:把 DSPy 用在客户现场 Agent 与企业 RAG 流水线上的前置部署 Forward Deployed Engineer(FDE),以及任何已经把 DSPy 写进交付代码库、需要提前排维护窗口的团队。落地分五步,每一步都能直接执行。 第一步先量影响面:在交付仓库里执行 python -W default::DeprecationWarning your_program.py,把弃用告警一次性全部列出来。原文给出的弃用清单是四项,公开的 OpenAI 风格 lm(messages=[...]) 调用(包括 SDK 消息对象)、自定义的 BaseLM.forward() 与 aforward() 集成、LegacyEngine 与 AsyncLegacyEngine、以及自定义 complete_legacy() 快捷方式。这四项在 3.4 仍会执行,但会发 DeprecationWarning,并计划在 3.5 移除。 第二步改调用形态:把 messages= 改成显式 Request,把生成参数从 lm(...) 搬到 Config,把 response.outputs[0].parts 改成 response.message.parts,把 dspy.System 与 dspy.User 与 dspy.Assistant 换成 Request(system=...) 与 Message.user() 与 Message.assistant()。 第三步换自定义 LM 的实现面:把自定义 BaseLM.forward() 换成实现引擎接口,最小实现可以照抄一个只实现 complete(request) 的类,返回 Response(id=None, model=request.model, message=Message.assistant("hello"), finish_reason="stop", usage=Usage()),然后用 dspy.LM("custom/echo", engine=EchoEngine()) 挂上。 第四步改 RLM:把 rlm(interpreter, ...) 改成 rlm(..., interpreter_factory=dspy.PythonInterpreter),并全局搜一遍签名里是否有人用了 interpreter_factory 这个输入名,避免构造时抛 ValueError。 第五步再决定要不要吃新能力:只有需要审计留痕、需要概率证据、或需要按指标自动校准阈值的团队,才值得在 3.4 就启用 Jev 与 ReAnchor,因为这两块都是实验 API,接口在 3.5 之前仍可能变。 适用条件是三条:Python 需在 3.10 及以上且低于 3.15,Pydantic 至少 2.11.0,用 Jev 还要额外装 0.6.0 到 0.99 之间的 typesafe-sdk。前置部署 Forward Deployed Engineer(FDE)在多客户复用的基础镜像里要一次把这三个前提固化进去,避免到了现场才发现版本不对。 回滚方式要说清楚:最稳的是把 dspy 固定回 3.3.1 并同时回滚代码,因为 3.3 的实验类型在 3.4 已被移除,只回滚包版本会直接导入失败;已经迁到 lm15 的代码无法靠降级回退。建议先在分支上完成迁移并跑通既有评估集再合并。不想现在动的团队可以把迁移安排在 3.5 发布前的一个迭代,但要把 DeprecationWarning 设成持续集成里的失败条件,否则会静默拖到截止;注意 lm("hello") 这种写法在 3.5 仍然受支持且仍然返回列表,不必改。 ## 十二、原文自述的局限与本条未覆盖的场景 局限一,Jev 相关的 API 全部标注为实验性;Jev 是决策后端而不是通用文本生成器,所有输出字段都必须被支持,而且没有自动的生成式回退。 局限二,决策流式、RLM 决策输出、以及用 TypeSafe 做生成式优化器或微调都不被支持;嵌套的决策输出不会得到同样的证据解码,必须用顶层决策输出。 局限三,Noul 的 confidence 度量的是它与所配置阈值之间的距离,不是统计意义上的校准;生成式 LM 的 confidence 是模型自报的。 局限四,ReAnchor 不保证固定的推理预算:缓存默认必需,但上游决策变化与原生输出提升仍可能产生新的后端调用;RLM 决策输出不支持;单选项 Choice 的校准目前会报错。 局限五,LocalInterpreter 不是安全沙箱,生成的代码保留宿主的文件系统、环境变量、凭据、子进程与网络访问,默认解释器仍是 PythonInterpreter。 局限六,适配器迁移只走到 3.4 这一步:内置适配器仍使用内部的字典与列表边界,它们规范化的请求与响应迁移排在 3.5;自定义适配器不能依赖已经被移除的 dspy.clients.openai_format 模块。 局限七,全部内容来自 DSPy 官方发布说明与 PyPI 元数据,没有第三方独立复现,也没有客户侧的性能或成本对比。原文通篇没有给出延迟、准确率或成本的前后对比数字,只有功能与语义层面的对照,这是本条最实的一处缺口。 本条未覆盖的场景包括:如何把 Jev 的概率证据接入既有的 LLM 评估 LLM evaluation 平台、ReAnchor 需要多大的训练集才稳定、以及 3.5 的确切发布日期——原文只说明 3.5 是移除截止,没有给日期。 ## 十三、与本站既有条目的差异说明 检索本站已发布列表标题,含 DSPy 的条目为 0 条,含 Jev、TypeSafe、ReAnchor、lm15、LocalInterpreter 的条目也全部为 0 条。 相邻类型上,含 LangGraph 的条目落在自托管部署脚本与 CLI 参数上,含 ADK 的两条分别落在技能生命周期与 2.10.0 的版本改动上,含 Langfuse 的条目落在 AI 网关元数据键名迁移上,含 vLLM 的条目落在 Model Runner V2 与 Fast Start 的初始化耗时上。 本条与它们的技术面都不同:本条讲的是 DSPy 的 LM 抽象层换代、把 messages= 换成显式 Request、把决策阈值留在本地、以及按整个程序的指标校准阈值,不是部署参数、不是元数据改名、也不是推理引擎的耗时优化。前置部署 Forward Deployed Engineer(FDE)在本条里换到的是 LLM 程序自身的可迁移性与可校准性。 ## 原始来源 原始来源: https://github.com/stanfordnlp/dspy/releases/tag/3.4.0 https://pypi.org/project/dspy/3.4.0/ https://github.com/stanfordnlp/dspy/pull/10463 https://github.com/stanfordnlp/dspy/pull/10475 https://github.com/stanfordnlp/dspy/pull/10493