# AI Skill Evaluator

> 【Skill 质量打分器】用一份测试题，给一个 Skill（SKILL.md）打分。能看出：该触发时有没有触发、不该触发时有没有误触、输出是否符合预期、改完之后是变好了还是变差了。适用场景：Skill 发布前自检、改完之后回头比一比、多个模型挑一个、看会不会乱触发、看响应快不快花费多不多。触发词：评估 skill、测试 skill、给 skill 打分、看看 skill 准不准、会不会误触、改完对比一下。⚠️必须用提前准备好的测试题打分，不能靠感觉说好不好

- Skill: `gitcustomer/ai-skill-evaluator` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add gitcustomer/ai-skill-evaluator`
- Raw SKILL.md: https://api.skillmd.com/api/skills/gitcustomer/ai-skill-evaluator/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: gitcustomer (https://skillmd.com/u/gitcustomer)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/gitcustomer/ai-skill-evaluator

---


# Skill 打分器

给一个 Skill（即 SKILL.md 文件）做质量打分。
用一份提前写好的测试题集，跑一遍这个 Skill，看它**该触发时有没有触发**、**输出对不对**、**会不会乱触发**、**多次跑结果稳不稳定**，并且能和**上一版结果做对比**。
最后输出一份打分报告（人看的 Markdown + 机器读的 JSON），让 Skill 的好坏"看得见、比得了、追得回"。

## ⚠️ Hard Rules

1. **必须用测试题打分** — 必须读取 `testset.json`，不允许凭感觉说"还行"、"不太行"。没有测试题就拒绝跑分
2. **该触发的、不该触发的都要测** — 测试题里既要有"应该触发的输入"，也要有"不该触发的输入"，只测一边的结果不可信
3. **每条失败都要列证据** — 报告里每个失败用例必须写出：输入是什么、期望什么、实际是什么。不允许只写"失败了"
4. **不准改被测 Skill** — 打分器只读，不写。发现问题只在报告里提建议，让用户自己决定改不改
5. **想做版本对比，必须给上一版的报告** — 要看"是变好还是变差"，就必须把旧版报告也提供出来，否则只输出当前结果
6. **指标算法统一** — 准确率/召回率/综合分都用业界通用算法，不允许自己发明"满意度"之类的指标
7. **调优集和验收集分开** — 测试题分两批：一批用来给改进建议（调优集），另一批专门用来打分（验收集）。禁止用验收集去反推怎么改 Skill，否则分数会"虚高"
8. **必须测稳定性** — 同一道题至少跑 3 次，结果波动超过 10% 就算不稳，单次高分不算数
9. **AI 打分员要防偏见** — 让 AI 给 Skill 打分时，要打乱顺序（防"先看到的得分高"）、隐藏 Skill 作者（防"AI 偏向自己写的"）

## 触发条件

当用户说以下内容时触发本 Skill：
- "给这个 skill 打分一下" / "测一下 skill 准不准"
- "skill 改完了，跑一下看有没有变差" / "新旧版本对比一下分数"
- "看看 skill 会不会乱触发" / "查一下命中率和误触率"
- "skill 跑得快不快、花费多不多"

## 不触发条件

以下情况用别的 Skill：
- 用户想新建一个 Skill → 用 `SKILL.template.md` 自己写
- 用户想测试游戏关卡能不能通关 → 用 `ai-game-playtester`
- 用户想检查素材文件格式 → 用 `ai-game-asset-pipeline`
- 用户只是想了解 Skill 怎么写 → 直接看模板文档

## 输入参数

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `skill_path` | 字符串 | ✓ | - | 要打分的那个 Skill 的 SKILL.md 文件路径 |
| `testset_path` | 字符串 | ✓ | - | 测试题集 JSON 文件路径（参考 `testset.example.json`） |
| `baseline_report` | 字符串 | | - | 上一版的报告路径，给了就会做新旧对比 |
| `model` | 字符串 | | `default` | 跑测试用的模型名字，仅记录在报告里 |
| `output_dir` | 字符串 | | `eval-reports/` | 报告输出目录 |
| `pass_threshold` | 数字 | | `0.85` | 综合分及格线，低于此值整体判为不及格 |
| `holdout_ratio` | 数字 | | `0.4` | "验收集"占比（推荐 4 成验收 + 6 成调优） |
| `stability_runs` | 数字 | | `3` | 同一题重复跑几次，用来看稳不稳定 |
| `judge_model` | 字符串 | | - | 当 AI 打分员的模型名字，留空就不打主观分 |

## 测试题集格式

测试题集是一个 JSON 数组，每道题包含：

```json
{
  "id": "case_001",
  "type": "应触发 | 不应触发",
  "split": "调优集 | 验收集",
  "input": "用户的输入原话",
  "expected_trigger": true,
  "expected_output_contains": ["关键字1", "关键字2"],
  "expected_params": { "字段名": "字段值" },
  "expected_tool_calls": ["read_file", "validate_schema", "write_file"],
  "rubric": { "清晰度": 4, "完整度": 5 },
  "tag": "可选标签，用于分组统计"
}
```

- `应触发` — 这种输入应该触发本 Skill，并且输出要包含指定关键字
- `不应触发` — 这种输入不该触发本 Skill（用来测会不会乱触发）
- `split` — `调优集`（用来给改进建议）/ `验收集`（用来打最终分，不能用它来反向调 Skill）
- `expected_output_contains` — 输出里必须包含的关键字（数组里全部命中才算通过）
- `expected_params` — 输出里必须有的字段（按字段名严格匹配）
- `expected_tool_calls` — 期望调用了哪些工具，且顺序也对
- `rubric` — AI 打分员的打分项（1-5 分），只有配置了 `judge_model` 才用

## 执行流程

### 步骤 0：前置检查

1. 检查 `skill_path` 文件存在，且文件开头有头部信息（即 `---` 包起来的那段）
2. 检查 `testset_path` 是合法的 JSON 数组（题目 ≥ 10 条，应触发和不应触发各 ≥ 3 条）
3. 缺一项就报告缺哪个 → **停止执行**

### 步骤 1：读懂被测 Skill

读取 Skill 文件，提取：

| 信息 | 来源 | 用途 |
|------|------|------|
| Skill 名字 | 头部信息 | 报告里显示用 |
| description 描述 | 头部信息 | 提取触发词 |
| 触发词列表 | description 里的"触发词:"段 | 算命中率用 |
| 强制规则数 | 正文 | 看 Skill 是否健壮 |
| 步骤数 | 正文中的"### 步骤 X" | 看 Skill 复杂度 |

### 步骤 2：跑测试题

每道题按下面流程跑一遍：

| 环节 | 动作 | 记录什么 |
|------|------|------|
| 喂入题目 | 把 `input` 当作用户输入 | - |
| 看是否触发 | 判断 Skill 是否被激活 | `是否触发` |
| 收集输出 | 拿到 Skill 输出的文字和字段 | `实际输出`、`实际字段` |
| 看工具顺序 | 记录都调用了哪些工具、顺序如何 | `实际工具调用` |
| 重复跑 N 次 | 同题跑 `stability_runs` 次看波动 | `多次结果`、`波动幅度` |
| 看会不会瞎编 | 输出里的字段必须能在输入里找到依据 | `没依据的字段` |
| AI 打分员（可选） | 让另一个模型按打分表给分 | `各项分数`、`打分理由` |
| 对答案 | 跟 `expected_*` 字段一项一项比 | `是否通过`、`哪些断言不过` |
| 算性能 | 记录耗时和字数消耗 | `耗时毫秒`、`字数` |

### 步骤 3：算分

打分维度（写大白话，但保留业界算法）：

| 指标 | 算法 | 通俗解释 |
|------|------|------|
| **准确率（Precision）** | 触发对的次数 ÷ 全部触发次数 | 触发的里面，有多少是真的该触发的（防乱触发） |
| **召回率（Recall）** | 触发对的次数 ÷ 全部应触发题数 | 该触发的里面，触发到了多少（防漏触发） |
| **综合分（F1）** | 准确率和召回率的加权平均 | 把上面两个综合起来的总分 |
| **输出通过率** | 输出断言通过数 ÷ 应触发题总数 | 输出对不对 |
| **误触率** | 不该触发却触发的次数 ÷ 不应触发题总数 | 越低越好 |
| **工具顺序得分** | 工具序列对得上的题数 ÷ 总数 | 是不是按预期的顺序调工具 |
| **稳定性** | 1 - 多次跑结果的波动幅度 | 同一道题反复跑，结果稳不稳定 |
| **编造率** | 没依据的字段数 ÷ 总字段数 | 输出里有多少是 AI 自己瞎编的（越低越好） |
| **AI 打分** | AI 打分员各项均分（1-5 分） | 主观质量分，仅配置了 `judge_model` 时有 |
| **响应时间（P95）** | 95% 的题在多少秒内能跑完 | 看快不快 |
| **平均字数消耗** | 每题平均花了多少字数 | 看花费贵不贵 |

> ⚠️ **打分用"验收集"，建议用"调优集"** —— 严守强制规则 7。报告里这两批的得分要分开列出来。

### 步骤 4：根据结果给改进建议（综合分不达标时）

当综合分 < `pass_threshold` 或误触率 > 0.1 时，进入分析循环（最多 3 轮）：

1. **把失败题归类** — 按原因分组：
   - 触发词漏掉了 → 提示：description 里要补哪些词
   - 字段没解析对 → 提示：哪个字段的解析规则要改
   - 误触集中在某类输入 → 提示："不触发条件"要补哪些边界
2. **生成改进建议日志**：
```
🔄 改进建议日志（第 N/3 轮）
━━━━━━━━━━━━━━━━━━━━
❌ 失败原因归类: 触发词覆盖不全（4 条不该触发的输入被触发了）
   样例输入: ["帮我看看代码", "查询一下文件"]
   分析: description 里"查"字这个触发词太宽泛
   建议: 在"不触发条件"里明确排除"查询/查看"这类只是看不动的意图
```
3. **建议只放在报告里** — 不会自动改 Skill（强制规则 4 不允许），由用户自己决定改不改
4. **3 轮没有新建议** → 停止循环，进入下一步

### 步骤 5：和上一版做对比（给了上一版报告时）

读取 `baseline_report` 文件，把每项指标和当前对比：

| 指标 | 上一版 | 这一版 | 变化 | 判定 |
|------|------|------|---|------|
| 综合分 | 0.82 | 0.91 | +0.09 | ✅ 变好了 |
| 误触率 | 0.15 | 0.08 | -0.07 | ✅ 变好了 |
| 响应时间 P95 | 1200ms | 1500ms | +300ms | ⚠️ 变慢了 |

**判定规则**：
- 任意一项指标比上一版差 5% 以上 → 标记为"倒退"
- 全部持平或变好 → 标记为"持平"或"改善"

### 步骤 6：先预览，确认后再写报告

生成两种文件：

**1) Markdown 报告（给人看）**：

```
📊 Skill 打分报告: ai-game-level-generator
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
打分时间: 2025-11-08 14:30 | 模型: default | 题目总数: 24

✅ 整体结果: 通过 (综合分=0.91, 及格线=0.85)

核心指标:
  准确率:        0.93  (15 次触发里 14 次正确)
  召回率:        0.88  (16 道应触发题里触发了 14 道)
  综合分:        0.91
  输出通过率:    12/14 (85.7%)
  误触率:        0.08  (12 道不应触发题里 1 次误触)
  响应时间 P95:  1.4 秒

❌ 失败题目（3 道）:
  - case_017 [应触发]: "做个最终关" 没有触发（触发词里缺"最终关"）
  - case_022 [输出]:   输出里缺少 "level_id" 字段
  - case_009 [不应触发]: "查看现有关卡" 被误触发了

🔄 改进建议:
  1. description 里补充触发词: "最终关"、"final stage"
  2. 在"不触发条件"里明确排除: "查看/浏览"这类只看不动的意图

📈 对比上一版 v1.0: 综合分 +0.09（变好了）
```

**2) JSON 数据（给机器读，作为下次的对比基准）**：

```json
{
  "skill_name": "ai-game-level-generator",
  "timestamp": "2025-11-08T14:30:00Z",
  "model": "default",
  "metrics": { "precision": 0.93, "recall": 0.88, "f1": 0.91 },
  "verdict": "PASS",
  "failed_cases": [...]
}
```

先把 Markdown 摘要展示给用户 → 等用户确认 → 再写到 `output_dir` 目录。

## 完成标志

报告写完后，输出：

```
<promise>DONE</promise>
```

## 错误处理

| 出错场景 | 处理方式 |
|------|----------|
| Skill 文件没头部信息 | 报告"不是合法的 SKILL.md"，停止 |
| 测试题不够 10 条，或应触发/不应触发不齐 | 拒绝执行，提示用户补题 |
| 单道题跑超 30 秒 | 标记为"超时"，计入失败统计，但不中断整体 |
| 上一版报告格式不对 | 跳过对比，只输出当前结果 |
| 输出目录没写入权限 | 报告具体路径并提示用户授权 |
| 稳定性波动 > 10% | 标"不稳定"，不中断流程但提示用户关注 |
| AI 打分员两次打分差超过 1 分 | 多跑几次取平均；还是波动就标"结论可信度低" |
| 没有验收集（用户全标了"调优集"） | 警告并强制按 6:4 自动切分 |

## 与其他 Skill 的协作

```
Skill 写好了
    ↓
ai-skill-evaluator（本 Skill）
    ├─ 读取 SKILL.md + testset.json
    ├─ 跑测试 + 算分
    ├─ 给改进建议
    └─ 输出报告（Markdown + JSON）
    ↓
用户根据报告手动改 SKILL.md
    ↓
再跑一次（带上一版报告）→ 看变好还是变差
```

