最近微软的 SkillOpt 很火,几乎每个关心 agent 的人都会刷到。刚开始看这个工作时,我的第一反应其实有点怀疑:让 agent 执行任务,记录失败轨迹,再让模型根据反馈修改一段 skill 文档,这听起来并不陌生。它到底是在学 skill ,还是把 prompt optimization 换了一个更适合 agent 的名字?
但我把论文和项目材料看完以后,觉得这个问题不能只停在“是不是换壳”上。SkillOpt 最有意思的地方,不是它包装了一个新名词,而是它碰到了一个日常使用 agent 时很难绕开的问题:同一种错误出现第二次、第三次之后,agent 到底有没有学到什么?
我们最常见的处理方式仍然是人工纠正。比如这次发现 agent 改代码前没有看现有测试,于是在对话里提醒它,下次先读测试再动手。模型也可能在复盘里总结几条“以后需要更谨慎”的原则,但这段复盘随着上下文结束就失效了。更多时候,经验会被临时补进 prompt 或 skill 文件:加一句“检查边界条件”,再加一句“输出前确认结果”。短期看,问题像是被修掉了;换个任务之后,又可能在别处出现新的副作用。
这类是一类难以复用的经验。模型权重通常不可控,代码有测试和版本管理,但夹在中间的操作经验往往比较松散。客服 agent 遇到退款、发票、合规边界时,要知道先查什么、什么时候拒答、什么时候转人工;代码 agent 在 monorepo 里修改模块时,要知道该看哪些文件、跑哪组测试、避开哪些生成文件;数据分析 agent 面对 DAU 、收入、退款口径时,要知道如何核对定义。这些问题不完全是模型是否“知道答案”,更多是执行方法有没有被稳定保存。
讲到这里,SkillOpt 的问题就不能只问“是不是新算法”了。如果它的全部新意只是让模型改一份 Markdown ,那确实不够有说服力。但如果它在处理的是这些松散经验如何留下、如何验证、如何避免越改越坏,事情就没那么简单。要理解它为什么会长成现在这样,需要先回到更早的 TextGrad 。
TextGrad 其实回答了一个更宽的问题:如果 AI 系统里很多关键部件本来就是文本,能不能像调代码一样调它们?它的回答是可以。prompt 、答案、代码片段、工具说明、执行计划,都可以被看成等待修改的对象;反馈不一定是数值,也可以是一段批注、一次测试失败、一个评分器结果。所谓 textual gradient ,翻译成人话就是:告诉你哪里错了,为什么错,下一轮大概该往哪边改。
从使用者的角度看,这件事并不神秘。我们平时调 agent ,也是在先跑一遍,看错在哪里,再改 prompt 、改工具说明、改检查步骤。TextGrad 做的,是把这种调试动作整理成一个可反复执行的循环:运行、评价、修改、再运行。它比普通 prompt optimization 更宽,因为被改的不只是 prompt ;但它也没有真正回答另一个问题:哪些修改应该长期留下来? SkillOpt 接过的正是这个问题,只是它没有继续优化万物,而是把目标收窄到一份 agent 下次还会读取的 skill 文档。
这个收窄很关键。因为在 agent 系统里,skill 不只是一次性的提示词,它更像一本操作手册:哪些检查要先做,哪些工具什么时候用,哪些错误以前出现过,什么情况下不能贸然继续。普通 prompt optimization 更关心“怎么把这段话写得更容易让模型答对”,SkillOpt 关心的则更像“怎么让这份操作手册在一批任务之后变得更可靠”。
但操作手册也会腐烂。一次没查文件,不能自动总结成“每次任务都扫描全仓库”;一次测试漏跑,也不等于以后都要跑全量测试;一次指标口径错了,也不意味着应该往 skill 里塞一大段适用于所有场景的长规则。很多 prompt 或 skill 后来越来越重,就是因为它只会增加经验,不会判断哪些经验不该留下。
SkillOpt 的循环,真正处理的是这个问题。它先让 agent 带着当前 skill 去做任务,看它在哪些地方失败;失败之后让模型写一段反思,把问题压缩成对 skill 的修改建议;修改不能无限发挥,只能做有边界的小改动;新版本还要和旧版本放到保留任务上比较,只有表现更好,才会进入 best_skill.md 。
那些没通过验证的修改也不会完全丢掉,而是被记下来,提醒系统不要反复尝试类似的坏主意。
这些机制合起来,其实指向一个判断:不是每次反思都值得写进记忆。一个会学习的文本系统,先要懂得拒绝那些看起来聪明、实际会带来回归的修改。SkillOpt 比普通“让模型反思一下”多出来的东西,也正在这里。它不是只生成一段更长的说明,而是让这段说明接受试跑、修改、验证、拒绝和保留。
表格任务能比较直观地说明这一点。agent 算错结果,不一定是不会计算,也可能是没先确认表头、没检查公式范围、没复算关键值、没把输出和原表结构对齐。放到 SpreadsheetBench 这样的任务里,一份 skill 更像表格任务 SOP ,而不是某个神秘能力本身。代码任务也类似,失败常常不是不会写代码,而是没读项目约定、跑错测试、忽略 migration 或 fixture 。人类 reviewer 会把这些经验写在评论、文档或脑子里。SkillOpt 想做的,是让这些经验进入 skill 文件,同时用验证挡住那些“文字上合理、实际会降低表现”的修改。
这也是它和 prompt optimization 最难分开的地方。如果 skill 只是几段 Markdown 任务说明,它和 prompt 的距离确实很近。如果一个 benchmark 对应一份 skill ,并在固定任务分布上反复优化,最后得到的文本也可能更像 benchmark-specific 的小抄,而不是稳定可迁移的技能。真实团队里的 skill 通常更厚,里面有工具权限、异常恢复、上下文判断、组织流程、历史问题和跨任务迁移。论文里的轻量形态适合展示机制,但还没有覆盖完整的 skill learning 。
所以 SkillOpt 不应该被说成“agent 已经能自动学会技能”。它没有让 agent 自主发现新工具,也没有让 agent 理解真实组织边界。它做的是一件更窄的事:在预先定义好的任务、评分器和验证环境里,迭代一份操作说明。理论上,它延续了 TextGrad 和 prompt optimization 的文本反馈路线,并不算全新;工程上,它把一个更具体的问题摆到了台面上:失败经验如何进入 skill ,哪些修改应该保留,哪些修改应该被挡住。
也许 agent 的一部分能力,最后不会只藏在模型深处,而会写在那些看起来普通的技能文件里:几条规则,几个例外,一些失败之后留下的警告。过去我们把它们当文档维护; SkillOpt 暗示了另一种看法。它们也许是一种可训练的行为记忆。真正困难的不是让模型改几句话,而是让这些话在一次次成功和失败之后,变得更可靠,而不是更自信。SkillOpt 离完整的 skill learning 还很远,但它至少比单纯调 prompt 多问了一步:哪些经验有资格留下来?让失败不白白发生,本身就是 agent 工程里一个值得认真对待的问题。
但我把论文和项目材料看完以后,觉得这个问题不能只停在“是不是换壳”上。SkillOpt 最有意思的地方,不是它包装了一个新名词,而是它碰到了一个日常使用 agent 时很难绕开的问题:同一种错误出现第二次、第三次之后,agent 到底有没有学到什么?
我们最常见的处理方式仍然是人工纠正。比如这次发现 agent 改代码前没有看现有测试,于是在对话里提醒它,下次先读测试再动手。模型也可能在复盘里总结几条“以后需要更谨慎”的原则,但这段复盘随着上下文结束就失效了。更多时候,经验会被临时补进 prompt 或 skill 文件:加一句“检查边界条件”,再加一句“输出前确认结果”。短期看,问题像是被修掉了;换个任务之后,又可能在别处出现新的副作用。
这类是一类难以复用的经验。模型权重通常不可控,代码有测试和版本管理,但夹在中间的操作经验往往比较松散。客服 agent 遇到退款、发票、合规边界时,要知道先查什么、什么时候拒答、什么时候转人工;代码 agent 在 monorepo 里修改模块时,要知道该看哪些文件、跑哪组测试、避开哪些生成文件;数据分析 agent 面对 DAU 、收入、退款口径时,要知道如何核对定义。这些问题不完全是模型是否“知道答案”,更多是执行方法有没有被稳定保存。
讲到这里,SkillOpt 的问题就不能只问“是不是新算法”了。如果它的全部新意只是让模型改一份 Markdown ,那确实不够有说服力。但如果它在处理的是这些松散经验如何留下、如何验证、如何避免越改越坏,事情就没那么简单。要理解它为什么会长成现在这样,需要先回到更早的 TextGrad 。
TextGrad 其实回答了一个更宽的问题:如果 AI 系统里很多关键部件本来就是文本,能不能像调代码一样调它们?它的回答是可以。prompt 、答案、代码片段、工具说明、执行计划,都可以被看成等待修改的对象;反馈不一定是数值,也可以是一段批注、一次测试失败、一个评分器结果。所谓 textual gradient ,翻译成人话就是:告诉你哪里错了,为什么错,下一轮大概该往哪边改。
从使用者的角度看,这件事并不神秘。我们平时调 agent ,也是在先跑一遍,看错在哪里,再改 prompt 、改工具说明、改检查步骤。TextGrad 做的,是把这种调试动作整理成一个可反复执行的循环:运行、评价、修改、再运行。它比普通 prompt optimization 更宽,因为被改的不只是 prompt ;但它也没有真正回答另一个问题:哪些修改应该长期留下来? SkillOpt 接过的正是这个问题,只是它没有继续优化万物,而是把目标收窄到一份 agent 下次还会读取的 skill 文档。
这个收窄很关键。因为在 agent 系统里,skill 不只是一次性的提示词,它更像一本操作手册:哪些检查要先做,哪些工具什么时候用,哪些错误以前出现过,什么情况下不能贸然继续。普通 prompt optimization 更关心“怎么把这段话写得更容易让模型答对”,SkillOpt 关心的则更像“怎么让这份操作手册在一批任务之后变得更可靠”。
但操作手册也会腐烂。一次没查文件,不能自动总结成“每次任务都扫描全仓库”;一次测试漏跑,也不等于以后都要跑全量测试;一次指标口径错了,也不意味着应该往 skill 里塞一大段适用于所有场景的长规则。很多 prompt 或 skill 后来越来越重,就是因为它只会增加经验,不会判断哪些经验不该留下。
SkillOpt 的循环,真正处理的是这个问题。它先让 agent 带着当前 skill 去做任务,看它在哪些地方失败;失败之后让模型写一段反思,把问题压缩成对 skill 的修改建议;修改不能无限发挥,只能做有边界的小改动;新版本还要和旧版本放到保留任务上比较,只有表现更好,才会进入 best_skill.md 。
那些没通过验证的修改也不会完全丢掉,而是被记下来,提醒系统不要反复尝试类似的坏主意。
这些机制合起来,其实指向一个判断:不是每次反思都值得写进记忆。一个会学习的文本系统,先要懂得拒绝那些看起来聪明、实际会带来回归的修改。SkillOpt 比普通“让模型反思一下”多出来的东西,也正在这里。它不是只生成一段更长的说明,而是让这段说明接受试跑、修改、验证、拒绝和保留。
表格任务能比较直观地说明这一点。agent 算错结果,不一定是不会计算,也可能是没先确认表头、没检查公式范围、没复算关键值、没把输出和原表结构对齐。放到 SpreadsheetBench 这样的任务里,一份 skill 更像表格任务 SOP ,而不是某个神秘能力本身。代码任务也类似,失败常常不是不会写代码,而是没读项目约定、跑错测试、忽略 migration 或 fixture 。人类 reviewer 会把这些经验写在评论、文档或脑子里。SkillOpt 想做的,是让这些经验进入 skill 文件,同时用验证挡住那些“文字上合理、实际会降低表现”的修改。
这也是它和 prompt optimization 最难分开的地方。如果 skill 只是几段 Markdown 任务说明,它和 prompt 的距离确实很近。如果一个 benchmark 对应一份 skill ,并在固定任务分布上反复优化,最后得到的文本也可能更像 benchmark-specific 的小抄,而不是稳定可迁移的技能。真实团队里的 skill 通常更厚,里面有工具权限、异常恢复、上下文判断、组织流程、历史问题和跨任务迁移。论文里的轻量形态适合展示机制,但还没有覆盖完整的 skill learning 。
所以 SkillOpt 不应该被说成“agent 已经能自动学会技能”。它没有让 agent 自主发现新工具,也没有让 agent 理解真实组织边界。它做的是一件更窄的事:在预先定义好的任务、评分器和验证环境里,迭代一份操作说明。理论上,它延续了 TextGrad 和 prompt optimization 的文本反馈路线,并不算全新;工程上,它把一个更具体的问题摆到了台面上:失败经验如何进入 skill ,哪些修改应该保留,哪些修改应该被挡住。
也许 agent 的一部分能力,最后不会只藏在模型深处,而会写在那些看起来普通的技能文件里:几条规则,几个例外,一些失败之后留下的警告。过去我们把它们当文档维护; SkillOpt 暗示了另一种看法。它们也许是一种可训练的行为记忆。真正困难的不是让模型改几句话,而是让这些话在一次次成功和失败之后,变得更可靠,而不是更自信。SkillOpt 离完整的 skill learning 还很远,但它至少比单纯调 prompt 多问了一步:哪些经验有资格留下来?让失败不白白发生,本身就是 agent 工程里一个值得认真对待的问题。