返回博客列表

SkillOpt:像训练神经网络一样训练你的 Agent Skill

2026-07-16T20:50:00+08:00
SkillOptAgentPrompt EngineeringMicrosoftLLM

SkillOpt:像训练神经网络一样训练你的 Agent Skill

写了 100 个 prompt,改了 50 次,还是不知道哪个版本最好。这是你吗?

Prompt engineering 有一个残酷的现实:改了一行,修好了一个 case,可能破坏了另外三个。 你没法做回归测试——因为 Agent 的行为不是确定性的。你没法做 A/B 测试——因为手工改 prompt 太慢了。你甚至不知道你的改法到底有没有用——因为没有一个自动化的评测循环。

微软研究院最近开源了 SkillOpt,它用一种极具创意的方式解决了这个问题:把深度学习训练范式搬到文本空间。

不再手工调 prompt。而是跑 rollout、做 reflection、生成有界编辑、用验证集做门控——整个过程就像训练一个神经网络,只不过"权重"是一段几百 token 的 Markdown 文档。

本文提纲

  1. 什么是 SkillOpt:把 skill 当作可训练状态
  2. 核心训练循环:四步走,每一步都在模仿梯度下降
  3. 文本学习率、拒绝缓冲区、验证门控——这些设计为什么关键
  4. SkillOpt-Sleep:让 Agent 在夜里自己进化 (v0.2.0)
  5. 实际用法:从安装到产出 best_skill.md
  6. 迁移能力:一份 skill 跨模型、跨框架通用
  7. 和手工调 prompt 的差距有多大

什么是 SkillOpt:把 skill 当作可训练状态

传统上,我们优化 Agent 有两种方式:

  1. 微调模型:改权重,贵、慢、需要大量数据、不可解释。
  2. 手工调 prompt:便宜、快、可解释,但不系统、不可复现、容易退化。

SkillOpt 走了第三条路:把 skill 文档当作可训练的外部状态,用独立优化器模型来自动改进它。

graph LR
    subgraph "Frozen Target Model"
        T["GPT / Qwen / Claude
权重完全冻结"] end subgraph "Optimizer Model" O["GPT-5.5
分析 rollout,生成文本编辑"] end subgraph "Trainable Skill" S["best_skill.md
300-2000 tokens
唯一的可训练状态"] end T -->|"执行 rollout"| R["Scored Trajectories
成功 + 失败"] R -->|"minibatch"| O O -->|"add/delete/replace"| S S -->|"指导行为"| T style T fill:#4ECDC4,color:#000000 style O fill:#FF6B6B,color:#000000 style S fill:#FFD93D,color:#000000 style R fill:#6C5CE7,color:#fff

这个设计的核心洞察是:对于现代 LLM,一段精心优化的文本指令,其效果提升往往超过微调,而成本不到微调的千分之一。

SkillOpt 在 52 个评测单元格(7 个目标模型 × 6 个基准 × 多个执行框架)上全面碾压了最强基线——包括人类手写的 skill、LLM 直接生成的 skill、Trace2Skill、TextGrad 和 GEPA。平均提升幅度在 +9% 到 +25% 之间。

核心训练循环:四步走

SkillOpt 的训练循环刻意模仿了深度学习优化器。每一步都能在梯度下降里找到对应:

深度学习 SkillOpt
Forward pass Rollout:冻结模型用当前 skill 执行任务
Backward pass Reflect:优化器分析成功/失败轨迹
Gradient update Edit:对 skill 文档做有界增/删/改
Validation Gate:只在 held-out 验证集上提升才接受

来看每一步的具体实现。

Step 1: Rollout — 用当前 skill 跑一批任务

from skillopt.engine import SkillOptTrainer

trainer = SkillOptTrainer(
    target_model="gpt-5.5",        # 冻结的目标模型
    optimizer_model="gpt-5.5",     # 优化器模型(可以更强,也可以同级别)
    env="searchqa",                 # 基准环境
    skill_path="initial_skill.md",  # 初始技能文档
)

# 跑一批 rollout,每个任务返回 scored trajectory
results = trainer.rollout(batch_size=8)
# results = [
#   {"task_id": "q1", "score": 0.75, "trajectory": [...], "verdict": "correct"},
#   {"task_id": "q2", "score": 0.0,  "trajectory": [...], "verdict": "wrong_answer"},
#   ...
# ]

Rollout 收集的不仅是分数,还有完整轨迹——包括每一步的消息、工具调用、验证器反馈和任务元数据。这为后续的反思提供了充分的上下文。

Step 2: Reflect — 从失败中提取可复用的规则

这是整个系统最精妙的部分。优化器不会笼统地说"做得不好,改改",而是将失败和成功分开分析:

# SkillOpt 内部的 reflect 逻辑(简化版)
def reflect(successes, failures, current_skill):
    """优化器分析轨迹,输出结构化反思"""
    
    # 分别分析成功和失败
    failure_patterns = optimizer.analyze(
        failures,
        prompt="""For each failure, identify:
        1. What went wrong (be specific)
        2. Is this a SKILL_DEFECT (skill instruction missing/wrong)?
        3. Is this an EXECUTION_LAPSE (model didn't follow instruction)?
        Only flag SKILL_DEFECT items for editing."""
    )
    
    success_patterns = optimizer.analyze(
        successes,
        prompt="Identify what the current skill does RIGHT — 
        these parts should be PRESERVED during editing."
    )
    
    return {
        "edit_candidates": failure_patterns["skill_defects"],
        "preserve": success_patterns["key_behaviors"],
        "appendix_notes": failure_patterns["execution_lapses"],
    }

这里有一个关键分类:SKILL_DEFECT vs EXECUTION_LAPSE。

  • SKILL_DEFECT:skill 本身有缺陷——缺了一条规则、描述不够清晰、顺序有问题。这类问题应该通过修改 skill body 来解决。
  • EXECUTION_LAPSE:skill 没问题,模型没执行到位——这次没调用工具、忘了验证、输出格式错了。这类问题记入 appendix(附录),不修改 body,因为改 body 可能破坏已经运作良好的行为。

这个分类机制是 SkillOpt 不会"改坏"已有能力的核心原因。

Step 3: Edit — 有界地修改 skill

优化器生成候选编辑,但这里的编辑不是随便改——有严格的约束:

# 编辑预算 = 文本学习率
edit_budget = {
    "max_add_lines": 4,       # 最多添加 4 行
    "max_delete_lines": 2,    # 最多删除 2 行
    "max_replace_blocks": 3,  # 最多替换 3 个块
    "protected_sections": [   # 受保护区域——不可编辑
        "slow_update",         # 慢更新记忆区
        "appendix",            # 附录(执行问题记录)
    ]
}

# 编辑操作示例
candidate_edits = [
    {"op": "add", "after_line": 12, "text": "4. If the answer involves math, verify with code execution."},
    {"op": "replace", "lines": "18-20", "text": "When searching, try at most 3 different query phrasings before giving up."},
    {"op": "delete", "lines": "7-8"},  # 删除一条导致过度谨慎的规则
]

文本学习率是一个极具创意的设计。它限制每次 epoch 的文本修改量,防止优化器大段重写——就像梯度下降里的 learning rate 防止权重剧烈震荡。论文的消融实验证实:去掉学习率限制,性能从 87.1% 掉到 84.6%。

Step 4: Gate — 严格验证才接受

候选编辑不是直接应用的。它必须在 held-out 验证集上通过门控:

def validation_gate(candidate_skill, current_best_skill, val_set):
    """只有验证集分数提升才接受新 skill"""
    
    current_score = evaluate(current_best_skill, val_set)
    candidate_score = evaluate(candidate_skill, val_set)
    
    if candidate_score > current_score:
        # 通过!更新当前 skill
        rejected_buffer.append({
            "accepted": True,
            "old_score": current_score,
            "new_score": candidate_score,
        })
        return candidate_skill
    else:
        # 拒绝!放入缓冲区供后续反思
        rejected_buffer.append({
            "accepted": False,
            "reason": f"Val score {candidate_score} <= {current_score}",
            "edits": candidate_edits,
        })
        return current_best_skill  # 保持不变

门控机制防止了"自我编辑幻觉"——优化器觉得自己改得很好,但实际测试分数掉了。被拒绝的编辑不会浪费,它们进入拒绝缓冲区,在后续 epoch 中作为负反馈,帮助优化器避免重复有害的编辑方向。

消融实验显示,去掉缓冲区后 SpreadSheet 分数从 77.5% 掉到 72.9%。

这三个设计的协同效应

文本学习率、拒绝缓冲区、验证门控——单独看每个都很直观,但它们的组合产生了一个涌现效应:

graph TB
    A["Rollout
收集成功/失败轨迹"] --> B["Reflect
分类 SKILL_DEFECT vs EXECUTION_LAPSE"] B --> C["Edit
文本学习率限制修改幅度"] C --> D["Gate
验证集严格门控"] D -->|"通过"| E["更新 skill
拒绝缓冲区记录成功"] D -->|"拒绝"| F["保持旧 skill
拒绝缓冲区记录失败方向"] F -->|"下一 epoch 避坑"| B style A fill:#4ECDC4,color:#000000 style B fill:#45B7D1,color:#000000 style C fill:#FFD93D,color:#000000 style D fill:#FF6B6B,color:#000000 style E fill:#96CEB4,color:#000000 style F fill:#E17055,color:#000000

这形成了一个带负反馈的自矫正优化系统——就像 Adam 优化器里的动量 + 自适应学习率,只不过操作对象是自然语言文本而非浮点数矩阵。

SkillOpt-Sleep:让 Agent 在夜里自己进化

v0.2.0 引入的 SkillOpt-Sleep 是一个真正的"重磅炸弹"。它让 Agent 在日常使用中积累经验,然后在夜间自动进行自我进化。

工作流程:Harvest → Mine → Replay → Consolidate

# SkillOpt-Sleep 的夜间循环(每天凌晨自动运行)
# 等价于: skillopt-sleep --env my_project --backend codex

# Phase 1: HARVEST — 收集白天的真实 session 数据
# 从 Codex/Claude Code/Copilot 的 session 历史中提取轨迹
sessions = harvest_sessions(
    source="codex",          # 从 Codex 历史中收集
    lookback_hours=24,       # 过去 24 小时的对话
    filter_min_turns=3,      # 至少 3 轮对话才算有效
)

# Phase 2: MINE — 从 session 中挖掘重复任务
tasks = mine_recurring_tasks(
    sessions,
    min_occurrences=3,       # 出现 3 次以上才算重复任务
    similarity_threshold=0.7,
)
# 输出: ["处理 CSV 文件导入", "API 文档生成", "数据库迁移脚本"]

# Phase 3: REPLAY — 离线重放任务,用当前 skill 重新执行
replay_results = replay_tasks(
    tasks,
    target_model="gpt-5.4-mini",
    current_skill=load_skill("best_skill.md"),
    num_rollouts=5,          # 每个任务跑 5 次看稳定性
)

# Phase 4: CONSOLIDATE — 将短期经验固化为长期 skill
new_skill = consolidate(
    replay_results,
    current_skill=load_skill("best_skill.md"),
    gate_mode="on",          # 必须通过验证门控
    update_memory=True,      # 更新慢更新记忆区
)

Sleep 引擎最巧妙的设计是 handoff 模式——如果你不想让它自动调用 API,它可以把所有需要模型推理的步骤写到一个 PROMPTS.md 文件里,退出(exit code 3),等你手动(或让 Claude Code sub-agent)回答后,重新运行就从断点继续。

# 第一次运行 — 生成待回答的 prompt
$ skillopt-sleep --backend handoff --env my_project
# 输出: PROMPTS.md + pending.json, 退出码 3

# 你用 Claude Code 回答完所有问题后
$ skillopt-sleep --backend handoff --env my_project
# 读取 answers/*.md, 继续执行 replay + consolidate

这个设计意味着 SkillOpt-Sleep 可以集成到你现有的任何 Agent 工作流里,不依赖特定的 API 或模型。

实际用法:从零开始优化一个 skill

假设你有一个 TheAIEra 项目的 tech-article-writer skill,你想优化它。这是完整的操作流程:

1. 安装

pip install skillopt
# 如果需要 Sleep 引擎
pip install skillopt[webui]

2. 准备你的 skill 和环境

# 目录结构
my_project/
├── initial_skill.md        # 你的初始 skill 文档
├── train_tasks.jsonl       # 训练集(50-100 个任务)
├── val_tasks.jsonl         # 验证集(20-30 个任务)
└── config.yaml             # SkillOpt 配置

config.yaml 的关键配置:

# SkillOpt 配置文件
target:
  model: "gpt-5.4-mini"     # 你要优化的目标 Agent 模型
  backend: "openai_chat"    # 后端类型

optimizer:
  model: "gpt-5.5"          # 优化器模型(建议比目标模型强)
  backend: "openai_chat"
  use_skill_aware_reflection: true  # 开启 skill-aware 反思

training:
  epochs: 10
  batch_size: 8
  learning_rate: 4           # 文本学习率(编辑预算)
  max_attempts_per_task: 3

gate:
  mode: "on"                 # 必须开启验证门控
  val_split: 0.2             # 20% 数据作为验证集

3. 启动训练

from skillopt.engine import SkillOptTrainer

trainer = SkillOptTrainer.from_config("config.yaml")

# 开始训练 — 就像训练一个神经网络
for epoch in range(trainer.config.training.epochs):
    # 自动执行 rollout → reflect → edit → gate
    trainer.run_epoch()
    
    print(f"Epoch {epoch}: "
          f"train_score={trainer.metrics['train_score']:.1%}, "
          f"val_score={trainer.metrics['val_score']:.1%}, "
          f"skill_size={len(trainer.current_skill)} tokens")

# 导出最优 skill
trainer.export("best_skill.md")
print(f"Best skill exported: {trainer.best_val_score:.1%} on validation set")

训练过程中,你可以通过 WebUI 实时监控:

skillopt-webui --port 7860

WebUI 会展示每个 epoch 的训练/验证分数曲线、当前 skill 的内容、每个候选编辑的接受/拒绝状态。

4. 部署

# 部署时只需要 best_skill.md
best_skill = open("best_skill.md").read()

# 直接用优化后的 skill 指导你的 Agent
agent = YourAgent(
    model="gpt-5.4-mini",
    skill=best_skill,  # 300-2000 tokens, 零额外推理开销
)

部署时,目标模型只消耗 best_skill.md——不存储拒绝缓冲区、不存储优化器状态、不增加任何推理调用。这就是"训练免费,部署零成本"的含义。

迁移能力:一份 skill 横扫多个模型

SkillOpt 产出的 skill 有一个令人惊讶的特性:跨模型迁移。 论文测试了三种迁移场景:

迁移类型 结果 示例
跨模型大小 +15.2% GPT-5.4 上优化的 LiveMath skill → GPT-5.4-nano
跨执行框架 +31.8% Codex 上优化的 SpreadSheet skill → Claude Code
自优化 +10.4% GPT-5.4-nano 既当目标模型又当优化器

这意味着:

  • 你可以在强模型(GPT-5.5)上优化 skill,部署到弱模型(GPT-5.4-nano)上
  • 你可以在 Codex CLI 上优化 skill,部署到 Claude Code 上
  • 即使只能用同一个模型做优化(没有更强的模型),也能获得 +10% 的提升

这背后的原理是:SkillOpt 优化的不是模型能力,而是执行过程的程序性知识——"先做什么、再做什么、怎么验证、怎么纠错"。这些规则与模型本身的能力正交,所以能跨模型、跨框架迁移。

和手工调 prompt 的差距,用数字说话

SkillOpt 论文在 6 个基准上对比了所有主流方法:

方法 SearchQA SpreadSheet OfficeQA DocVQA LiveMath ALFWorld
无 skill 70.4 12.3 13.4 76.7 13.6 72.4
人类手写 75.2 18.5 21.3 79.8 19.7 75.1
LLM 直接生成 73.1 15.7 17.2 78.2 16.8 73.8
Trace2Skill 76.8 35.6 41.8 85.3 28.4 72.1
TextGrad 77.2 42.3 45.1 86.2 30.5 73.5
GEPA 78.1 47.3 48.3 87.4 33.7 75.4
SkillOpt 80.0 51.7 52.4 89.1 42.9 84.3

几个值得注意的点:

  1. 人类手写只比无 skill 好一点点。 在 SpreadSheet 上,人类手写(18.5)仅比裸跑(12.3)高 6 分。说明即便是经验丰富的 prompt engineer,手工优化 agent skill 的效率也很低。

  2. Trace2Skill 已经很强了。 它从执行轨迹中提取 skill,比人类手写平均高 10-20 分。但 SkillOpt 在此基础上又高 3-15 分——差距来自迭代优化和验证门控。

  3. SkillOpt 在 SpreadSheet 上比其他方法高出一大截。 51.7 vs 第二名的 47.3(GEPA)。这是因为 SpreadSheet 任务最需要程序性知识——你先看哪个 sheet、怎么定位数据、用什么函数计算——这些恰恰是 SkillOpt 最擅长优化的。

参考文档与链接

你的 Agent skill 有多久没系统性地优化过了?试试跑一晚上 SkillOpt,看看它能帮你的 Agent 涨多少分。评论区聊聊你最想优化的 skill 是什么。


作者: itech001
来源: 公众号:AI人工智能时代
网站: https://www.theaiera.cn/
每日分享最前沿的 AI 新闻资讯和技术研究。

关注公众号,获取更多 AI 技术干货!

分享给朋友