DSPy 3 + GEPA 做智能体优化

7 分钟读完

T10
深入解析 · 训练智能体模型

GEPA 是"提示词+程序"的优化器,不是权重优化器——在"程序形状是瓶颈"的任务上,它比 RL 高 20%,rollouts 少 35 倍。

GEPA(ICLR 2026 oral,厂商数字尚未被独立复现)宣称:比 MIPROv2 高 13 分、比完整 RL/GRPO 高 20 分,rollouts 少 35 倍。机制是在程序形状与提示片段上做搜索,而不是更新权重。任务瓶颈在"哪个模块调用哪个"时,GEPA 是对的杠杆;瓶颈在"能力"时则不是。本文写 DSPy 3 + GEPA 的真实机制,以及"何时该抬手够它"的决策规则。

STEP 1

DSPy 程序,以及"提示词+程序优化"的形状。

一份 DSPy 3 程序是一小张模块图,每个模块是一次带签名的语言模型调用——签名声明输入与输出。一份检索增强 QA 程序是三个模块:一个查询改写器、一次检索调用、一个读检索上下文并作答的答案生成器。一份数学推理程序可能是"思维链生成 → 检查器 → 修正器"的链。程序的形状——有哪些模块、如何连接、签名声明了什么——是作者做的设计判断;每个模块起初的提示文本是作者写下的任何东西。这就是 DSPy 优化的对象。

这里的优化不是权重训练。模型是冻结的——常常是即便团队想调也调不了的前沿闭源模型——变化的是提示词、其中的 few-shot 示例、以及在 GEPA 的情形里还包括组合本身。优化器在一小份带标签集上端到端跑程序、度量结果、改动程序、迭代。产出是一份在相同底层模型上、分数比作者原写更高的程序。智能体框架概念覆盖了 DSPy 相对 LangGraph 和 CrewAI 的位置;这里要点在于 DSPy 的核心承诺是"组合的优化",而不只是它的编排。

这一领域在 2025 年之前主要由两类优化器把持。Bootstrap-few-shot 从一小份带标签集为每个模块生成 few-shot 示例。MIPROv2 在模块指令与 few-shot 束上做联合搜索、并跨图协调。两者都不动图结构。GEPA 的贡献——ICLR 2026 oral 引来一屋子人的原因——是在图本身上搜索:在编辑提示之外,还增加、删除、重塑模块。

STEP 2

GEPA vs MIPROv2 vs RL/GRPO:对比与告诫。

论文的头条数字就是每篇"读 GEPA"帖都会引的那两个:在所报任务套件上比 MIPROv2 高 13 分、比完整 GRPO 微调高 20 分、rollouts 少 35 倍。这些数字应带两条告诫。其一:它们由作者报告、在作者选择的任务上、截至 2026 年年中的独立复现仍稀薄;负责任的叙述是"在所报基准上,规模大、可信、但尚未被独立确认的优势"。其二:"rollouts 少 35 倍"跨类别做算术:GEPA 的 rollout 是在冻结模型上端到端跑一份程序,而 GRPO 的 rollout 含更新模型的那一步梯度。一比一比会略微美化 GEPA;诚实的读法是——在能拿到提升的地方,GEPA 达到同等提升所需算力远少于 GRPO。

那句"在能拿到提升的地方"是关键。GRPO 改变模型;GEPA 不改。在"底层模型能力是天花板"的任务上——无论提示与模块怎么排布,都不能让冻结的模型解开它解不了的一类问题——GEPA 就平台化,再多搜索也修不好这份平台化。在"模型原则上能解,但一份组合糟糕的程序让它实际做不出"的情形里,GEPA 效果戏剧性地好。任务落在这条线的哪一侧,是可以实证判断的——手工调优提示以测得冻结模型的天花板,看这个天花板是否在目标之上——应在为 GEPA 订算力之前测。

与 MIPROv2 的比较在同一轴上、但尺度更小。两者都冻结模型;差别是 MIPROv2 在固定图内优化提示,而 GEPA 同时优化提示与图。在作者一开始就把图形状画对的地方,两者收敛到相近性能;在图形状画错的地方,GEPA 能重塑,MIPROv2 不能。建议用 GEPA 而不是 MIPROv2 的信号不是基准羡慕——而是你已经试过 MIPROv2、并看到它在模型天花板之下平台化。

STEP 3

形状优化何时取胜。

GEPA 的原型任务是"模块边界承重、但作者一开始没画对位置"的多模块管线。若一份检索增强 QA 程序把查询改写逻辑放进了检索器的提示里而不是作为独立模块,GEPA 常常能把改写移到专用模块,从而受益;同一份程序如果只有一个巨大的"读上下文并作答"模块,GEPA 有时能拆出一个"用上下文检查答案"模块,从而受益。这些是"作者迭代得够久就会注意到"的形状决定;GEPA 在带 oracle 的回路里更快注意到。

第二种原型是有明确中间信号的管线。GEPA 的搜索受益于"能按各模块中间输出的质量对它们进行奖励或惩罚",而不仅按终端任务分数。当一个模块的职责是产出一份下游模块要消费的结构化产物——一个查询、一份计划、一个 schema——产物质量能独立可校验,GEPA 就能把搜索引向"中间质量更好"的方向。中间输出不透明(一个隐状态、一份无结构的摘要)的管线,GEPA 更难改进,因为搜索没有足够反馈可用。

# Minimal DSPy 3 program: a two-module RAG QA. GEPA optimizes both.
import dspy

class QueryRewrite(dspy.Signature):
    """Rewrite the user question to maximize retrieval recall."""
    question: str = dspy.InputField()
    query:    str = dspy.OutputField()

class GroundedAnswer(dspy.Signature):
    """Answer using only the provided context; cite the passage id."""
    question: str       = dspy.InputField()
    context:  list[str] = dspy.InputField()
    answer:   str       = dspy.OutputField()
    cite:     str       = dspy.OutputField()

class RAG(dspy.Module):
    def __init__(self, retriever):
        self.rewrite = dspy.Predict(QueryRewrite)
        self.answer  = dspy.Predict(GroundedAnswer)
        self.retrieve = retriever

    def forward(self, question):
        q   = self.rewrite(question=question).query
        ctx = self.retrieve(q, k=8)
        return self.answer(question=question, context=ctx)

# GEPA searches over prompt fragments AND graph composition.
optimizer = dspy.GEPA(metric=exact_match, budget=400)
optimized = optimizer.compile(RAG(retriever), trainset=labeled_qa)

最后两行是有趣行为的所在。GEPA 的 compile 调用在训练集上以小预算跑若干次程序,用一份"搜索加反思"的过程提出改动(新的提示片段、替代的模块组合、增加或删除模块),返回它找到的最好一份程序。budget 参数控制搜索多少:两模块程序 400 次执行是像样的起点,更大的程序可能需要几千次。

STEP 4

它帮不上的地方。

能力受限的任务是 GEPA 修不了的第一种情形。若冻结模型不具备解一类问题所需的底层推理能力,任何提示与模块的重排都产不出答案。GEPA 的搜索会很快平台化,诚实的回应是换模型,而不是换程序。这就是RLVR 与 GRPO 一文的配方成为对的杠杆的边界:RL 能教会模型它原来不会的技能;GEPA 只能把它已经会的技能显影出来。

单模块程序是第二种。若程序就是一次 LM 调用,就没有图可优化——GEPA 在做 MIPROv2 一样的事,图搜索这套额外机构是空转的。GEPA 相对 MIPROv2 的盈亏平衡复杂度约为三个模块加非平凡的连接性;再往下,更简单的优化器同样能打。

中间信号不透明的程序是第三种。GEPA 的搜索靠"能奖励中间质量"运转,而中间步骤产出无结构或难核验产物的管线会把搜索"饿死"。绕过办法——在签名里加上结构化中间输出,让其质量可校验——常常正是"你本就该做的重构",这是对 GEPA 强加的守纪的一句轻度背书。

GEPA search log (budget=400, initial score=0.42 on val, target=0.75)

iter  step                                                      val_score  delta
----+---------------------------------------------------------+----------+------
  012  edit rewrite prompt: add "expand acronyms" instruction    0.48     +0.06
  031  edit answer prompt: force citation before answer          0.51     +0.03
  058  ADD module: passage_selector between retrieve and answer  0.61     +0.10
  094  edit passage_selector prompt: rank by claim overlap       0.66     +0.05
  147  REMOVE module: passage_selector (regressed on subset)     0.63     -0.03
  183  ADD module: check_answer after answer (verifies cite)     0.72     +0.09
  247  edit check_answer prompt: reject if cite missing          0.76     +0.04
  312  RESHAPE: check_answer feeds back to answer on reject      0.78     +0.02
  400  budget exhausted — best: iter 312 program                 0.78     final
STEP 5

如何在实操中跑 DSPy 3 + GEPA。

生产团队真的上线的工作流短且可复用。先写一份能在几例上正确跑的手写 DSPy 3 程序。在一小份带标签评测集上度量它——100 到 300 项足以看到有意义的位移。先跑 MIPROv2,因为它更快、且如果图形状已经对,MIPROv2 就能带你走大半路。若 MIPROv2 的改进在目标之下平台化,切到 GEPA、给一个中等预算(200 到 500 次执行)、让它搜形状变化。若 GEPA 也平台化,要么模型是瓶颈(换模型),要么中间信号不透明(重构以把它们暴露)。

团队用血泪学到的失败模式是"对训练集过拟合"。GEPA 的搜索会找到一份在训练样本上分数高的程序,若训练集偏小,这份程序常常会利用不泛化的、特异的模式。缓解方法与 ML 通例一致:留一份 GEPA 从不看到的验证集,把"验证集提升"而不是"训练集提升"当作信号。不留 held-out 就上线的团队会上线回归、然后甩锅 GEPA;留了 held-out 的团队才拿到所报的收益。

值得带走的思路:DSPy 3 + GEPA 是一根有特定支点的杠杆——它能把一个固定模型推得比手写提示更远,就在"模型有能力、但程序形状把它拦住"的那一类任务上。条件成立的地方,它是 2026 年工具箱里最便宜的优化器。条件不成立的地方,收益住在 RL 那一栈里。知道你在哪一侧,比任何单个优化器都值钱。