GEPA架构全解析:Prompt与Skill优化最佳实践
为Agent系统编写Prompt,极少有人能一次性到位。更常见的场景是:线上出现故障,工程师翻阅日志、检查工具调用、分析模型输出,然后手动调整Prompt或Skill版本,跑一轮评测,分数提升就暂时存档。
这套流程虽然能跑通,但既耗费精力,又缺乏稳定性。最棘手的不是“改不动”,而是优化A导致B指标倒退——比如分类准确率提升了,回复却变得僵硬;检索召回率上去了,推理链路开始逻辑跳跃。仅凭一个总分做决策,很容易牺牲掉那些长尾能力。
GEPA 正是针对这类困境提出的方案。它并非创造一套更复杂的调参工程,而是将“观察轨迹、定位错误、修改Prompt、保留互补版本”这一套人工经验,嵌入到一条可自动执行的进化流水线里。
本文从工程实践视角解析GEPA:它真正优化的是什么,为何比单纯依赖分数的Prompt搜索更稳定,以及在业务系统落地时,哪些环节最容易遭到低估。
图示:轨迹反馈驱动Prompt候选版本持续迭代进化
Prompt优化的核心瓶颈:反馈信号过于稀疏
众多Prompt优化方案最终都会卡在同一点:评测器只输出一个分数。
从61分涨到74分,看似进步明显。但这13分从何而来?哪些样本表现变好,哪些样本反而退步?如果某个Prompt版本更擅长捕捉并发bug,却将大量低风险diff误判为高危告警,是否该被保留?
人类在手动调优Prompt时,绝不仅仅依赖分数——我们会分析错例、审查执行轨迹、从失败案例中提炼规则。GEPA的起点正是基于这个洞察:LLM既然能阅读代码、解析日志、理解用户反馈,它同样可以分析自身的运行轨迹,并将错误原因直接写回Prompt。
| 优化方式 | 反馈形式 | 学习成果 | 常见问题 |
|---|---|---|---|
| 人工手动调优 | 人类分析案例后总结 | 经验性规则 | 成本高昂,难以复制推广 |
| 标量搜索 | 单一分数指标 | 哪个版本分数更高 | 无法解释为何表现更优 |
| 强化学习 | 奖励/ rollout数据 | 参数或策略偏好 | 调试复杂度高,样本消耗巨大 |
| GEPA | 轨迹记录 + 自然语言反馈 | 可阅读、可审计的Prompt规则 | 依赖评测器的输出质量 |
这也是GEPA与众多Prompt优化器的根本分野——它并不要求模型凭空“构思一个更好的提示词”,而是强制模型先完整读取执行轨迹,再依据具体的失败原因进行针对性改写。
Reflective Mutation:将单次失败转化为可复用规则
GEPA的变异算子称为Reflective Prompt Mutation。听起来像是遗传算法术语,落到工程实现中,其实只有四个步骤:
- 从训练集中抽取一个minibatch,用当前的候选Prompt跑完完整链路。
- 保存完整的执行轨迹,包括模块输入/输出、推理过程、工具调用、工具返回结果及最终答案。
- 评测器除了输出分数,还给出文字反馈。例如“定位到了风险点,但缺少触发路径说明”或“评论给出了结论,却没有提供最小修复建议”。
- 反思模型读取“旧Prompt + 轨迹数据 + 反馈信息”,仅针对当前选中的模块Prompt进行修改。
图示:单次失败如何被反思模型改写为新的候选Prompt
这里有一个实用的工程细节:反思模型通常可以比目标模型更强大。它不会直接接管线上任务,而是将诊断能力“编译”成目标模型可执行的Prompt指令。这种方式比模型微调成本低得多,产物也更容易审计。
如果目标系统包含多个模块,GEPA不会每次都对整套Prompt进行全局修改——它会选择单个模块执行局部变异,比如只修改分类Prompt,保留回复Prompt不变。这个约束至关重要:一次只变动一个变量,后续才能准确判断收益和退化来自何处。
Pareto前沿:别急着放弃“偏科生”
对GEPA最常见的误解,是认为它总是保留总分最高的那个版本。
在复合任务场景中,总分最高的候选版本未必在所有样本上都占优——它可能在大部分样本上获胜,却在少数关键样本上输得很惨。传统的贪心策略会直接删除旧版本,导致后续失去了从旧分支继续演化的机会。
图示:Pareto前沿保留具有互补能力的候选版本,而非只追逐最高平均分
GEPA采用Pareto前沿来维护候选池。判断标准非常直观:只要某个候选版本在至少一条验证样本上表现最佳,它就有理由被保留。
图示:基于验证样本的优势来维护候选池,并继续采样演化
这带来一个非常实际的好处:优化器不会被平均分所迷惑。一个候选版本即使只擅长5%的长尾样本,它也可能成为后续合并出强大版本的素材。
| 候选版本保留策略 | 可能的结果 | 风险点 |
|---|---|---|
| 仅保留总分最高 | 主干清晰简洁 | 容易丢失长尾能力 |
| 保留所有历史版本 | 信息保存完整 | 候选池膨胀,搜索效率降低 |
| Pareto前沿 | 保留具有互补优势的版本 | 需要稳定的验证集支持 |
可以把Pareto前沿理解为一个“能力档案柜”——它并非收集所有旧版本,而是只保留那些仍具备独特价值的版本。
System-Aware Merge:将分支上的优质成果组合回来
在生成多个分支之后,GEPA还能做一件链式优化难以完成的事情:合并。
假设一个Agent包含两个模块:
| 模块 | 职责描述 |
|---|---|
| L | 在代码diff中定位风险点,例如并发问题、空指针、权限绕过、资源泄露 |
| C | 将识别出的风险点撰写为评审评论,要求包含触发条件、影响范围和修复建议 |
第一轮反思可能让L模块更擅长捕获并发问题,但C模块产出的评论变得像报警器一样生硬,损害了可读性。第二轮从旧版本出发,可能优化了C模块的语气和证据链,但L模块仍然遗漏了边界条件。GEPA会在候选版本谱系中识别这种“同源、互补、互不支配”的关系,然后执行模块级别的合并。
图示:同源候选版本在模块边界清晰的情况下可以进行横向合并
这并非简单的复制粘贴操作。合并过程需要满足几个关键约束:
| 约束条件 | 含义说明 |
|---|---|
| 共同祖先 | 仅合并同一任务谱系下的候选版本,避免将无关修改强行拼接 |
| 模块边界清晰 | 候选版本必须能拆解为模块级别的Prompt或规则 |
| 验证无退化 | 合并后必须重新在验证集上评测,不能仅看局部收益 |
这一步解释了为什么GEPA更适合多模块系统。单个Prompt当然也能优化,但在Agent、RAG、工具链、工作流这类系统中,模块之间经常此消彼长,合并操作的收益会更为显著。
图示:多模块Agent的分支经验在系统边界内合并
代码评审Agent示例:高分版本也可能存在偏科
换一个更贴近工程团队的实际例子。假设我们在构建一个代码评审Agent,它首先从diff中识别风险点,然后生成行级评论。样本来自历史MR,其中包含人工标注的真实缺陷、误报案例以及可被接受的评论。为防止调优过程“记住”测试集,数据被切分为三份:
| 数据集 | 数量 | 用途 |
|---|---|---|
| 轨迹集 | 90 | 每轮抽取12个MR,让反思模型观察具体的失败过程 |
| 裁判集 | 45 | 所有候选版本在此完整评测,用于决定谁能留在Pareto前沿 |
| 封存集 | 30 | 优化期间完全不用,最终用于验证真实泛化能力 |
初始版本S0仅能勉强跑通链路:
复制代码{
"L": "阅读diff,找出可能存在的bug",
"C": "将发现的问题写成code review评论"
}
S0在裁判集上的综合得分为61%。第一轮抽取的12个MR中,漏报主要集中在两类问题:一类是goroutine写共享map未加锁,另一类是鉴权判断被提前return绕过。反思模型本次只修改L模块,补充了“并发写”和“权限短路”两组检查规则,得到S1。
| 候选版本 | 高风险缺陷召回率 | 评论可采纳率 | 综合得分 |
|---|---|---|---|
| S0: L0 + C0 | 61% | 67% | 61% |
| S1: L1 + C0 | 88% | 54% | 74% |
S1总分更高,但它有一个令人头疼的问题:抓bug抓得更猛之后,评论中混入了不少“可能有问题”的泛化提醒,人工评审会感到噪音过大。S0虽然漏掉了一些缺陷,但在低风险MR上表现得更为克制。两者互不支配,因此都暂时保留。
第二轮父代采样时,S0被选中。新的minibatch暴露了评论生成模块的弱点:评论只说“这里可能有空指针”,但既没有解释触发路径,也不提供修复方法。反思模型这次不动L模块,只修改C模块,得到S2。
| 候选版本 | 缺陷召回率 | 评论可采纳率 | 综合得分 |
|---|---|---|---|
| S0: L0 + C0 | 61% | 67% | 61% |
| S1: L1 + C0 | 88% | 54% | 74% |
| S2: L0 + C1 | 70% | 91% | 82% |
S2基本覆盖了S0的优势,S0可以退出。但S1还不能删除,因为它在高风险缺陷召回方面明显更强。此时合并操作的价值就体现出来了:将S1的定位规则和S2的评论写法组合起来。
复制代码{
"L": "定位v1: 显式检查并发写、权限短路、nil分支和资源释放路径",
"C": "评论v1: 先给出触发条件,再说明影响范围,最后提供最小修改建议"
}
| 候选版本 | 高风险缺陷召回率 | 评论可采纳率 | 综合得分 |
|---|---|---|---|
| S1: L1 + C0 | 88% | 54% | 74% |
| S2: L0 + C1 | 70% | 91% | 82% |
| S3: L1 + C1 | 87% | 89% | 88% |
继续运行几轮后,最终版本在封存集上获得了86%的综合得分——初始S0只有59%。更关键的是,人工复核中“评论可直接粘贴到MR”的比例从41%提升到了76%。
这个案例中最值得关注的并非86%这个数字,而是S1在那一刻没有被平均分策略淘汰掉——虽然它当时的评论质量很差,但缺陷召回这条能力后来被S3继承并利用。如果只保留S2,系统会变得很擅长写评论,却会继续漏过真正危险的diff。
GEPA主循环:搜索的是可解释的程序,而非玄学Prompt
将上述步骤串联起来,GEPA的主循环大致如下:
图示:从候选版本采样、轨迹反馈到前沿更新和最终验证的全流程
停止条件通常包含三类:
| 停止条件 | 说明 |
|---|---|
| 预算耗尽 | 目标模型调用次数达到 max_metric_calls |
| 前沿饱和 | 连续K轮没有新候选版本进入Pareto前沿,K由 patience 参数控制 |
| 满分达成 | 验证集指标达到预设的目标上限 |
这套循环看起来像搜索过程,但实际产出的更接近一组可解释的文本程序——每次修改都附带轨迹数据、反馈信息和验证分数,并且可以追溯是哪个模块发生了变化。
与GRPO、MIPROv2对比:GEPA的优势在哪里
在HotpotQA、IFBench、HoVer、PUPA等多个任务上进行了实验。关键信息可以概括为下表:
| 对比维度 | GEPA | GRPO / RL | MIPROv2 |
|---|---|---|---|
| 平均效果 | 相比基线提升约 +10 分 | 作为主要对照基线 | 通常低于GEPA 10%以上 |
| 样本消耗 | 通过文字反馈减少无效rollout | rollout最多可达GEPA的35倍 | 与传统Prompt搜索接近 |
| AIME-2025 | 相比MIPROv2提升约 +12% | 未作为同表核心对比项 | 基线参照 |
| 结果形态 | 自然语言Prompt,可读且可审计 | 策略或参数更新,运维成本更高 | Prompt搜索结果 |
GEPA的优势不仅仅体现在分数上。工程层面的吸引力在于以下几个方面:
| 维度 | GEPA的处理方式 | 对工程团队的价值 |
|---|---|---|
| 样本效率 | 从单条轨迹中提炼文字规则 | 减少大量无意义的试验运行 |
| 可解释性 | 知识固化在Prompt中 | 便于审计,也支持手工修改 |
| 泛化方式 | 学习的是规则,而非仅仅是分布偏好 | 对长尾样本更加友好 |
| 部署成本 | 不需要进行权重训练 | 主要消耗API调用和评测预算 |
| 系统适配 | 支持多模块、谱系管理和合并操作 | 更适合Agent / RAG / 工具链场景 |
GEPA还可以作为推理时的搜索策略应用于代码优化等任务——也就是说,即使没有显式的训练阶段,只要系统能够执行、能够反馈、能够比较,反思机制和Pareto前沿也能用来搜索更优的候选方案。
从链式迭代到候选树:优化形态的根本性转变
传统的Prompt调优大多是一条线性路径:
图示:传统Prompt调优通常只维护一个“当前版本”
这条线性路径的问题是,任何时间点上只能有一个“当前版本”。一旦V1被V2覆盖,V1的优势除非人工记录,否则就会丢失。
GEPA更像一棵支持横向焊接的候选树:
图示:GEPA将线性迭代扩展为可回访、可合并的候选树结构
这种结构带来了几个链式迭代无法实现的能力:
| 能力 | 具体含义 |
|---|---|
| 回访分岔点 | 旧候选版本只要还保留独占优势,就能继续作为父代使用 |
| 横向合并 | 两个分支分别习得的模块经验可以组合在一起 |
| 并行探索 | 多个候选分支可以独立进行评测和反思 |
这也是GEPA对Agent工程最具启发性的地方——它将“版本”从一个线性编号,转变成了一个带有谱系的候选族群。
落地边界:并非所有任务都适合GEPA
GEPA并非万能方案。它要求任务能够被执行、被观测、被评价。特别是评测器,如果只能输出0/1二值分数,GEPA的效果将大打折扣。
| 适合使用GEPA的场景 | 不太适合使用GEPA的场景 |
|---|---|
| Agent、RAG、工具链等多模块系统 | 仅提供二值奖励、没有文字反馈的任务 |
| 评测器能够解释出错原因 | 验证集规模过小或噪声过大 |
| 需要白盒、可审计的优化结果 | 目标模型的上下文窗口极短,无法容纳反思规则 |
| 没有GPU训练资源,只能调用API | 反思模型的预算非常有限 |
| 模型调用成本高、链路长,对rollout成本敏感 | 需要权重级别的知识注入或风格内化 |
实际在业务中落地时,接入GEPA只是第一步。后续通常还有四个层级的工作需要完成:
| 层级 | 需要做的事 | 代码评审场景示例 |
|---|---|---|
| L1 反馈函数 | 将错误原因写成自然语言,而非仅输出分数 | “指出了并发风险,但没有说明哪个共享对象会被两个goroutine同时写入” |
| L2 参数配置 | 调整 auto、use_merge、num_threads、track_stats 等开关 | 根据验证曲线决定是否启用合并和并行评测 |
| L3 自定义Adapter | 将业务链路中的工具调用、延迟、复审率等数据接入轨迹 | GEPA同时看到diff片段、静态扫描结果、人工采纳状态 |
| L4 Instruction Proposer | 约束反思模型如何生成Prompt | 限制长度,过滤合规风险,多模型投票后再写入 |
逐层推进下去,边际收益会逐渐递减,但工程的可控性会不断增强。尤其是L1层级,最容易被低估——如果评测器写得很粗糙,GEPA就只能在粗糙的反馈上低效循环。
我对GEPA的判断
GEPA将“调Prompt”这件事的重心向上游挪了一大步。过去,工程师的主要时间花在修改提示词上;现在,更应该将精力投入到评测器设计、轨迹可观测性和反馈粒度上。
这也解释了Claude Code作者Boris那句名言:“I don't prompt Claude anymore. I write loops.”——真正重要的不是某句神奇的Prompt,而是让Prompt能够自我改进的循环。这个循环能走多远,完全取决于评判信号的精细程度。
GEPA的适用范围也比Prompt engineering更广。只要一个对象能够文本化、能够执行、能够评分,它就有机会被反思式进化所优化。Prompt可以,工具描述可以,Skill可以,workflow、路由规则、harness中的约束条件同样可以。
因此,我不太倾向于将GEPA视为“自动写Prompt的工具”。它更像一个系统优化的框架:将运行轨迹转化为反馈,将反馈转化为文本规则,将文本规则放回系统,再利用Pareto前沿保留那些暂时看起来偏科、但未来可能合并出更强版本的候选方案。
对于Agent工程而言,这个方向比单纯追求更擅长写Prompt的人才更有想象力。真正稀缺的能力,可能会变成如何设计评测指标、如何暴露轨迹信息、如何让系统的每一次失败都能沉淀下可复用的经验。








