AI Skill 很容易被误解成一段高级 prompt:写一个 SKILL.md,放几份参考资料,再加几个脚本,大语言模型就能按要求完成某类任务。
能跑起来确实不难,难的是稳定、可控、可评测。
一个真正可用的 Skill 通常包含这些东西:
my-skill/
├── SKILL.md # 能力说明、触发条件、执行协议
├── references/ # 知识库、规范、示例
├── scripts/ # 可执行脚本、校验工具、辅助程序
├── evals/ # GT 测试集、评测脚本
└── tests/ # 回归测试、端到端测试
这已经不是单纯的 prompt,而更像一个 harness:它把模型、规则、工具、数据、执行路径和安全边界组织在一起。只要其中一个环节出问题,Skill 的行为就会偏离预期。
典型问题有三类。
| 问题 | 例子 | 根因 |
|---|---|---|
| 稳定性问题 | 加了“git 状态不干净就拒绝执行”的规则,但新目录还没 git init,直接失败 | 测试环境覆盖不到真实使用环境 |
| 边界问题 | iteration-10 按字符串排序排在 iteration-2 前面,清理逻辑误删新结果 | 边界条件没有进入测试集 |
| 规则冲突 | 安全规则保护了 A 场景,却把协议允许的 B 场景封死 | 规则数量增加后,交互关系变复杂 |
更大的问题不是“会不会崩”,而是“是否匹配真实数据分布”。
比如用户问:
员工离职后邮箱还在不在?
Skill 可能把问题路由到“邮箱”分类,但正确答案其实藏在“通讯录 / 删除或禁用成员”章节。这个错误不是语法错误,也不是程序崩溃,而是行为没有贴合业务数据。
所以 Skill 优化的目标不该只是“规则写得更完整”,而应该更像训练模型:给它数据、指标和反馈,让它不断逼近目标行为。
Skill 可以被当成训练对象
训练机器学习模型时,我们不会只看代码能不能运行,而是看 loss 是否下降、验证集是否变好、回归集是否稳定。Skill 也可以用类似方式管理。
| 机器学习训练 | Skill 训练 |
|---|---|
| 参数 | SKILL.md、references、scripts、触发规则 |
| 训练数据 | GT case,也就是标准输入和期望输出 |
| loss / metric | pass rate、recall、fact coverage、路径命中率 |
| dev 集 | 迭代中可见的评测数据 |
| holdout 集 | 迭代中不可见,用来检查是否过拟合 |
| checkpoint | 每轮修改后的 Git commit |
| rollback | 指标不合格就 git revert |
| optimizer | LLM Agent 提出修改方案 |
| trace | 每个 case 的完整执行记录 |
GT(Ground Truth,标准答案)是整个系统的地基。它描述“什么输入应该得到什么结果”,例如:
id: case-041
input: "员工离职后邮箱还在不在?"
expected:
route_should_hit:
- "通讯录/删除或禁用成员.md"
answer_should_contain:
- "删除成员"
- "禁用成员"
forbidden_paths:
- "邮箱/账号续期.md"
assertions:
- type: path_hit
- type: contains
- type: not_contains
没有 GT,就只能靠人工手感判断 Skill 好不好;有了 GT,优化才有方向。
一个可训练的 Skill 系统需要四个核心机制:
- 8 阶段 Loop:规定每一轮怎么观察、思考、修改、验证和回滚。
- 三层评测:用不同成本的测试逐层过滤坏改动。
- 5 维 AND 门控:只有所有质量条件都满足,修改才被保留。
- Trace 诊断:失败时不只看分数,而是查看完整执行轨迹。
整体结构可以表示为:
flowchart TD
A[GT 数据集] --> B[Skill Evolver]
C[当前 Skill<br/>SKILL.md / references / scripts] --> B
B --> D[提出一个原子修改]
D --> E[Git Commit]
E --> F[三层评测]
F --> G{5 维 AND 门控}
G -- 全部通过 --> H[保留 checkpoint]
G -- 任一失败 --> I[Git Revert]
H --> J[记录结果与 Trace]
I --> J
J --> K{继续 / 升层 / 停止}
K -- 继续 --> B
K -- 停止 --> L[输出最终 Skill 和报告]
8 阶段 Loop:每轮只做一个可验证修改
Skill Evolver 的迭代不是让 Agent 随便重写一大段文件,而是把每一轮拆成固定流程。Phase 0 是一次性初始化,后面每轮走 Phase 1 到 Phase 8。
Phase 0: Setup 创建 workspace、生成评测计划、建立 baseline
Phase 1: Review 读取历史结果、失败 case、Git 记录和 Trace
Phase 2: Ideate 基于 Trace 诊断失败原因,提出一个原子修改
Phase 3: Modify 执行修改,每轮只改一个点
Phase 4: Commit 先提交到 Git,留下审计轨迹
Phase 5: Verify 运行三层评测流水线
Phase 6: Gate 通过 5 维 AND 门控决定保留还是回滚
Phase 7: Log 写入结果、实验记录和 per-case Trace
Phase 8: Loop 决定继续、升层、切换策略或停止
Phase 0:初始化不是建目录,而是生成计划
Setup 阶段要做几件事:
- 检查
SKILL.md是否存在。 - 检查工作区是否可被 Git 管理。
- 加载 GT 数据。
- 建立 baseline 分数。
- 生成
evolve_plan.md。
evolve_plan.md 应该明确这些内容:
# evolve_plan.md
## Objective
提升客服问答 Skill 的 S1 路径召回率,同时把候选路径数量从 10 降到 6。
## Metric
- primary: strict_full_recall
- secondary: avg_candidate_count
- regression: existing_cases_pass_rate
## Gate Threshold
- dev pass_rate 不下降
- holdout pass_rate 不下降
- avg_candidate_count <= 6.5
- critical safety violation = 0
## Mutation Layers
- Layer 1: trigger words, root index, routing hints
- Layer 2: SKILL.md protocol and examples
- Layer 3: scripts and references
这份计划相当于训练配置,后面的修改、评测和门控都围绕它执行。
Phase 1:从历史中提取下一轮信号
每轮开始前,Agent 不能只看当前文件,还要读取最近的实验记忆:
- 最近 20 条 git log
- 最近 20 行 results.tsv
- 最近 10 条 experiments.jsonl
- 上一轮失败 case 的 Trace 路径
- 当前 mutation layer 的状态
它要从这些记录里提取五类信号:
| 信号 | 用途 |
|---|---|
| 哪些改法成功过 | 复用有效模式 |
| 哪些改法失败过 | 避免重复试错 |
| 哪些 case 持续失败 | 优先处理顽固问题 |
| 哪些 case 容易回归 | 加强回归守卫 |
| 是否已经卡住 | 决定是否升层或切换激进策略 |
如果没有实验记忆,Agent 很容易反复尝试同一种无效改法。experiments.jsonl 的价值就在于让系统记住“什么试过了、结果如何、为什么失败”。
一个实验记录可以长这样:
{
"iteration": 12,
"layer": 2,
"change": "adjust git initialization protocol for non-git skill directories",
"evidence": ["traces/dev/case-007.json", "traces/dev/case-019.json"],
"dev_pass_rate_before": 0.91,
"dev_pass_rate_after": 0.96,
"gate": "keep",
"reason": "fixed non-git workspace failure without regression"
}
Phase 2:没有 Trace 证据就不允许修改
Ideate 阶段不允许“感觉这里可以优化”。每个修改建议都要遵守反事实诊断格式:
Case X 失败,因为执行轨迹中出现了 Y。
如果修改 Z,预期输出会变成 W。
这个修改只影响 Layer N 的一个文件,不会改变其他路径。
例如:
Case 41 失败,因为 Trace 显示路由器把“员工离职后邮箱还在不在”归入邮箱分类。
GT 要求命中“通讯录/删除或禁用成员.md”。
如果在 root_index.md 中加入“离职、禁用、删除成员优先查通讯录”的易混淆提示,
该 case 的候选路径会包含正确文档,同时不会影响普通邮箱续费问题。
这种约束能减少盲改。Agent 的职责不是随机生成补丁,而是基于证据提出最小修复。
Phase 3:每轮只做一个原子修改
原子修改的判断很简单:如果一句话描述里出现“和”,大概率应该拆成两轮。
| 不合格修改 | 合格拆分 |
|---|---|
| 修改路由规则和清理旧脚本 | 第 1 轮只改路由规则;第 2 轮只清理脚本 |
调整 SKILL.md 协议并重构工具目录 | 第 1 轮改协议;第 2 轮重构目录 |
| 增加安全扫描和优化召回排序 | 第 1 轮增加安全扫描;第 2 轮优化排序 |
可以用 git diff --stat 做一个粗略检查:
git diff --stat
# 如果一次改动涉及过多文件,通常说明它不是原子修改
原子性很关键。一次只改一个点,评测结果才容易归因;一次改十个点,即使分数涨了,也不知道是哪一处带来的收益。
Phase 4:先 Commit,再验证
修改完成后先提交:
git add .
git commit -m "iter-012 fix non-git workspace initialization"
再进入评测。这样做有两个好处:
- 每轮都有明确 checkpoint。
- 门控失败时可以直接回滚。
git revert --no-edit HEAD
Git 在这里不只是版本管理工具,也是实验记忆和安全网。
Phase 8:继续、升层或停止
循环不能无限跑,需要有停止条件和升层策略。
flowchart TD
A[完成一轮评测] --> B{Gate 通过?}
B -- 是 --> C[保留修改<br/>重置连续失败计数]
B -- 否 --> D[回滚修改<br/>连续失败 +1]
C --> E{达到目标?}
E -- 是 --> F[停止并输出报告]
E -- 否 --> G[继续当前层]
D --> H{连续 K 轮无 keep?}
H -- 是 --> I[当前层 exhausted<br/>升到下一层]
H -- 否 --> J{连续 5 次 discard?}
J -- 是 --> K[切换激进策略]
J -- 否 --> G
I --> L{三层都已尝试?}
L -- 是 --> F
L -- 否 --> G
分层 mutation 可以避免一开始就大拆大改。推荐顺序是:
| 层级 | 修改对象 | 成本 | 适合解决的问题 |
|---|---|---|---|
| Layer 1 | 触发词、索引、路由提示 | 低 | 分类偏差、召回漏项、易混淆问题 |
| Layer 2 | SKILL.md 主协议、示例、约束 | 中 | 执行流程错误、规则冲突、输出格式不稳定 |
| Layer 3 | 脚本、references、工具代码 | 高 | 结构性缺陷、数据处理 bug、工程能力不足 |
三层评测:先用便宜测试挡掉坏修改
评测不能只跑一次大而全的 benchmark。LLM(Large Language Model,大语言模型)评测有成本,也有波动,所以更合理的方式是分层:越便宜的越早跑,越昂贵的越晚跑。
flowchart LR
A[候选修改] --> B[L1 快速门卫<br/>秒级]
B -- 失败 --> R1[Discard + Revert]
B -- 通过 --> C[L2 Dev Eval<br/>分钟级]
C -- 失败 --> R2[Discard + Revert]
C -- 通过 --> D{是否触发 L3?}
D -- 否 --> K[进入 Gate]
D -- 是 --> E[L3 Strict Eval<br/>holdout + regression]
E -- 失败 --> R3[Discard + Revert]
E -- 通过 --> K
L1:快速门卫
L1 只做程序性检查,不调用 LLM,目标是在秒级挡掉明显坏的修改。
检查项可以包括:
1. SKILL.md 是否存在,结构是否完整
2. quick_validate 是否通过
3. 安全扫描是否出现危险删除命令
4. 是否硬编码 API key、token、密钥
5. 是否硬编码本机绝对路径
6. 随机抽样 3 个 GT case,检查基本输出结构
安全规则要区分 critical 和 warning:
| 级别 | 例子 | 处理方式 |
|---|---|---|
| critical | rm -rf /、硬编码密钥 | 直接 discard |
| warning | 可疑临时文件、非关键格式问题 | 记录给下一轮诊断 |
L1 失败后不需要继续跑 L2,因为坏修改已经足够明确。
L2:Dev Eval
L2 跑全量 dev 集 GT case,并使用多种 assertion 评分。
常见 assertion 可以分成两类。
| 类型 | 示例 | 判定方式 |
|---|---|---|
| 程序可判定 | contains、not_contains、regex、json_schema、script_check、exit_code | 直接用代码判断 |
| 语义型 | path_hit、fact_coverage | 让 LLM 做 YES/NO 分类 |
示例:
assertions:
- type: contains
value: "删除成员"
- type: not_contains
value: "邮箱续费"
- type: path_hit
expected_path: "通讯录/删除或禁用成员.md"
- type: fact_coverage
facts:
- "离职后可通过删除或禁用成员处理账号"
- "邮箱状态与成员账号状态有关"
L2 的输出不该只有总分,还要保存 per-case 结果:
{
"case_id": "case-041",
"passed": false,
"score": 0.5,
"failed_assertions": ["path_hit", "fact_coverage"],
"trace_path": "traces/dev/iter-012/case-041.json"
}
这些记录会成为下一轮 Phase 1 和 Phase 2 的输入。
L3:Strict Eval
L3 成本最高,不必每轮都跑。典型触发条件有三种:
1. 每 N 轮自动触发一次
2. dev pass_rate 超过阈值
3. mutation layer 晋升之前
L3 主要检查两件事:
| 数据集 | 目的 |
|---|---|
| holdout 集 | 优化器没有见过,用来判断是否过拟合 dev 集 |
| regression 集 | 确保老问题没有被新修改重新打坏 |
如果需要比较新旧 Skill,还可以做 blind A/B 对比:隐藏版本信息,让 evaluator 判断哪个输出更符合 GT。
5 维 AND 门控:任何一项不合格都回滚
很多优化系统喜欢用加权总分,例如质量涨 10 分、成本扣 3 分,最后总分仍然通过。Skill 训练不适合这么做,因为某些维度不能被抵消。
质量涨了,但引入安全风险,不能保留。
召回涨了,但 token 消耗翻倍,也不一定能接受。
dev 集涨了,但 holdout 集掉了,可能是过拟合。
AND 门控更适合这种场景:五个条件全部为 YES,修改才保留。
| 维度 | 问题 | 不通过时的含义 |
|---|---|---|
| Correctness | 主指标是否变好或至少不下降? | 修改没有带来质量收益 |
| Regression | 旧 case 是否保持通过? | 新改动破坏已有能力 |
| Safety | 是否没有 critical 安全问题? | 存在危险命令、密钥泄露等风险 |
| Cost | token、时间、候选数等成本是否在预算内? | 质量提升靠不可接受的成本换来 |
| Scope | 是否符合当前 mutation layer 和原子性要求? | 改动范围失控,无法归因 |
门控逻辑可以写成非常直白的代码:
def gate(result):
checks = {
"correctness": result.dev_score >= result.baseline_dev_score,
"regression": result.regression_pass_rate >= result.baseline_regression_pass_rate,
"safety": result.critical_violations == 0,
"cost": result.avg_cost <= result.cost_budget,
"scope": result.is_atomic and result.layer_respected,
}
passed = all(checks.values())
return passed, checks
失败就回滚:
passed, checks = gate(eval_result)
if passed:
keep_checkpoint()
else:
run("git revert --no-edit HEAD")
mark_discard(checks)
关键点是:控制流交给程序,不交给 LLM。LLM 可以提出补丁、解释失败、生成候选方案,但是否保留必须由确定性的门控函数决定。
Trace 诊断:不要只给分数,要给现场记录
只告诉 Agent “case 41 没过,得分 0.5”,它只能猜问题在哪里。更好的方式是保存每个 case 的完整执行轨迹:
{
"case_id": "case-041",
"input": "员工离职后邮箱还在不在?",
"steps": [
{
"name": "route_query",
"output": {
"selected_category": "邮箱",
"candidate_paths": [
"邮箱/账号续费.md",
"邮箱/登录问题.md"
]
}
},
{
"name": "retrieve_reference",
"output": {
"top_docs": [
"邮箱/账号续费.md"
]
}
},
{
"name": "answer",
"output": "员工离职后邮箱是否保留取决于邮箱服务状态..."
}
],
"expected": {
"must_hit": "通讯录/删除或禁用成员.md"
},
"failed_assertions": ["path_hit", "fact_coverage"]
}
下一轮诊断时,不需要把所有 Trace 全塞进 prompt。更好的方式是给出索引:
Failed cases:
- case-041: traces/dev/iter-012/case-041.json
- case-052: traces/dev/iter-012/case-052.json
- case-077: traces/dev/iter-012/case-077.json
Agent 自己按需读取。这样既保留原始证据,又避免上下文被无关细节撑爆。
Trace 驱动的修改链路是:
sequenceDiagram
participant Eval as 评测器
participant Trace as Trace 文件
participant Agent as Proposer Agent
participant Skill as Skill 文件
participant Gate as 门控
Eval->>Trace: 写入每个 case 的执行轨迹
Agent->>Trace: 读取失败 case 的原始记录
Agent->>Agent: 做反事实诊断
Agent->>Skill: 提交一个原子修改
Eval->>Skill: 重新评测
Eval->>Gate: 提交指标、安全、成本、范围结果
Gate-->>Skill: Keep 或 Git Revert
这套机制的价值在于把“优化 Skill”从凭经验调 prompt,变成基于证据的实验过程。
自进化实验:让 Skill Evolver 优化自己
一个有意思的验证方式是让 Skill Evolver 改进它自己。此时 SKILL.md 同时扮演两个角色:
- 它是执行协议,告诉 Agent 如何演化 Skill。
- 它也是被修改对象,接受评测、门控和回滚。
这种设置会放大协议缺陷。只要流程里有模糊地带,执行过程中就会暴露出来。
一次自进化实验跑了 19 轮,结果如下:
| 指标 | 数值 | 含义 |
|---|---|---|
| 迭代轮数 | 19 | 包含 bug 修复、文档修正、安全加固、死代码清理和重构 |
| 被丢弃轮次 | 0 | 每轮都通过 5 维门控 |
| 崩溃次数 | 0 | 全流程没有中断 |
| 测试用例 | 17 → 31 | 迭代过程中自动补充 14 个新 case |
| 最终检查点 | 71/71 | 训练集、留出集、回归集全部通过 |
| 主文件行数 | 1411 → 557 | 大文件拆成 13 个单一职责文件 |
19 轮修改可以按类型拆开:
| 类型 | 轮次占比 | 典型内容 |
|---|---|---|
| 文档与协议修复 | 较高 | 补充边界说明、修正规则冲突 |
| 安全加固 | 中等 | 增加危险命令、密钥、路径扫描 |
| 代码修复 | 中等 | 修复 workspace、cleanup、Git 初始化等问题 |
| 死代码清理 | 较低 | 删除不再使用的逻辑 |
| 结构重构 | 较高 | 拆分大文件,建立单一职责模块 |
其中一个典型问题很能说明自进化的价值。
第 7 轮中,系统给自己加了 Git 安全检查:工作区不干净就拒绝执行。这个规则在已有测试里全部通过,因为测试目录都已经是 Git 仓库。
但真实用户可能在一个全新目录里创建 Skill,这个目录还没有 git init。如果直接执行“检查 Git 状态”,就会报错退出。
后续迭代中,系统通过 Trace 发现这个问题,并把初始化逻辑改成四条分支:
1. 已经是干净 Git 仓库 → 继续执行
2. 已经是 Git 仓库但有脏改动 → 报错,要求用户处理
3. 不是 Git 仓库 → 自动 git init 并创建 baseline commit
4. 系统没有安装 Git → 给出安装指令和失败原因
然后它在全新空目录上跑端到端测试,验证修复没有破坏原有路径。
这个例子说明了一个现实问题:人工测试往往覆盖熟悉路径,而自进化循环会不断制造新 regime。测试从不超过 9 轮时,iteration-10 排序问题不会出现;所有工作区都有 Git 时,非 Git 目录问题不会出现。循环跑得越久,越容易碰到人工没想到的边界。
真实业务场景:压缩候选数,同时保持召回
自进化只能证明系统能改自己,更重要的是能不能优化其他 Skill。
一个客服问答 Skill 的任务是从近千篇知识库文档中召回候选路径。初始状态下,手工调优后的召回率约为 96%,但每次会给 Stage 2 留下约 10 个候选路径,后续处理成本偏高。
新的目标是:
候选路径数量:约 10 个 → 约 6 个
核心指标:strict_full_recall 不能下降
GT 数量:91 条
压缩候选数后,召回率先掉到 86%,75 个 case 里大约有 10 个 miss。交给 Skill Evolver 迭代后,修复了 9 个 miss,最终只剩 1 个。
| 指标 | 优化前 | 压缩后初始 | 迭代后 |
|---|---|---|---|
| S1 路径召回率 | 约 96% | 86% | 98.67% |
| 标准题召回 | - | - | 100% |
| 难题召回 | - | - | 97.3% |
| 平均候选数 | 约 10 | 约 6 | 约 6 |
| Stage 2 压力 | 基线 | 下降 | 下降约 59% |
这里的关键不是“让 LLM 多想想”,而是把目标写成可评测指标:
objective:
primary_metric: strict_full_recall
constraints:
avg_candidate_count_max: 6.5
regression_pass_rate_min: 1.0
datasets:
dev: evals/dev.yaml
holdout: evals/holdout.yaml
regression: evals/regression.yaml
有了指标,Agent 才知道哪些修改该保留,哪些修改虽然看起来合理但必须回滚。
工程落地时要注意的坑
LLM 评测会有噪声
语义型 assertion 需要 LLM 判断,因此同一份输入可能出现波动。比如同一个 Skill、同一份 GT,跑多次 pass rate 可能在 0.79 到 0.92 之间漂移。
应对方式有几种:
| 方法 | 好处 | 代价 |
|---|---|---|
| 同一评测跑 3 次取均值 | 降低随机波动 | 成本约增加 3 倍 |
| 只让 LLM 做 YES/NO 分类 | 减少开放式评分漂移 | 表达能力受限 |
| 提高程序型 assertion 占比 | 结果稳定、便宜 | 需要更精细的 GT 设计 |
| 关键 case 进入 regression 集 | 防止反复被打坏 | 测试集维护成本增加 |
能用程序判断的地方尽量不用 LLM。LLM 更适合判断“事实是否覆盖”“路径是否语义命中”这类硬规则难写的问题。
GT 质量决定上限
如果 GT 本身有争议,Skill Evolver 会反复尝试但无法稳定修复。一个 case 连续多轮失败时,不一定是 Skill 有问题,也可能是标准答案不清楚。
可以给 case 增加状态:
id: case-048
input: "如何进入内部群?"
status: disputed
reason: "知识库中没有明确入口,不同业务线答案不一致"
usage: regression_guard_only
对于 disputed case,不要继续把它当成优化目标,否则 Agent 会为了通过它引入更奇怪的规则。更合理的做法是修 GT、拆 case,或者把它放进回归集做防护。
自动化不等于免费
每轮都要调用模型、运行评测、保存 Trace。尤其 L3 Strict Eval 和多次采样会带来明显成本。
实际使用时可以控制触发频率:
eval_policy:
l1: every_iteration
l2: every_iteration
l3:
every_n_iterations: 5
when_dev_pass_rate_above: 0.95
before_layer_promotion: true
前 3 到 5 轮也不建议完全放手。早期的 experiments.jsonl 还没有积累足够记忆,人工可以帮助它确定方向;一旦成功和失败样本积累起来,Agent 会更少重复无效尝试。
控制流必须写进代码
LLM 不适合当状态机控制器。它可能跳过验证、扩大修改范围、为了涨分硬编码 case,或者违反“不并行”的流程要求。
更稳的分工是:
| 组件 | 职责 |
|---|---|
| 程序 | 控制流程、执行评测、判断门控、回滚 |
| LLM | 读 Trace、诊断原因、生成单点修改 |
| Git | 保存 checkpoint、提供审计轨迹 |
| GT | 定义目标行为 |
| Trace | 提供失败证据 |
不要试图靠更长的 prompt 让 LLM 永远守规矩。规则应该落在代码里,比如:
if changed_files_count > max_files_per_iteration:
reject("scope violation")
if not eval_result.l1_passed:
revert("L1 failed")
if eval_result.critical_safety_violations:
revert("critical safety violation")
从零搭一套 Skill Evolver 的最小流程
一个可落地的最小版本不需要一开始就做得很复杂,可以按这个目录组织:
skill-evolver-workspace/
├── target_skill/
│ ├── SKILL.md
│ ├── references/
│ └── scripts/
├── evals/
│ ├── dev.yaml
│ ├── holdout.yaml
│ └── regression.yaml
├── traces/
├── results.tsv
├── experiments.jsonl
└── evolve_plan.md
最小执行流程:
# 1. 初始化 Git
cd target_skill
git init
git add .
git commit -m "baseline"
# 2. 建立 baseline
python ../tools/eval_skill.py \
--skill . \
--gt ../evals/dev.yaml \
--out ../results.tsv \
--trace-dir ../traces/baseline
# 3. 进入迭代循环
python ../tools/evolve.py \
--skill . \
--plan ../evolve_plan.md \
--dev ../evals/dev.yaml \
--holdout ../evals/holdout.yaml \
--regression ../evals/regression.yaml
results.tsv 至少记录这些字段:
iteration layer commit dev_pass holdout_pass regression_pass cost gate action
0 - baseline 0.82 - 1.00 1.0 keep baseline
1 1 a13f9c2 0.86 - 1.00 1.1 keep routing hint
2 1 b81d0aa 0.84 - 0.98 1.1 discard reverted
当系统能稳定完成这些动作后,再加入更复杂的能力:
- 自动生成 GT。
- blind A/B comparator。
- 多次采样取均值。
- 失败 case 聚类。
- 自动升层策略。
- 成本预算优化。
- 更细粒度的安全扫描。
适合和不适合的场景
Skill Evolver 适合优化“可以被评测”的 Skill。如果没有明确输入输出,也没有稳定指标,循环很难判断什么叫变好。
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 客服问答路由 | 适合 | 有 GT、有正确文档路径、有召回指标 |
| 代码生成规范化 | 适合 | 可以用测试、lint、安全扫描评测 |
| 数据清洗脚本 Skill | 适合 | 输入输出明确,可用脚本校验 |
| 长文创作风格优化 | 部分适合 | 需要设计高质量 rubric,LLM 评测噪声较大 |
| 纯开放式头脑风暴 | 不适合 | 难定义稳定 GT 和回归标准 |
| 法务、医疗等高风险决策 | 谨慎 | 自动修改必须有人审查,不能只靠门控 |
判断一个 Skill 能不能训练,可以问三个问题:
1. 能不能列出一批真实输入?
2. 能不能写出每个输入的期望输出或判断标准?
3. 能不能接受“坏修改自动回滚,好修改自动保留”的流程?
三个问题都能回答“能”,就可以尝试把它纳入自进化循环。
技术来源与组合方式
这套设计可以理解为几类已有思想的组合:
| 支柱 | 作用 |
|---|---|
| autoresearch 式外循环 | 不断提出小改动、短时间验证、保留好结果、回滚坏结果 |
| 通用化 Agent 优化 | 把“优化训练代码”扩展为“优化任何可测对象” |
| Meta-Harness 式 Trace 诊断 | 让 proposer 查看原始执行轨迹,而不是只看摘要和分数 |
| skill-creator 式评测 | 提供 quick validate、grader、comparator 和 GT 生成能力 |
| Git checkpoint | 把每轮 Skill 状态变成可审计、可回滚的实验点 |
组合后的核心原则很简单:
数据定义目标。
Trace 解释失败。
LLM 提出修改。
程序执行评测。
门控决定生死。
Git 保证可回滚。
Skill 不必停留在手工打磨的 prompt 文件上。只要准备好 GT、指标、评测和回滚机制,它就可以像模型 checkpoint 一样被迭代、比较和选优。真正重要的不是让 Agent 自由发挥,而是给它一个严密的训练环境:每次只改一点,每步都有证据,每轮都能回滚,最终留下经得起 dev、holdout 和 regression 检查的版本。