目录

APO - GEPA

上一篇中,我们了解了 LLM 评估和 APO 的基本概念,今天我来学习 DSPY 提供 APO 的一种实现 GEPA。

GEPA 整体流程

GEPA 整体分为三大阶段:准备初始化阶段 → 循环迭代阶段(重复直到预算耗尽) → 终止输出阶段

文本流程简图(ASCII)

/images/aigc/evaluate/gepa.png

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
输入材料(训练集、打分器、baseline提示词)
【准备阶段】拆分数据集D_pareto / D_feedback,初始化候选池,设置优化预算
【循环开始(预算未耗尽则循环)】
        ├─①根据D_pareto上的表现,选出本轮待优化种子prompt
        ├─②随机选一个模块作为本轮优化对象
        ├─③D_feedback采样minibatch小样本,运行得到完整执行轨迹
        ├─④Teacher大模型反思诊断,生成新版本prompt
        ├─⑤第一层刹车校验:同minibatch对比新旧版本
        │    ├─如果新版本更差 → 直接丢弃,回到循环开头
        │    └─如果新版本更好 → 继续往下执行
        ├─⑥新版本在D_pareto跑分,加入候选池
        └─⑦下一轮根据D_pareto分数再次筛选;回到循环开头判断预算
        ↓(预算耗尽,跳出循环)
【终止输出阶段】候选池选出最优prompt,在隔离验证集评测

第一阶段:准备初始化阶段

1. 输入基础材料,数据集拆分,初始化候选池

  • 怎么做:
    1. 输入训练数据集、打分机制;准备完全隔离的验证数据集(仅用于最终评测)
    2. 将训练集trainset拆分成2个子集
      • D_pareto:小部分数据(如20%),用于候选池打分和候选挑选
      • D_feedback:大部分数据(如80%),用于循环迭代采样
    3. 初始化候选池candidates pool
      • 池内放入初始baseline基线提示词
      • 把baseline在D_pareto跑出来的pareto_scores存入候选池
  • 为什么这么做:
    1. D_pareto与D_feedback承担不同职责;验证数据集完全隔离,保证评估结果的可靠性
    2. 候选池用来保存不同版本提示词+对应D_pareto分数,作为后续迭代的种子库

2. 设置预算驱动(budget‑driven)

  • 怎么做:
    1. 预先设置优化预算,预算对应循环迭代总轮次
    2. 预算耗尽即停止优化,无提前终止逻辑
  • 为什么这么做:
    1. 将提示词优化转化为成本工程问题,而不是单纯追求算法效果
    2. 在可控成本内拿到该预算条件下最好结果,提升工程实践价值

第二阶段:Loop循环迭代(只要预算没耗尽,完整循环会反复执行)

双重淘汰机制:①minibatch快速刹车(本轮直接丢弃);②根据D_pareto分数筛选(下一轮不再选中)

3. 根据D_pareto上的表现挑选本轮待优化的prompt

  • 怎么做:
    1. 淘汰池内各方面都很差的候选:在D_pareto的所有测试用例中,没有任何一项分数是所有候选中最高的。通俗来说,就是没有任何特长
    2. 除了这种各方面都很差的候选,其他候选都保留:无论是综合表现好,还是只在某一项上表现很好、其他项很差
    3. 在保留下来的候选集合中随机选1个,作为本轮要优化的目标prompt
  • 为什么这么做:
    1. 不是只选总分最好、平均分最好或者各方面都好的候选,避免在一个方向上越优化越深,却在其他方面表现越来越差
    2. 保留各个候选的局部优点,后续有机会把多个局部优点合并,保障泛化能力

4. 随机挑选本轮待优化的DSPy模块

  • 怎么做:
    1. 如果AI应用包含多个DSPy模块,本轮随机挑选其中一个模块,本轮只优化该模块
    2. 如果是单模块应用,直接使用唯一模块作为优化对象
  • 为什么这么做:
    1. 解决多模块优化“按下葫芦起了瓢”现象;防止优化一个模块,把其他模块性能搞坏

5. 采样minibatch小样本,运行并保存完整执行信息

  • 怎么做:
    1. 从D_feedback大数据集中随机抽取一小部分样本,作为本轮minibatch
    2. 本轮迭代内部minibatch保持不变;下一轮循环会重新随机采样新minibatch
    3. 在minibatch上运行当前待优化prompt
    4. 完整记录:分数、全部输入输出、thinking思考过程等完整执行轨迹
  • 为什么这么做:
    1. 成本均衡,跑全量D_feedback速度慢;轮次足够、采样随机可以保障通用性
    2. D_feedback和D_pareto做训练数据隔离,防止模型在单一方向过度学习,其他方向性能退化

6. Teacher模型做反思,改写生成新版本prompt

  • 怎么做:
    1. 将minibatch全套执行信息(分数、输入输出、思考轨迹)交给高质量teacher模型
    2. teacher模型分析分数高低与输入输出之间关联,定位失败原因,做反思诊断
    3. 根据反思结果改写prompt,生成新版本prompt
  • 为什么这么做:
    1. 需要能力强的teacher模型做诊断反思;student小模型反思能力不足,不能充当teacher角色
    2. 将失败案例的经验总结,转化为对student小模型的提示词改进

7. 第一层过滤:minibatch快速刹车校验

  • 怎么做:
    1. 将刚生成的新版本prompt,跑在本轮固定不变的minibatch数据集
    2. 对比新版本与旧版本在同一个minibatch的分数
    3. 分支判断:
      • 分支A:新版本分数变差 → 直接丢弃新版本,结束本轮,回到循环开头
      • 分支B:新版本分数有提升 → 继续向下执行
  • 为什么这么做:
    1. teacher模型本身可以看到minibatch样本;如果在极小样本上已经回退,更大数据集大概率同样回退
    2. 快速丢弃无效优化,节省LLM调用成本,实现高效刹车

8. 新版本在D_pareto上评估,加入候选池

  • 怎么做:
    1. minibatch校验通过的新版本prompt,放到D_pareto数据集完整运行
    2. 获取新版本对应的pareto_scores
    3. 将新版本prompt + pareto_scores存入候选池
  • 为什么这么做:
    1. minibatch上有提升,不等于D_pareto上一定表现更好
    2. 存入候选池,下一轮再根据D_pareto的各项分数做第二层淘汰

9. 第二层过滤:下一轮根据D_pareto分数再次筛选

  • 怎么做:
    1. 新版本存入候选池之后,不立刻处理;等待下一轮循环执行步骤3
    2. 如果新版本在D_pareto的每一项上都比其他候选差,下一轮步骤3筛选时会被剔除,永远不会被选为优化种子
    3. 判断预算:预算没有耗尽 → 返回循环开头;预算耗尽 → 跳出循环
  • 为什么这么做:
    1. 构成完整双重淘汰机制:minibatch不行直接丢弃;minibatch通过但在D_pareto的每一项上都比其他候选差,虽然保存在池子,但不会被选中,等价淘汰
    2. 持续保证候选池整体质量不断变好

第三阶段:终止输出阶段

10. 预算耗尽,选出最优提示词,隔离验证集做最终评测

  • 怎么做:
    1. 停止循环,从候选池中选出综合表现最优的提示词
    2. 在全程没有参与优化流程的隔离验证数据集上运行评估,输出最终指标
  • 为什么这么做:
    1. 使用从未见过的数据,检验优化后prompt真实泛化效果

GEPA vs MIPROv2

MIPROv2的局限是第一阶段生成候选Prompt、第二阶段统一打分,打分结果无法反过来继续优化已经生成的候选Prompt。

  1. MIPROv2:两阶段分步架构;一次性生成全部候选prompt,之后统一打分;打分得到的信息无法反馈回去修改已经生成的候选
  2. GEPA:持续循环架构;每一轮都拿打分、执行结果反馈驱动下一轮prompt改写
  3. DSPy 的优化器指引认为,MIPROv2适合小样本、依赖few‑shot的任务,GEPA擅长多样化任务
  4. 从架构设计上看,MIPROv2能做好的事情,GEPA应该也能做好;反过来则未必。MIPROv2的绝对成本可能更低,需要根据对性能和成本的关注做取舍

总结

APO 对于大规模 AI Agent 开发有价值。当应用 Prompt 或 System Prompt 扩展到成千上万的维护数量时,人工逐个优化和迭代的时间与成本都会很高。

APO 的前提仍然是高质量数据集和高质量打分器。MIPROv2、GEPA 等中间算法和策略可以持续演进,但数据集和打分器是不变的基础。