如何系统评测 Agent Skills:从触发用例到自动化评分的完整指南
改了一版 Skill,究竟是真变好了,还是只在几次试跑中看起来更顺?这份 OpenAI 实操指南用六个步骤搭出完整评测闭环:定义成功、人工试跑、设计正反用例、检查 JSONL 轨迹,再用结构化 rubric 审查质量。文中保留全部代码,并提供可直接生成测试 Prompt 与检查清单的交互工具。
开发 Agent Skill 时,最普遍的困扰是改动往往凭感觉判断:换了一版提示词,某些用例看似更顺畅,但你很难确认改动是否真的变好,更难察觉倒退(Regression)——原本该触发的场景不再响应、关键执行步骤被遗漏,或者工作区残留了临时文件。摆脱这种主观推测的解决办法,是建立可比较的评测机制(Evals),用可重复执行、可比较的运行记录判断效果变化并及时发现回归。
Skill 本质上是一组组织有序的提示词与指令集(Prompts and Instructions)。针对 Skill 的评测,类似于面向智能体的轻量级端到端集成测试:输入目标 Prompt → 完整捕获 Agent 执行过程(Trace 轨迹与产生产物) → 运行针对性规则检查 → 输出可跨版本横向对比的分数。
将评测落到实处可以分为六个步骤:定义成功标准、写出可评测的 Skill、人工试跑并暴露隐藏假设、建立 10–20 条包含正反样本的微型评测集、用 JSONL 和确定性 grader 检查行为与产物,以及用只读 Codex 与 JSON Schema 做 rubric-based grading。在此基础上,再视需要引入成熟后的进阶监控。
步骤 1:定义成功标准
很多 Skill 难以评测的根源,在于一开始没有明确界定何为“执行成功”。如果只有模糊的期望,最终验收往往只能依赖随意的人工肉眼观察。第一步要做的是把成功标准清晰定义下来,拆解为聚焦在必过项(Must-pass)上的具体维度。
具体做法是在动笔编写或重构前,列出一份精简的检查清单,明确区分四个目标维度:
- 结果目标(Outcome goals):任务是否最终完成?构建的应用能否正常跑起来?
- 过程目标(Process goals):Agent 是否真正调用了对应的 Skill?是否按照预期顺序执行了指定工具和命令?
- 风格目标(Style goals):生成的文件结构、组件写法、样式方案是否符合既定技术规范?
- 效率目标(Efficiency goals):执行过程中是否存在无效重试(比如反复尝试同类错误命令,或者消耗了异常庞大的 Token)?
判断通过的标准在于硬性指标是否全部达标。例如在脚手架场景中,“是否执行了 npm install”和“是否生成了 package.json”属于过程与结果层面的硬性检查,必须严格通过;而“目录结构是否规整”“样式是否遵循约束”则作为风格标准逐项核对。
完成这步后,你会得到一份涵盖四个维度的结构化验收检查清单(Checklist)。它为后续编写测试断言提供了明确的靶心,评测的核心目的就是在早期暴露具体的回归信号,而不是给出一个模棱两可的综合合格率。
步骤 2:写出可评测的 Skill
要让评测能够运行,前提是 Skill 本身具有清晰可检验的行为边界。第二步要做的是编写出符合规范、包含明确验收条件的 Skill 骨架。
在 Codex 体系中,一个 Skill 本质上是一个包含 SKILL.md 文件的独立目录。文件开头必须包含由 name 和 description 组成的 YAML Front Matter,其后则是规范 Agent 行为的 Markdown 指令,以及可选的配套脚本或资源。
这里的 name 和 description 是整个 Skill 最核心的触发信号。Codex 正是依据这两项元数据,来判断当前任务是否应当调用该 Skill,并决定何时将 SKILL.md 中的完整正文注入到上下文窗口中。如果命名与描述过于宽泛或语义过载,Skill 就无法稳定激活。
如果从零开始,可以直接在终端调用官方提供的脚手架技能:
$skill-creator
脚手架会通过交互引导你明确 Skill 的职责、触发时机,以及属于纯指令型(Instruction-only)还是脚本辅助型(Script-backed)。这里的默认建议是优先采用纯指令型设计。
实战示例:统一规范的 Demo 搭建技能
我们以一个名为 setup-demo-app 的最小化实战 Skill 为例。它的职责是在统一的规范下,稳定初始化一个轻量 React 演示项目:采用 Vite 的 React + TypeScript 模板,通过官方 Vite 插件配置 Tailwind CSS,遵循精简的目录架构,并给出可直接核验的完成定义(Definition of Done)。
根据生效范围的不同,你可以将下方内容保存至两个路径之一:
- 仓库级(Repo-scoped):
.codex/skills/setup-demo-app/SKILL.md - 用户全局(User-scoped):
~/.codex/skills/setup-demo-app/SKILL.md
---
name: setup-demo-app
description: Scaffold a Vite + React + Tailwind demo app with a small, consistent project structure.
---
## When to use this
Use when you need a fresh demo app for quick UI experiments or reproductions.
## What to build
Create a Vite React TypeScript app and configure Tailwind. Keep it minimal.
Project structure after setup:
- src/
- main.tsx (entry)
- App.tsx (root UI)
- components/
- Header.tsx
- Card.tsx
- index.css (Tailwind import)
- index.html
- package.json
Style requirements:
- TypeScript components
- Functional components only
- Tailwind classes for styling (no CSS modules)
- No extra UI libraries
## Steps
1. Scaffold with Vite using the React TS template:
npm create vite@latest demo-app -- --template react-ts
2. Install dependencies:
cd demo-app
npm install
3. Install and configure Tailwind using the Vite plugin.
- npm install tailwindcss @tailwindcss/vite
- Add the tailwind plugin to vite.config.ts
- In src/index.css, replace contents with:
@import "tailwindcss";
4. Implement the minimal UI:
- Header: app title and short subtitle
- Card: reusable card container
- App: render Header + 2 Cards with placeholder text
## Definition of done
- npm run dev starts successfully
- package.json exists
- src/components/Header.tsx and src/components/Card.tsx exist
通过明确的步骤拆解与完成定义(Definition of done),这一步产出了一份结构规范的 SKILL.md 文件。判断它是否合格,关键看输出边界与完成条件是否清晰可核验;如果规则模糊多变,后续的自动化评测就会失去判定依据。
步骤 3:人工试跑并暴露隐藏假设
写好 SKILL.md 之后,不要急于编写大批量脚本,这一步要做的是在真实或临时沙盒环境中手动试跑,观察 Agent 在哪些环节偏离预期,从而暴露并修复 Skill 中未明说的隐藏假设。
你可以先在交互界面中通过 /skills 斜杠命令选择,或者直接在提示词中使用 $setup-demo-app 显式引用该技能。这一阶段的重点是系统排查出三类常见的隐藏假设:
- 触发假设(Triggering assumptions):本该触发的通用提问(例如“帮我搭一个快速 React demo”)未能命中;或者本不该触发的临近场景(例如已有项目提问“帮我给现存应用配个 Tailwind”)却被意外激活。
- 环境假设(Environment assumptions):Skill 默认当前处于完全干净的空目录,或者默认操作系统必然安装了
npm且比其他包管理器更优先。 - 执行假设(Execution assumptions):Agent 误以为依赖已经在全局装好而直接跳过了
npm install,或者在 Vite 项目还没初始化完成前就急着去配置 Tailwind。
当你准备把这些排查过程固化下来时,应当从交互式界面切换到无界面的自动化命令行工具 codex exec。该命令将执行进度流式输出到标准错误(stderr),而仅将最终产出输出到标准输出(stdout),适合脚本调度与 CI 集成。
由于脚手架搭建任务涉及磁盘文件的写入与创建,执行时需要显式附加 --full-auto 权限标识:
codex exec --full-auto \
'Use the $setup-demo-app skill to create the project in this directory.'
在自动化调用时,应遵循最小权限原则,仅授予完成任务所需的最低权限范围,并检查是否出现超出预期的权限升级。这一步不是要求首次试跑就无报错,而是要把偏离预期的地方记录下来。完成排查后,你会得到一组边界问题与可重复执行的调用命令,它们构成下一步评测集的核心来源。
步骤 4:建立 10–20 条包含正反样本的微型评测集
评测 Agent 不需要一上来就堆砌几百上千条庞大基准。对于单个独立 Skill,维持一套包含 10 到 20 条代表性用例的微型评测集,就足以在早期发现有价值的回归信号。
这一步要做的是把前期排查到的边界沉淀为测试用例。我们可以创建一个精简的 CSV 文件(例如 evals/setup-demo-app.prompts.csv),用表格来维护触发预期与具体 Prompt,后续遇到新问题时持续追加新行:
id,should_trigger,prompt
test-01,true,"Create a demo app named `devday-demo` using the $setup-demo-app skill"
test-02,true,"Set up a minimal React demo app with Tailwind for quick UI experiments"
test-03,true,"Create a small demo app to showcase the Responses API"
test-04,false,"Add Tailwind styling to my existing React app"
这四条用例分别代表了不可或缺的四类测试意图:
- 显式调用(Explicit invocation,如
test-01):提示词中明确带上了$setup-demo-app。它用于验证在用户明确指名时,底层调用链路是否畅通,确保后续修改配置时没有破坏基本激活能力。 - 隐式触发(Implicit invocation,如
test-02):完全不提 Skill 名称,只陈述该技能针对的标准使用场景。它用于验证 Front Matter 中的name和description是否足够清晰,能让模型在日常对话中自主选择该技能。 - 带上下文的复杂触发(Contextual invocation,如
test-03):加入了具体业务场景(例如接入 Responses API),引入现实世界中的轻度噪声。它用于验证在存在额外干扰信息时,模型能否依然准确提取脚手架需求并保持统一的代码结构。 - 负向控制样本(Negative control,如
test-04):用于防御误触发(False Positives)。用户询问的是“给已有项目添加 Tailwind 样式”,语义上与 Skill 描述高度相近,但此时绝不能触发新建脚手架动作。缺少负向控制用例,往往会导致 Agent 在用户修改既有代码时误覆盖整个工程。
将正向隐式调用与负向控制对照结合,判断通过的标准就是正向场景稳定激活且步骤完整,负向场景坚决不误触发。完成这步后,你会得到一份结构化的评测数据集文件(evals/setup-demo-app.prompts.csv)。
下面的方案生成器把这一步变成可以直接带走的模板:填入 Skill 名、目标任务、必须执行的命令和必须生成的文件,它会生成四类测试 Prompt、确定性检查清单和建议保存路径。
可带走工具
把你的 Skill 变成第一组可运行的 Eval
修改下面四项,生成器会同步改写四类测试 Prompt、硬规则检查和建议保存路径。默认示例对应正文里的 setup-demo-app。
生成结果
四类 Prompt
确定性检查
建议产物
修改字段即可生成自己的版本。
步骤 5:用 JSONL 和确定性 grader 检查行为与产物
要确凿验证 Agent 真的执行了底层步骤,而不是仅仅碰巧交出一份看似可用的代码,不能依赖主观推测。这一步要做的是通过确定性 grader 直接检验命令轨迹与磁盘产物。
实现这一点的关键是在运行 codex exec 时开启 --json 参数,标准输出(stdout)会转为按行分隔的 JSONL 结构化事件流。每一次底层 Shell 命令的调用,都会以 item.started 和 item.completed 事件清晰记录,且其 item.type 均为 command_execution。
自动化执行与检测脚本
借助这一机制,我们可以编写一个轻量的 Node.js 脚本 evals/run-setup-demo-app-evals.mjs。运行该脚本会依次执行 Agent、捕获轨迹、解析 JSONL 事件,并直接断言命令记录与磁盘文件是否存在:
// evals/run-setup-demo-app-evals.mjs
import { spawnSync } from "node:child_process";
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import path from "node:path";
function runCodex(prompt, outJsonlPath) {
const res = spawnSync(
"codex",
[
"exec",
"--json", // 核心参数:输出结构化 JSONL 事件流
"--full-auto", // 授予必要的文件系统修改权限
prompt,
],
{ encoding: "utf8" }
);
mkdirSync(path.dirname(outJsonlPath), { recursive: true });
// --json 模式下 stdout 即为标准的 JSONL 格式内容
writeFileSync(outJsonlPath, res.stdout, "utf8");
return { exitCode: res.status ?? 1, stderr: res.stderr };
}
function parseJsonl(jsonlText) {
return jsonlText
.split("\n")
.filter(Boolean)
.map((line) => JSON.parse(line));
}
// 确定性规则 1:Agent 是否真正执行过包含 npm install 的命令?
function checkRanNpmInstall(events) {
return events.some(
(e) =>
(e.type === "item.started" || e.type === "item.completed") &&
e.item?.type === "command_execution" &&
typeof e.item?.command === "string" &&
e.item.command.includes("npm install")
);
}
// 确定性规则 2:磁盘上是否真正创建了 package.json 文件?
function checkPackageJsonExists(projectDir) {
return existsSync(path.join(projectDir, "package.json"));
}
// 单用例测试流程示例
const projectDir = process.cwd();
const tracePath = path.join(projectDir, "evals", "artifacts", "test-01.jsonl");
const prompt =
"Create a demo app named demo-app using the $setup-demo-app skill";
// 步骤 1:调用 CLI 执行任务,并将轨迹固化到磁盘
runCodex(prompt, tracePath);
// 步骤 2:读取轨迹事件并执行断言
const events = parseJsonl(readFileSync(tracePath, "utf8"));
console.log({
ranNpmInstall: checkRanNpmInstall(events),
hasPackageJson: checkPackageJsonExists(path.join(projectDir, "demo-app")),
});
这套确定性检查的判断标准很直接:脚本输出中的 ranNpmInstall 与 hasPackageJson 等断言必须全部返回 true。运行后,你会得到一份保存在 evals/artifacts/test-01.jsonl 的完整轨迹产物以及清晰的布尔检验结果。如果某项检查失败,开发者无需猜测,可以直接打开 .jsonl 跟踪日志,按时间顺序查看记录下来的命令执行事件。
步骤 6:用只读 Codex 与 JSON Schema 做 rubric-based grading
确定性代码断言能有效确认基础动作是否完成,但难以评估生成内容是否符合代码风格规范。例如:代码是否全部采用了函数式组件?Tailwind 是否严格使用了原子类而不是混入了 CSS Modules?组件目录结构划分是否严谨?
这一步要做的是在确定性检查后,引入第二道基于预定义标准的只读模型审查环节。具体做法是使用 codex exec 的 --output-schema 参数,强制模型输出符合特定 JSON Schema 的结构化评审数据,做到只读审查而不修改代码。
定义评审标准 Schema
我们先创建一份评分标准契约文件 evals/style-rubric.schema.json。在这份 Schema 中,顶层包含总体是否通过(overall_pass)与总体量化分值(score),具体检查维度则由逐项的通过判定(pass)与评审说明(notes)组成:
{
"type": "object",
"properties": {
"overall_pass": { "type": "boolean" },
"score": { "type": "integer", "minimum": 0, "maximum": 100 },
"checks": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"pass": { "type": "boolean" },
"notes": { "type": "string" }
},
"required": ["id", "pass", "notes"],
"additionalProperties": false
}
}
},
"required": ["overall_pass", "score", "checks"],
"additionalProperties": false
}
执行只读代码审查
脚手架生成完成后,再次调用 codex exec 指向该目录。此时不传 --full-auto 权限,只提供检查提示词与 Schema 路径,并将评分 JSON 存储到产物目录:
codex exec \
"Evaluate the demo-app repository against these requirements:
- Vite + React + TypeScript project exists
- Tailwind is configured via @tailwindcss/vite and CSS imports tailwindcss
- src/components contains Header.tsx and Card.tsx
- Components are functional and styled with Tailwind utility classes (no CSS modules)
Return a rubric result as JSON with check ids: vite, tailwind, structure, style." \
--output-schema ./evals/style-rubric.schema.json \
-o ./evals/artifacts/test-01.style.json
审查结果会写入结构化 JSON:overall_pass 表示总体是否通过,score 用于跨版本比较,每个检查项则提供 pass 与 notes。如果团队另行设定分数阈值,可以在评测脚本中增加对应判断;原文示例本身没有规定统一阈值。这一步会输出评审报告文件 evals/artifacts/test-01.style.json。
通过 --output-schema 约定的输出不再是难以自动化解析的自然语言,而是可直接在 CI 或自动化评测流水线中合并、对比和追踪的结构化数据。如果在 GitHub Actions 中运行,官方提供的 Codex Action 原生支持在 codex-args 中透传该参数。
成熟后的进阶监控
当基础的“执行-断言-评分”双层回路稳定运转后,评测体系可以随着技能复杂度提升进一步扩展。优先建立快速明确的行为反馈,随着测试深入再逐步补充更重、耗时更长的校验手段。
以下六个方向可以把基础评测逐步扩展到更真实的工程场景:
- 命令抖动与循环重试检查:统计 JSONL 日志中的
command_execution发生频次。如果某一版修改导致同类命令执行次数激增,说明 Agent 陷入了报错重试的循环内耗中。 - Token 消耗监控:从事件流的
turn.completed中提取usage.input_tokens与usage.output_tokens。这能直观发现提示词膨胀或上下文冗余,防止单次执行开销失控。 - 静态编译验证:在 Agent 交付后由评测系统追加执行
npm run build。这是关键的端到端硬指标,能有效发现隐蔽的代码语法错误、损坏的模块导入或工具链版本冲突。 - 运行时冒烟探测:执行
npm run dev启动服务,随后使用curl请求本地端口,或者运行极轻量的 Playwright 脚本确认界面挂载正常。这种测试较为耗时,建议针对高风险核心用例按需启用。 - 仓库纯净度检查:执行
git status --porcelain,断言执行完毕后工作区不存在非预期的多余配置文件或临时代码,保持代码库整洁。 - 沙盒与权限回退测试:验证 Skill 在未提权的基础受限沙盒中是否依然能顺畅工作,确认在迭代过程中没有出现超出预期的权限升级,也没有引入强制要求提权的操作命令。
结语
从只靠主观感受调整,转向保留轨迹、逐项检查和持续比较,能让 Agent Skill 的迭代建立在可靠的工程基础上。这套流程将“这版改动看起来挺顺手”的主观推测,替换成了结构化的事实依据:
从定义明确的成功标准开始,写出结构规范的 Skill;通过人工试跑摸清假设边界,把调试中暴露的问题沉淀为 10–20 条典型评测用例;底层用 JSONL 事件流与确定性 grader 校验命令与文件产物,上层借助 JSON Schema 规范模型的代码审查;待技能成熟后,再按需扩展编译校验与运行时监控。
有了可重复运行的评测基座,每次调整提示词或执行逻辑时,都能明确看出改动究竟带来了哪些改进,并及时发现意外的回归信号。通过真实问题持续扩充用例,Skill 才能在长期迭代中稳定保持既有能力。
如何系统评测 Agent Skills:从触发用例到自动化评分的完整指南
开发 Agent Skill 时,最普遍的困扰是改动往往凭感觉判断:换了一版提示词,某些用例看似更顺畅,但你很难确认改动是否真的变好,更难察觉倒退(Regression)——原本该触发的场景不再响应、关键执行步骤被遗漏,或者工作区残留了临时文件。
Skill 本质上是一组组织有序的提示词与指令集(Prompts and Instructions)。针对 Skill 的评测,类似于面向智能体的轻量级端到端集成测试:输入目标 Prompt → 完整捕获 Agent 执行过程(Trace 轨迹与产生产物) → 运行针对性规则检查 → 输出可跨版本横向对比的分数。
步骤 1–3:定义 4 维标准与清晰行为边界
很多 Skill 难以评测的根源,在于一开始没有明确界定何为“执行成功”。如果只有模糊的期望,最终验收往往只能依赖随意的人工肉眼观察。第一步要做的是把成功标准清晰定义下来,拆解为聚焦在必过项上的具体维度:
SKILL.md 中 Front Matter 的 name 与 description 决定。编写完骨架后,先通过 codex exec --full-auto 手动试跑,排查“触发假设”(意图未命中)、“环境假设”(误认目录已就绪)与“执行假设”(误认依赖已全局安装)三大暗坑,把边界转化为固定调用。
步骤 4:构建包含正反样本的微型评测集
评测 Agent 不需要一上来就堆砌几百上千条庞大基准。对于单个独立 Skill,维持一套包含 10 到 20 条代表性用例的微型评测集,就足以在早期发现有价值的回归信号。评测集必须涵盖四类互补意图:
Create a demo app named devday-demo using the $setup-demo-app skill
验证在用户明确指名时,底层调用链路畅通无阻,确保配置未被破坏。
Set up a minimal React demo app with Tailwind for quick UI experiments
验证元数据(name / description)是否准确,模型能否在自然提问中自主命中。
Create a small demo app to showcase the Responses API
引入业务噪声与轻度干扰,验证复杂背景下能否稳定抽离核心脚手架动作。
Add Tailwind styling to my existing React app
关键防御!语义高度临近但决不能触发全新脚手架,防止错误覆盖用户现有项目。
步骤 5–6:核心机制——确定性断言与只读 Schema 双层闭环
要确凿验证 Agent 真的执行了底层步骤,而不是仅仅碰巧交出一份看似可用的代码,不能依赖主观推测。单纯看文件无法察觉执行过程中的盲目重试,而单纯依靠大模型自由评分又容易产生幻觉与提示词漂移。解决之道是建立软硬解耦的双层评测流水线:
双层自动化评测流水线(Dual-Layer Pipeline)
以确定性日志断言核验底层动作硬指标,以只读 Schema 审查上层代码风格规范,形成机器可量化比对的客观报告。
运行该脚本会依次执行 Agent、捕获轨迹、解析 JSONL 事件,并直接断言命令记录与磁盘文件是否存在:通过 codex exec --json --full-auto 导出每一步事件。检查事件中是否存在 command_execution 且匹配 npm install;同时直接调用 Node.js 的 existsSync 检查 package.json 是否存在。任何一项不符直接判挂,零模型幻觉干扰。
具体做法是使用 codex exec 的 --output-schema 参数,强制模型输出符合特定 JSON Schema 的结构化评审数据,做到只读审查而不修改代码。调用时剥离 --full-auto 权限防止修改文件,模型审查组件是否为函数式、Tailwind 是否避免 CSS Modules,并输出 overall_pass、score 与逐项布尔值和说明。
成熟后的进阶监控流水线
优先建立快速明确的行为反馈,随着测试深入再逐步补充更重、耗时更长的校验手段。当基础的双层评测跑通后,可将测试体系逐步延伸到工业级防线:
统计 JSONL 中 command_execution 发生频次。同类命令执行次数骤增代表 Agent 陷入了报错重试的内耗。
从 turn.completed 事件中提取输入与输出 Token 用量,防止提示词修改引发上下文爆炸与成本失控。
Agent 交付后自动追跑 npm run build,拦截隐蔽的 TypeScript 类型报错、语法缺陷与损坏的依赖导入。
执行 npm run dev 并用轻量 Playwright 或 curl 请求本地端口,确认 UI 根节点能成功挂载渲染。
执行 git status --porcelain,断言任务结束后没有残留多余的临时日志或未清理的脏配置。
验证 Skill 在无提权的受限沙盒中是否依然能正常运转,确保版本迭代中未引入非预期的强制提权指令。