数据截至 (上游 commit fd6b9a6352af)
自演化引擎:评测闭环与优化器家族
30 秒导读: EvoAgentX 最大的卖点不是"跑一个多智能体工作流",而是自动把它改得更好。 做法是:把工作流里的每个 prompt / 代码块暴露成一个"可读写的目标",然后拿一个数据集(benchmark) 反复给这套工作流打分,让某种搜索算法根据分数去改这些目标——像给 prompt 做梯度下降。本章讲清 这套"评测闭环 + 优化器家族"是怎么搭起来的。
前几章已讲过工作流本体:03-workflow-execution.md(WorkFlowGraph 与执行引擎)、
04-auto-construction.md(从一句话目标自动生成工作流)。本章不重复图/agent 的结构,
只聚焦一件事:一个已经能跑的工作流,如何用分数反向迭代改进自己。
1. 这是什么(零基础也能懂)
-
一句话定义: 一台"自动调优机"——把工作流当成待优化对象,拿测试集分数当反馈, 反复改工作流里的 prompt(有时也改结构/代码),让分数越来越高。
-
它解决什么问题: 你手搓了一个多智能体工作流去做数学题,准确率 62%。你怀疑 某个节点的 prompt 写得不好,但不知道怎么改、也不想一条条手试。自演化引擎替你干这件事: 你只要给它一个能打分的数据集,它自己去搜更好的 prompt,最后还给你一个准确率更高的工作流。
-
它给谁用: 想让 agentic workflow "自己进化"、而不是永远停在人工初版的研究者和工程师。
-
一句话直觉/类比: 把工作流当成一个有很多旋钮的机器,每个 prompt 就是一个旋钮; benchmark 就是一台打分仪。优化器不断拧旋钮、看打分仪读数、往读数变高的方向拧。 区别只在于"怎么拧":有的随机试(蒙特卡洛)、有的让 LLM 讲"哪里错了该怎么改"(文本梯度)、 有的用进化算法(交叉+变异)、有的用贝叶斯搜索。
-
用起来什么样: 下面这段(改编自
examples/optimization/textgrad/math_textgrad.py)是最小骨架—— 三步:建评测器 → 建优化器 →optimize()。# 示意,非源码:自演化的最小三步evaluator = Evaluator(llm=executor_llm, num_workers=5) # 1. 谁来打分optimizer = TextGradOptimizer( # 2. 谁来改graph=math_graph, evaluator=evaluator,optimizer_llm=optim_llm, max_steps=20,)optimizer.optimize(dataset=gsm8k) # 3. 拿数据集反复改进
本节不出现底层细节。记住一句:优化 = 评测闭环(打分)+ 搜索策略(改),两块拼起来。
2. 顶层全景(它大概怎么转)
整个子系统分两层:下层是评测闭环(把工作流在数据集上跑一遍、打出分数), 上层是优化器(拿这个分数当信号去改工作流)。所有优化器共用同一个下层。
怎么读这张图: 从上到下是一个优化步(step)的数据流;虚线是"分数反馈"回到优化器。
┌──────────────────────────────────────────┐
│ Optimizer(某个优化器) │
│ 持有 graph + 一套"怎么改"的策略 │
└───────────────┬──────────────────────────┘
① 提出一个改法(改某些 prompt / 代码块 / 结构)
▼
┌──────────────────────────────────────────┐
│ 可优化字段抽象(OptimizableField/Registry)│
│ 把改法写回 graph 里真实的 prompt 属性 │
└───────────────┬──────────────────────────┘
② 改后的 graph
▼
┌───────────────────────── 评测闭环 ───────────────────────────┐
│ Evaluator │
│ · 从 Benchmark 取一批数据(train/dev/test 三分) │
│ · 并行地把每条样本喂进 graph 执行,拿到预测 │
│ · 调 Benchmark.evaluate(预测, 标签) 打分 │
│ · 求平均 → 一个 metrics 字典 {acc: 0.71, ...} │
└───────────────┬──────────────────────────────────────────────┘
③ 分数
╎ (虚线反馈)
└╌╌╌╌╌╌╌╌╌╌╌╌╌╌► 回到 Optimizer:据此决定下一个改法
各部件一句话职责:
| 部件 | 干什么 | 在哪个文件 |
|---|---|---|
Benchmark | 数据源 + 打分器抽象:管 train/dev/test 三分,定义 evaluate(预测,标签) | evoagentx/benchmark/benchmark.py:13 |
Evaluator | 评测执行器:并行把一批样本跑过 graph、逐条打分、求平均 | evoagentx/evaluators/evaluator.py:20 |
Optimizer(抽象) | 优化契约:optimize / step / evaluate / convergence_check | evoagentx/optimizers/optimizer.py:12 |
OptimizableField / ParamRegistry | 把 graph 里任意 prompt/代码块暴露成可读写目标 | evoagentx/optimizers/optimizer_core.py:17、engine/registry.py:20 |
| 优化器家族 | 各家不同的"怎么改"策略(AFlow/TextGrad/MIPRO/EvoPrompt/SEW/MAP-Elites) | evoagentx/optimizers/*_optimizer.py |
主线走一遍(高层): 优化器提出改法 → 通过"可优化字段"写回工作流 → 评测闭环在数据集上打分 → 分数回到优化器 → 决定下一步怎么改 → 循环,直到收敛或用完步数。
3. 核心机制之一:评测闭环(打分是一切的前提)
优化的第一性原理:没有可信的分数,就没有优化。 所以先讲这层。
3.1 Benchmark —— 数据源 + 打分器
Benchmark 是抽象基类(benchmark/benchmark.py:13)。它同时管两件事:
-
数据的三分供给:
_train_data / _dev_data / _test_data三个切片,以及取数接口get_train_data / get_dev_data / get_test_data(benchmark.py:199-224),都支持indices/sample_k/seed抽样。这让"训练集搜、验证集调、测试集报"这套规范流程成为可能。 -
打分:每个子类必须实现三个抽象方法——
_get_id(样本唯一 id)、_get_label(取标准答案)、evaluate(prediction, label) -> dict(benchmark.py:73-85,返回如{"em": 1.0, "f1": 0.8})。 注意返回的是字典(可以多指标),不是单个数。
编码任务是特例。 CodingBenchmark(benchmark.py:227)在打分时不能只比字符串,而要
真的执行代码跑单元测试。它的 check_solution(benchmark.py:254)把模型生成的解答
exec 进一个受限命名空间,再 exec 测试代码,在 timeout 内调用 check 函数,返回
(SUCCESS/FAILED/TIMEOUT, 信息);还提供 compute_pass_at_k(benchmark.py:302)算 pass@k。
具体数据集就是这些抽象的实现,充当现成数据源:
| 数据集类 | 任务 | 文件 |
|---|---|---|
GSM8K | 小学数学应用题 | benchmark/gsm8k.py:39 |
HotPotQA | 多跳问答 | benchmark/hotpotqa.py:23 |
HumanEval | 代码生成(继承 CodingBenchmark) | benchmark/humaneval.py:32 |
还有
math_benchmark.py、mbpp.py、livecodebench.py、bigbenchhard.py、nq.py等, 套路一致:_load_data灌数据、evaluate定义指标。