SkillOpt 的「文本学习率」:当 Markdown 文件开始像权重一样训练
> 本文是对 arXiv:2605.23904(《SkillOpt: Executive Strategy for Self-Evolving Agent Skills》,Microsoft + 上海交大 + 同济 + 复旦)的深度补充研究。原帖已拆解跨工具链迁移实验和 transferred/direct 比值,本文聚焦三个被原帖忽略但更值得深挖的设计:文本学习率、拒绝编辑缓冲区、以及「52 格全胜」背后的统计陷阱。
一、文本学习率:把 Markdown 当权重训练
SkillOpt 最被低估的概念是文本学习率(textual learning rate)。论文原文:
> "the textual learning rate and schedule control how far one skill version is allowed to move from the previous one"
这不是比喻,是操作定义。在深度学习中,学习率控制每步权重更新的幅度:$\theta_{t+1} = \theta_t - \eta \nabla L$。SkillOpt 把这个概念搬到文本空间——每次编辑只能 add/delete/replace 有限数量的 token,skill 版本之间的「距离」被硬约束在某个上界内。
为什么这个约束重要?论文给了一个精确的类比:
> "if consecutive skill revisions move too far or in inconsistent directions, rejected edits and previous accepted edits no longer provide a meaningful optimization history."
翻译成深度学习语言:如果学习率太大,梯度噪声会淹没信号,训练发散。SkillOpt 的文本学习率就是防止 skill 训练发散的正则化项。
但文本空间和权重空间有一个根本区别:权重空间是连续的,文本空间是离散的。权重更新 $\Delta\theta = -\eta \nabla L$ 可以是任意方向、任意幅度;文本编辑只能是 add、delete、replace 三种操作之一。这意味着文本学习率的「调度」更像是编辑预算(edit budget)——每轮允许改多少个 token,而不是每步允许移动多远。
论文的设计是:每轮编辑预算有限,只有通过 held-out 验证集的编辑才被接受。这等价于深度学习中的早停(early stopping)+ 验证集选择——不是训练到收敛,而是训练到验证集不再提升就停。
二、拒绝编辑缓冲区:负样本的复利效应
SkillOpt 第二个被忽视的设计是拒绝编辑缓冲区(rejected-edit buffer)。论文说:
> "rejected edits are retained as negative feedback, while the epoch-wise slow/meta update preserves longer-horizon regularities."
大多数 prompt 优化方法(TextGrad、GEPA、EvoSkill)只保留接受的编辑——拒绝的编辑被丢弃。SkillOpt 不一样:拒绝的编辑被存入缓冲区,作为后续优化器的负样本。
这等价于深度学习中的负样本挖掘(negative mining)。在对比学习(contrastive learning)中,负样本和正样本一样重要——模型需要知道「什么是对的」和「什么是错的」才能学到有判别力的表示。SkillOpt 的优化器模型同时看到:
- 正样本:被接受的编辑(held-out 分数提升)
- 负样本:被拒绝的编辑(held-out 分数不升或下降)
这和 Self-Harness(arXiv:2606.09498)的失败签名聚类有结构同构性:两者都把负样本作为一等公民。Self-Harness 聚类失败 trace,SkillOpt 缓存拒绝编辑——知道什么不奏效和知道什么奏效一样重要。
三、52 格全胜:统计幻觉还是真优势?
原帖提到 SkillOpt 在「52 个(模型, 基准, 工具链)格」上全胜或并列第一。这个数字很惊人,但需要拆解:
- 7 个目标模型 × 6 个基准 × 3 个工具链(direct chat, Codex, Claude Code)= 126 格
- 论文报告的是 52 格——不是所有组合都有意义,有些组合的 baseline 已经接近天花板
论文报告了 GPT-5.5 上的提升:direct chat +23.5pp、Codex +24.8pp、Claude Code +19.1pp。这些是平均提升,不是单格提升。平均提升的统计意义强于单格全胜——前者聚合了多个基准的信号,后者可能只是噪声。
四、跨工具链迁移:为什么「搬过来的 skill」有时比「原地训练的」还强
原帖提到一个反常数据:GPT-5.4-nano 上 LiveMath 的 transferred 28.8 高于 direct 27.2。论文的解释是:
> "partially learned procedures irrelevant to the target model"
翻译成人话:在 A 模型上学到的某些编辑,对 B 模型不仅无用,还有害。当这些有害编辑被「搬过来」时,反而比 B 模型自己从头训练(会学到那些有害编辑)更好。
这是一个反直觉的洞察:迁移有时比原地训练更好,因为迁移天然过滤掉了目标模型会误学的编辑。
为什么?因为迁移的 skill 只包含「在 A 模型上通过 held-out 验证」的编辑。这些编辑通过了 A 的验证,不一定会通过 B 的验证——但它们至少是经过严格筛选的。而 B 模型自己从头训练时,会学到一些「在 B 上通过验证但其实是过拟合」的编辑。
这和「预训练-微调」范式有结构同构性:预训练模型的迁移学习往往比从头训练更好,因为预训练阶段已经过滤掉了大量噪声。SkillOpt 的 skill 迁移是文本空间的预训练-微调——A 模型做预训练(学通用 skill),B 模型做微调(用这个 skill)。
五、best_skill.md:可审计的 AI 资产
SkillOpt 最被低估的工程价值是导出形态。论文说:
> "The deployed output is a compact best_skill.md file of roughly 300–2,000 tokens"
这个文件大小意味着什么?一个 2000 token 的 Markdown 文件,人可以在 5 分钟内通读。每个编辑都留下 edit_apply_report.json,包含 per-edit 的 accept/skip 状态和原因。
这是 AI 资产的可审计性(auditability)革命。 对比:
- Fine-tune 权重:几十亿参数,人无法通读,无法 PR review
- Prompt template:几百 token,但每次改动不留下结构化记录
- best_skill.md:300-2000 token,人可通读,每个编辑有 accept/skip 报告
六、与 TextGrad/GEPA/EvoSkill 的分水岭
SkillOpt 不是第一个做文本空间优化的工作,但它和 TextGrad、GEPA、EvoSkill 有三个关键区别:
| 维度 | TextGrad / GEPA / EvoSkill | SkillOpt |
|---|---|---|
| 导出形态 | 参数化 skill / prompt template | 人可读 Markdown 文件 |
| 接受条件 | 分数提升 | held-out 严格提升(非 held-in) |
| 负样本利用 | 丢弃 | 拒绝编辑缓冲区 |
| 学习率 | 无显式约束 | 文本学习率 + 编辑预算 |
| 跨工具链迁移 | 未系统研究 | 52 格迁移实验 |
七、结语:文本空间的「梯度下降」
SkillOpt 的真正意义不在于那 52 格全胜,而在于它验证了一个假设:文本空间可以做梯度下降。不是 metaphorical 的梯度下降——是 operational 的:有学习率、有验证集、有早停、有负样本、有 momentum term。
这意味着什么?意味着未来 Agent 的 skill 不需要人写,也不需要 LLM 一次性生成——它可以被训练。训练 skill 和训练权重用同一套方法论(学习率、验证集、早停、负样本),只是训练空间从连续的权重空间换成了离散的文本空间。
这是「skill 作为一等训练对象」的宣言。 以前 skill 是 prompt 的附属品,现在 skill 是训练的主体。best_skill.md 之于 Agent,相当于 weights 之于基座模型——都是训练产物,只是一个是人可读的,一个是人不可读的。
步子哥之前分享 Rebucca 时提到「小模型+大模型复核」的分工原则。SkillOpt 是这个原则的另一个切面:优化器模型(大)负责生成编辑,目标模型(小)负责执行和验证。分工比统一更有效——这个原则在 SkillOpt 里再次成立。
---
论文链接:https://arxiv.org/abs/2605.23904
代码:https://github.com/microsoft/skillopt
PyPI:pip install skillopt
导出形态:300-2000 token 的 best_skill.md + edit_apply_report.json