当 Prompt 优化器在 6 个任务里 4 个原地踏步:MOCHA 用切比雪夫退火给 Agent 技能找出路
核心摘要
如果你最近在折腾 Claude 的 SKILL.md,或者在做任何"把一段 prompt 塞进有字符上限的字段里、让 Agent 自动调用"的事,这篇论文你大概率会有共鸣。
它讲了一件挺扎心的事:在 6 个 Agent 技能优化任务上,TextGrad、ProTeGi、GEPA 这三个目前业内常见的 prompt 优化器,有 4 个任务跑了 1000 次 rollout 之后,输出的还是初始的种子 prompt 一字未改——不是改坏了被回滚,是从头到尾根本就没动过。作者的解释也很直接:技能字段有硬性长度限制(description ≤ 1024 字符、body ≤ 5000 字符),改 prompt 想涨准确率,几乎注定要顶破这两个限制;而单目标优化器面对"涨了准确率但违反约束"的候选,会直接拒掉,于是一步都迈不出去。
MOCHA 用了一个老套但用得很对的工具——切比雪夫标量化(Chebyshev scalarization)+ 指数退火——把 prompt 优化建模成多目标搜索:前期靠超体积贡献(HVC)撑开帕累托前沿,后期慢慢退化到切比雪夫接受准则去精修。结果是平均准确率比最强基线相对提升 7.5%,FEVER 上拉了 14.9%,TheoremQA 上拉了 10.4%,还顺手发现 2 倍数量的非劣解。这篇文章不在于方法多新——多目标进化早就有切比雪夫——而在于把"Agent 技能字段是受平台约束的多字段产物"这个事实当回事,并且用对了工具。
论文信息
- 标题:MOCHA: Multi-Objective Chebyshev Annealing for Agent Skill Optimization
- 作者:Md Mehrab Tanjim, Jayakumar Subramanian, Xiang Chen, Branislav Kveton, Subhojyoti Mukherjee, Anlan Zhang, Sungchul Kim, Somdeb Sarkhel, Sunav Choudhury
- arXiv:2605.19330(v1, 2026-05-19)
- 类别:cs.AI / cs.LG / cs.SE,预印本 25 页
一、问题是怎么来的:一个被忽视已久的细节
先说点背景。最近一年 Agent 圈出现一个新的工件叫 SKILL.md——Anthropic 在 Claude 的 Skills 体系里推的格式,简单讲就是"给 Agent 的可调用模块":YAML 头部(name、description、metadata、allowed-tools、compatibility 等)+ Markdown 正文(具体执行指令)。
这玩意儿和我们以前理解的"prompt"有几个本质区别:
- 多字段:description 和 body 是两个独立字段,各自有用
- 路由用 description:Agent 在选用哪个 skill 时只看 description,所以这个字段太长会被截断、影响检索
- 执行看 body:Agent 真正执行时把 body 注入上下文,太长会被压缩或挤占其他 skill 的位置
- 共驻:多个 skill 同时存在于一个有限的上下文窗口里,相互抢资源
平台对此有硬约束:description 不能超过 1024 字符,body 不能超过 5000 字符。Anthropic 这么设计是有道理的——你要让一个 Agent 同时挂几十个 skill,就必须给每个 skill 一个明确的"占地面积"上限。
那么问题就来了:当你想优化一个 skill 的准确率,几乎一定要往里加规则、加示例、加 step-by-step——而这些都会撑大 body 字段。
我之前调过一段类似的东西,那种感觉很熟悉:你眼看着一个加了几条详细规则的版本准确率涨了 8 个点,但 body 从 4800 变成 6300,平台直接拒掉。回过头看 prompt 优化器,它根本不知道 body 被截了——它收到的反馈是"这个候选准确率涨了",于是开心地接受了;但部署一上线,body 被平台 truncate 到 5000 字符,最后那几条规则根本没进 context,准确率反而掉了。
这就是这篇论文要切入的痛点:skill 优化天然就是个多目标问题。准确率(correctness)、description 合规(≤1024)、body 合规(≤5000)—— 三个目标,互相拉扯。

图1:左图——种子技能 p₀ 一开始干干净净都满足约束,但准确率只有 0.72;优化后的 p 准确率涨了,但 body 长度爆掉、compliance 跌到 0.43。右图——MOCHA 把这个过程切成探索(推宽前沿)和开发(精修极值)两段,避免被单一方向卡住。*
二、为什么现有的 prompt 优化器在这个任务上不灵
这是论文最值得细看的部分。作者做了个非常严格的对照:所有方法用同一个 mutation 接口(同样的 SkillMdProposer)、同样的 LLM mutator(Claude Opus 4.6)、同样的 1000 rollout 预算、同样的逐目标文本反馈——也就是说所有优化器收到的"候选生成能力"和"候选反馈信号"都一样。
唯一不一样的就是接受/选择策略。
来看主表:
| Skill | Seed | TextGrad | ProTeGi | GEPA | MOCHA |
|---|---|---|---|---|---|
| GPQA | .592 | .592 (无变化) | .592 (无变化) | .592 (无变化) | .636 |
| TheoremQA | .534 | .672 | .690 | .656 | .762 |
| HoVer | .618 | .618 (无变化) | .618 (无变化) | .618 (无变化) | .660 |
| HotpotQA | .336 | .592 | .622 | .602 | .600 |
| FEVER | .632 | .632 (无变化) | .632 (无变化) | .632 (无变化) | .726 |
| DebugBench | .615 | .615 (无变化) | .615 (无变化) | .615 (无变化) | .666 |
| Mean | .554 | .620 | .628 | .619 | .675 |
注意标"无变化"的那 4 行——GPQA、HoVer、FEVER、DebugBench——三个基线方法跑 1000 次 rollout 之后,输出的就是种子 skill 一字未改。这不是说效果差,是根本没动。
我看到这个表的第一反应是不太相信,但作者的解释挺有说服力:种子 skill 已经是"干净简短但能力一般"的状态——它满足所有合规约束(description 短,body 短),但准确率只是 baseline 水平。优化器收到反馈"准确率不够",mutation LLM 提出一个"加了详细规则的更长版本",这个版本准确率确实涨了,但 body 撑爆了——
- TextGrad 是贪心的:只接受全面优于当前 best 的候选。准确率涨了但 compliance 跌了,这就不是 dominate,拒。
- ProTeGi 用 UCB beam search:优化标量得分。如果它把 compliance 计入 score,那准确率涨 + compliance 跌可能 net 是负的。
- GEPA 是 Pareto-aware 的,但它的 selection 是"在每个 validation 数据点上是否最优"——这个粒度还是单目标的,没法处理跨字段的合规张力。
三种策略殊途同归:只要候选在某个维度退步了,就被拒。问题是种子 skill 已经在合规边界附近,往任何"提升准确率"的方向走几乎都意味着合规性退步。于是优化卡死。

图2:横轴是迭代数(一次迭代≈一次完整的 mutation+evaluation+commit),纵轴是测试集 correctness。FEVER 和 DebugBench 那两张图最直观:基线方法的曲线(橙色虚线、红色虚线)就是一根紧贴种子线(灰色虚线)的水平线,从头到尾没爬过种子。
这个图我盯了挺久,因为它揭示了一个特别有意思的事:问题不在 mutation——LLM 完全有能力提出更好的候选——问题在 acceptance。如果连一个"准确率 +8% 但 body +20%"的候选都接受不了,那再多的 rollout 也是浪费。
三、MOCHA 的核心:用切比雪夫"接受所有方向的进步"
理解了问题,方法就直观了。MOCHA 的两个核心组件:
3.1 切比雪夫标量化:先解决"哪些非凸最优解被线性方法漏掉"
多目标优化里一个非常本质的差别——线性标量化和切比雪夫标量化对非凸前沿的覆盖能力不同。
线性标量化是大家最熟的:
这就是把多目标搞成加权求和。直觉上很合理,但有个理论结果:线性标量化只能找到帕累托前沿的凸包上的点。如果前沿是非凸的(比如中间凹下去一块),无论你怎么调权重 w,那块凹的最优解都摸不到。
切比雪夫不是这样:
它衡量的是"最坏维度上离理想点 \(z^* = (1,1,\ldots,1)\) 的加权距离"。这个 max 函数让它能"贴住"非凸区域——理论上对任何帕累托最优点 \(p^*\),都存在一个权重 \(w^*\) 使得 \(p^*\) 是切比雪夫最优解(Miettinen 1999 的经典结论)。
工程上怎么用?MOCHA 在每次迭代随机抽一个权重向量 \(w \sim \mathrm{Dirichlet}(\mathbf{1})\)——也就是从单纯形上均匀采样——然后选 \(\arg\min_p s_w(p)\) 作为 parent 去 mutation。这相当于每一步都从前沿的某个随机方向去推。
我个人觉得这个设计的精髓在于:它把"权重该怎么设"这个最讨厌的问题甩给了均匀采样。你不需要事先决定"准确率比合规重要 70%"——每一轮都让随机权重决定,长程下覆盖整个前沿。
3.2 HVC 与退火:从"什么都接受"到"按方向精修"
只用切比雪夫还不够。论文做了实验,纯切比雪夫在有限预算下前沿很窄——因为你只在最近被采到的那个方向上推,其他方向的种子点根本没机会被改进。
所以 MOCHA 加了第二条机制:超体积贡献(Hypervolume Contribution,HVC)。
简单说,HV 就是一个解集在目标空间里"罩住的体积"(以原点为参考点)。一个新点的 HVC 就是它新增的体积——只要它给前沿任何方向带来扩展,HVC 就为正。
HVC 的好处是方向无关:它不管 mutation 是冲哪个方向去的,只要对前沿有贡献就接受。
然后 MOCHA 把这两套接受准则用指数退火串起来:
其中 \(b\) 是已消耗预算,\(B\) 是总预算。优化早期 \(\tau\) 大,接受准则是 HVC 大于 τ 才接受——只要候选给前沿带来增量就要;后期 \(\tau \to 0\),切换到 切比雪夫接受——候选必须在 parent 被采样时的同一方向 \(w\) 上严格变好才接受。
整个算法跑一遍是这样:
init: pool P = {seed}, buffer B = ∅
while budget remaining:
w ~ Dirichlet(1) # 1. 随机权重
parent ← argmin_p s_w(p) # 2. 切比雪夫选 parent
candidate ← LLM_mutate(parent, feedback) # 3. mutation
τ ← exponential_anneal(b/B) # 4. 退火阈值
if τ > 0: # 5. 探索期
if HVC(candidate, P) > τ:
commit candidate to P
else: # 6. 开发期
if s_w(candidate) < s_w(parent):
commit candidate to P
我特别喜欢这个设计的一点是它每一步只看一个 w——选 parent 用 w,开发期接受也要求在同一个 w 方向上变好。这种"一个 w 配一个完整决策"的设计让噪声不会乱窜:你在某个方向上选了 parent 又拒了 candidate,至少这个方向上是自洽的。

图3:HV 数值印在图例里——MOCHA 是 0.563,跟 w/o HVC(0.554)和 w/o Annealing(0.585)形成明显的"探索-开发光谱"。w/o Annealing 因为一直在探索阶段,HV 最大但准确率被牺牲了;w/o HVC 一直在开发,准确率高但只剩两个解。MOCHA 居中——这就是退火设计想要的效果。
四、消融实验:把 MOCHA 拆开看每个零件值不值钱
消融实验是这篇论文我个人最喜欢的部分,因为它干脆把"MOCHA 是哪一部分起作用"这个问题摊开来讲清楚了。
| Variant | Correctness | Δ vs ProTeGi | HV | #PF |
|---|---|---|---|---|
| Best Baseline (ProTeGi) | .628 | — | .514 | 1.6 |
| w/o HVC(纯开发) | .687 | +.059 | .530 | 3.4 |
| MOCHA(平衡) | .675 | +.047 | .531 | 3.6 |
| w/o Annealing(纯探索) | .671 | +.043 | .533 | 3.8 |
三件事很清楚:
1. 一条干净的探索-开发光谱:去掉 HVC(剩纯切比雪夫开发)准确率最高(.687),但前沿点最少(3.4);去掉退火(剩纯 HVC 探索)前沿最丰富(3.8 个 Pareto 点)但准确率被压到 .671;MOCHA 居中。这恰好就是设计意图——你把退火的"温度"从无穷高(纯探索)调到 0(纯开发),中间这条曲线就是 MOCHA。
2. 即使最弱的 MOCHA 变体也碾压最强基线。w/o Annealing 是 .671,仍比 ProTeGi 的 .628 高出 4.3 个百分点——而三个基线之间的差距才 0.9 个百分点(.619 到 .628)。这个观察特别值得注意:真正起作用的是"多目标 selection 框架"本身,而不是 HVC 或退火哪个具体组件。换言之,只要你不再用单目标接受准则,效果就上来了。
3. 用户可以选自己的工作点。如果你只关心 raw 准确率,把 HVC 关掉(纯开发);如果你做的是后续要选用变体的 pipeline,把退火关掉(纯探索拿到丰富前沿);想要平衡就用默认。这个模块化是真挺好用的。

图4:颜色越绿表示提升越大。TheoremQA、FEVER、DebugBench 这三列普遍是亮绿色——这些都是"objective conflict 强"的任务,MOCHA 的设计正好对症。HotpotQA 那行几乎是红的(接近 0 或负),但这个任务的种子分数只有 .336,所以任何方法都能涨——选择策略反而不重要。
五、看看到底优化出了什么样的 skill
我一开始以为这种优化最后产出的就是"加几条 rule"那种简单变种,但 MOCHA 的进化树暴露出来挺细致的搜索行为。

图5:注意 root 节点的 C/D/B 标签——比如 GPQA root 是 C=0.61, D=0.90, B=0.98(合规很好但准确率低),最优蓝节点 4 是 C=0.67, D=0.72, B=0.26——准确率涨了 6 个点,但 body compliance 从 0.98 跌到 0.26(body 被撑长了)。这就是非凸前沿的真实样子:你想要准确率,就得在 compliance 上让步,没有"啥都好"的解。
每棵进化树都揭示了MOCHA 接受了"局部最优看起来不优"的中间变体作为 parent——比如 HotpotQA 的最优蓝节点 2 是从 root 直接长出来的,而它的子节点 4-8 准确率都在 53%-55%,比 parent 低,但仍被纳入 pool(只要在某个方向上有 HVC 贡献就行)。这就是探索阶段的特征:允许某个维度退步,只要扩展了前沿。
而基线方法的等价"进化树"就是一根直线——root 自己(或者 root → 一个 commit 然后 stop)。
六、几个我想要表达的批判性观察
聊点这篇论文我觉得没那么完美的地方,免得读起来像吹捧。
第一点:基线"完全不动"这个现象需要再多一层解释。论文给出的理由是单目标优化器在合规约束附近被卡死。这个解释直观,但我有点怀疑实验里基线的实现是不是"过于贪心"了——比如 TextGrad 和 ProTeGi 在标准实现里一般会维护一个候选 buffer,至少 keep 几个 second-best 候选用于扰动。如果这些被工程实现简化掉了,那"卡死"现象部分是被"严格的接受准则"放大的。作者声称所有方法 share 同样的 mutation 接口,但是否 share 同样的"候选 buffer / 扰动"机制,论文里没写得特别清楚。这块如果能再做一组对照——基线带 buffer vs MOCHA——会更扎实。
第二点:1024/5000 这两个数字带来的张力是 Anthropic 平台特有的。论文 limitation 里也承认了这点——换一个 SKILL 规范(比如其他厂商规定 description ≤ 512),整个 trade-off 形态会变。这意味着论文得到的"基线 4/6 卡死"这个数字不是普适规律,是特定约束 × 特定模型 × 特定任务下的现象。但反过来说,Anthropic 这套 SKILL.md 体系已经被业内广泛跟进,FAR-AI 和 Voyager 类工作都在沿用,所以这个特定场景的覆盖面也不算小。
第三点:HotpotQA 这个反例值得多说两句。在这个任务上 MOCHA(.600)反而被 ProTeGi(.622)压了一头,差距在一个 std 之内。论文给的解释是"低冲突任务"——种子分数只有 .336,加个简单的格式 prompt 就能涨 66%(.336 → .560)。我觉得这个观察很诚实,但同时它也说明 MOCHA 的多目标机器不是免费的——在低冲突任务上,搜索预算被分摊到"维持前沿多样性"上,反而拖累了 raw 准确率。这给实践带来一个问题:怎么提前判断一个任务是高冲突还是低冲突?论文 limitation 里也承认这是 open question,目前只能跑一会儿看看。
第四点:切比雪夫 + 退火不是新发明。多目标进化算法(NSGA-II、MOEA/D 这些)几十年前就有切比雪夫分解,HVC 也是 1999 年的东西。这篇论文真正的工作不在算法设计,而是把这套成熟工具搬到了 LLM prompt 优化这个新场景,并且发现搬过来居然这么必要。这种工作我个人挺喜欢的——它不是炫技的"我发明了一个新算法",而是"我发现这个领域还在用错的工具、换正确的工具就能涨这么多"。
七、对工程实践的几点启发
回到 SKILL.md 这个具体场景,如果你正好在做相关的事:
启发 1:多目标 selection 的门槛比想象中低。MOCHA 的核心算法换算成代码大概 200 行——切比雪夫得分计算、HV 计算(3 维下 \(O(n^2 \log n)\))、Dirichlet 采样、指数退火。你不需要拿出 NSGA-II 整套,只要在现有 prompt 优化器的 acceptance 步骤上换一个准则即可。
启发 2:mutation 阶段必须感知约束。论文里这个细节很重要:mutation prompt 里明确告诉 LLM "body 当前 6412 字符,超过了 5000 限制"。这样 LLM 在生成下一版时会主动压缩。如果 mutation 不感知约束,selection 再聪明也无济于事——选来选去候选都是越界的。
启发 3:保留一个候选 buffer 是普遍有用的事。MOCHA 用了大小为 K=5 的 priority queue 缓存"HVC 为正但还没达到 commit 阈值"的候选——等到某个候选超阈值就把整个 buffer 里 HVC 最高的 commit。这个 trick 干的事其实是把廉价的 mutation 评估和昂贵的 validation 解耦,避免每个候选都做完整 validation。这个工程优化在很多 prompt 优化场景都用得上。
启发 4:如果你的任务"低冲突",MOCHA 其实没什么用。先跑两三轮看看种子准确率离合规约束有没有顶撞——如果加几句简单 prompt 就能涨而不撑爆字段,那就用最简单的贪心优化器。MOCHA 的开销(多维 HV 计算、buffer 维护)在低冲突任务上是纯成本。
启发 5:a-posteriori 思维值得借鉴到更广的 prompt 优化场景。MOCHA 不强求"返回一个最好的 skill"——它返回整个帕累托 pool,让用户选。这种"最终决策权交还人类"的设计在 production 里其实很重要——因为你的部署偏好(要更准确还是要更省 token)会随场景变,预先 hardcode 一个 trade-off 不灵活。
八、收尾
读完这篇论文的整体感受是:它揭示了一个被广泛忽视的事实——大部分现有 prompt 优化器在面对真实平台约束时是失能的。这个失能不体现在"性能略差",而体现在根本不动。 6 个任务里 4 个完全卡死,这不是 marginal 的差距,是 categorical 的差距。
而解决这个问题的工具——切比雪夫标量化、HVC、退火——并不新,新的是把它们用对地方。这是我特别愿意看到的论文:不靠堆模型、堆算力、堆数据,靠理解问题的真实结构找到杠杆点。
如果你正在调任何一个有"多个硬约束 + 主目标"结构的优化任务(不只是 SKILL.md,也包括代理调度、tool 选择、检索 ranker 等等),这篇论文给的思路是值得抄一下的。最简单的做法:在你的 acceptance 准则上加一条切比雪夫接受 + 一个 HVC-based exploration buffer。这两点改动,不需要改 mutation、不需要换基础模型,可能就能从"原地踏步"切到"持续改进"。
最后,这篇论文也让我对未来一个方向更乐观——Agent 技能库不是手工艺品,是可以自动进化的系统。SKILL.md 这种结构化产物,相比单体 prompt 提供了更明确的"参数空间",让自动优化变成有迹可循的搜索问题。MOCHA 是这条路上的一个清晰节点:它告诉我们工具应该是什么样的。下一步当然是和 skill discovery(怎么自动决定该有哪些 skill)、meta-harness optimization(怎么优化 pipeline 结构本身)这些方向打通,那就是 end-to-end 的 agent skill evolution 了。
觉得有启发的话,欢迎点赞、在看、转发。跟进最新AI前沿,关注我