这是 Codex 系列的第 12 篇。
第 11 篇做出了第一个仓库级 Skill:review-technical-article。它已经能告诉 Codex 什么时候审查技术文章、按什么顺序检查、最终怎样给出发布结论。
但“能被调用”只是起点。实际用几次后,会出现更麻烦的问题:
事实核对的来源规则越来越长,要不要继续塞进 SKILL.md?
frontmatter、日期和图片路径每次都让模型重新判断,结果能稳定吗?
改了 description 以后,怎样知道 Skill 没有在错误场景里乱触发?
一份 evals.json 通过,是否等于整个 Skill 已经可靠?
这一篇不重新创建另一个玩具 Skill,而是继续改造同一个真实案例。最终目录里会加入 references/、scripts/、evals/ 和 agents/openai.yaml,并用成功样例和故意损坏的样例验证脚本。
| 项目 | 说明 |
|---|---|
| 内容类型 | Codex Skills 进阶实战 |
| 适合读者 | 已完成一个 SKILL.md,准备长期维护或共享工作流的人 |
| 跟做前提 | Node.js 18+、一个可修改的项目目录、基础命令行经验 |
| 阅读 / 练习时间 | 阅读约 20-23 分钟,跟做约 45-60 分钟 |
| 可带走产物 | 完整 Skill 目录、来源策略、文章预检脚本、7 个触发评估用例 |
| 本文案例 | review-technical-article 1.1.0 |
| 官方资料核对日期 | 2026-07-15 |
| 本地验证 | Skill 结构、评估清单、脚本成功路径与失败路径均已实际运行 |
版本与证据说明
本文按 2026-07-15 的 OpenAI Skills 文档、Agent Skills specification、Anthropic Agent Skills 与 Claude Code 文档整理,并对照 OpenAI、Anthropic 的官方 GitHub 示例。Codex 是本文的实际运行环境,Claude 只用于说明共用规范和产品边界;官方约定与本文项目自定义字段会分开标注。
本文已经验证目录结构和两个确定性脚本,但没有把“评估清单结构通过”写成“模型行为已经通过”。完整行为评估需要在相互独立的干净任务里运行当前 Skill 和基线,再人工比较结果。
一分钟概览
先给结论:
SKILL.md负责判断和编排,不应该变成所有知识的仓库。references/放按需读取的详细知识,scripts/放确定性、重复且适合机器执行的步骤。evals/evals.json保存真实测试任务,但不会在 Skill 每次触发时自动运行。- 评估至少要覆盖应该触发、不应该触发和需要澄清三类输入。
- 行为评估需要三层证据:脚本结果、可观察行为断言、人工 review。
- “结构通过”“脚本通过”“行为通过”和“文章事实正确”是四件不同的事。
SKILL.md的核心格式可以复用,但 Codex、Claude Code、Claude API 的安装位置、配置扩展和运行依赖并不相同。
图 1:从一份 SKILL.md 升级为可验证 Skill 包;判断、知识、确定性处理和回归评估各有职责,SVG 连线带有轻量动态效果
这次升级后的目录如下:
.agents/
└── skills/
└── review-technical-article/
├── SKILL.md
├── agents/
│ └── openai.yaml
├── references/
│ └── source-policy.md
├── scripts/
│ ├── article-preflight.mjs
│ └── validate-evals.mjs
└── evals/
├── evals.json
└── files/
└── sample-codex-skill-tutorial.md
这是一份可以工作的完整目录,不是所有 Skill 的固定模板。一个稳定的 instruction-only Skill 仍然可以只有 SKILL.md。只有当新文件解决了真实问题,才值得拆出去。
下载本文验证过的 review-technical-article 1.1.0 Codex 仓库级示例包
压缩包只包含 Skill 目录和一篇明确标注“故意含错”的评估 fixture,不包含博客正文或项目依赖。它验证于 Codex 和当前博客仓库,不是可以原样上传到所有 Agent 产品的通用包。解压到其他项目以后,仍需安装 Node.js 与 gray-matter,或者把预检脚本改成自包含实现;先运行文中的结构和脚本检查,再决定是否启用隐式调用。
在 npm 项目中可以安装本文实际使用的版本:
npm install --save-dev gray-matter@4.0.3
node .agents/skills/review-technical-article/scripts/article-preflight.mjs --help
使用 pnpm、Yarn 或 Bun 时换成对应的安装命令。若只需要 instruction、references 和 eval 用例,也可以不运行依赖 gray-matter 的文章预检脚本,但必须把这项验证标成未执行。
本文附件的 SHA-256 为 41815817F52B82BFA755F8E1BEEA1106D69C272338FA3F67165A32D264F128C9。它用于确认下载文件与本文最终复验的压缩包一致,不代表包内 Skill 已完成模型行为评估。
同一份 Skill,到了 Claude 为什么不能只改目录名
Agent Skills 的开放规范让不同 Agent 可以共享 SKILL.md、references/、scripts/ 和 assets/ 这些基本组织方式。但“格式相通”只代表迁移成本较低,不代表安装、权限和运行时完全相同。
| 维度 | Codex | Claude Code | Claude API / claude.ai |
|---|---|---|---|
| 项目级发现位置 | .agents/skills/ | .claude/skills/ | 上传 ZIP,或通过 Skills API 管理 |
| 共用核心 | SKILL.md、references、scripts、assets | 基本相同 | 基本相同,但要满足容器约束 |
| 产品专属扩展 | agents/openai.yaml | Claude Code 配置与 Plugin | Skill ID、服务端版本和产品设置 |
| 运行环境 | 随 Codex 入口、sandbox 和 approval 设置变化 | 使用本机文件系统与本机依赖 | Claude API 无网络且不能在运行时安装包;claude.ai 依设置而异 |
| 分发方式 | 仓库目录;跨项目优先 Plugin | 仓库目录或 Claude Code Plugin | 每个产品表面分别上传或管理,不会自动同步 |
因此,迁移时应该先保留通用核心,再为目标产品补适配层:Codex 的 agents/openai.yaml 不属于开放规范,Claude Code 的工具控制也不会因为复制这份文件自动生效。本文附件中的 gray-matter 更是明确的运行时依赖;在不能安装包的容器里,应该换成自包含脚本或确认目标环境已经预装依赖。
这张表解决的是边界问题,不改变本文主线:下面所有命令和实际验证仍以 Codex 仓库级 Skill 为准。
1. 先定义“可靠”,不要先创建目录
第一次写 Skill 时,最容易把“可靠”理解成“写得很详细”。这不够。
对本文这个技术文章审查 Skill,我把可靠拆成四层:
| 层次 | 要回答的问题 | 证据 |
|---|---|---|
| 结构可靠 | 文件能否被发现,元数据和清单格式是否正确 | Skill validator、JSON schema 检查 |
| 机械可靠 | 日期、字段、路径等确定性检查能否重复得到同一结果 | 脚本输出和退出码 |
| 行为可靠 | 应触发时是否触发,不应触发时是否保持安静 | 正向、负向、模糊用例 |
| 内容可靠 | 事实、复现步骤和读者价值是否真的成立 | 官方来源、实际运行、人工 review |
这四层不能相互替代。
例如,article-preflight.mjs 能确认图片文件存在,却不知道图里是否写错了命令;它能确认摘要不是空字符串,却不知道摘要是否准确;它能确认外链返回了 HTTP 200,却不能证明链接内容支持正文里的主张。
所以本文的目标不是“用脚本替代编辑”,而是:
让机器负责适合机器的确定性,
让 Agent 负责需要上下文的判断,
让人保留最终发布责任。
2. 内容到底应该放在哪里
Skill 变复杂以后,第一个设计问题不是“如何拆文件”,而是“这条内容属于谁”。
图 2:Codex Skill 内容放置决策;先按职责在 AGENTS.md、SKILL.md、references、scripts、evals 与 MCP 或 App 之间选择
我现在使用下面这张决策表:
| 内容 | 更合适的位置 | 例子 |
|---|---|---|
| 整个项目长期遵守的规则 | AGENTS.md | 构建命令、目录约束、博客分类规则 |
| 一类任务的触发、步骤与输出 | SKILL.md | 技术文章 review 的审查流程 |
| 只在部分任务里需要的详细知识 | references/ | 来源优先级、证据台账字段 |
| 可确定计算、重复执行的处理 | scripts/ | frontmatter、日期、图片路径检查 |
| 修改 Skill 后需要重复验证的任务 | evals/ | 正向、负向、模糊触发用例 |
| 实时访问外部系统和执行动作 | MCP / App | 读取 GitHub PR、发送消息、查询数据库 |
| 面向 Codex 界面的名称、提示与调用策略 | agents/openai.yaml | 展示名称、默认 Prompt、隐式调用开关 |
这里有两个常见误区。
第一,references/ 不是把 SKILL.md 随便拆小。参考文件应该可以被一句明确条件召回,例如“文章包含版本敏感或安全相关事实时读取来源策略”。如果正文只写“按需读取相关资料”,Agent 仍然不知道什么时候读。
第二,scripts/ 不是“看起来更工程化”的装饰。若步骤需要综合语境、判断论证是否成立,脚本很可能不合适;若结果可以通过解析和规则稳定算出,才适合脚本。
3. references:把详细知识变成按需证据
第 11 篇的 SKILL.md 里已经有一句:
Verify material claims with primary sources.
方向没错,但真实 review 里还会遇到:
- 官方文档与社区教程冲突时信谁?
- 博客里的亲身经验算不算证据?
- 哪些主张值得逐条记录,哪些只是普通叙述?
- 链接能打开,是否等于它支持正文?
- 版本敏感事实需要记录什么日期?
如果全部继续写进主文件,Skill 每次触发都要加载一大段来源规范,即使任务只是检查一个不含版本事实的静态算法教程。
因此我增加:
references/source-policy.md
它只在文章包含“版本敏感、存在争议、安全敏感或产品特定主张”时读取。核心优先级是:
当前官方文档
→ 官方仓库 / release / specification / 安全公告
→ 原始研究或维护者资料
→ 社区教程与 issue 讨论
→ 作者个人判断
低优先级来源不是不能用。社区 issue 很适合补充真实故障,作者经验也可能非常有价值;问题在于不能把它们写成官方保证。
3.1 一份够用的证据台账
参考文件还定义了一个小型 evidence ledger:
| 字段 | 含义 |
|---|---|
| Claim | 能独立核对的最小主张 |
| Source class | 官方文档、官方仓库、标准、社区或作者判断 |
| Source | 直接支持该主张的 URL 或本地文件 |
| Checked on | YYYY-MM-DD 核对日期 |
| Status | verified、contradicted、partially supported、unverified |
| Note | 版本边界、冲突、推断或剩余不确定性 |
例如,本文关于评估目录的记录可以写成:
| Claim | Source class | Checked on | Status | Note |
|---|---|---|---|---|
Agent Skills 的创建指南建议把测试任务放进 evals/evals.json | 公开规范的官方指南 | 2026-07-15 | verified | 这是 authoring convention |
| Codex 每次调用 Skill 都会自动运行该文件 | 无 | 2026-07-15 | contradicted | 不能这样表述 |
activation 是官方运行时字段 | 本项目自定义 | 2026-07-15 | unverified as official | 仅用于本文校验脚本 |
这张表的价值不是让文章显得严谨,而是迫使作者把三个相近概念分开。
3.2 references 的三个边界
不要重复项目规则。 博客的分类、构建命令和发布流程已经在 AGENTS.md,来源策略不再复制。
不要形成深层引用链。 SKILL.md 指向一份具体参考文件,比“参考文件 A 再让 Agent 去找 B、C、D”更可控。
不要无条件加载。 如果每次都需要读,内容可能就应该留在 SKILL.md;如果只在少数场景需要,才适合渐进式加载。
4. scripts:把机械判断交给确定性程序
技术文章 review 中有一批问题非常适合脚本:
- frontmatter 必填字段是否齐全;
date与updated是否为真实日期;updated是否早于date;- 发布检查时
draft是否已经是false; - 标签是否为空或数量明显失控;
- 本地图片和文件链接是否存在;
- 图片 alt 是否为空;
- 外部链接是否至少可以访问。
这些问题如果只交给模型,每次都要重新扫描文本,还可能漏掉隐藏在长文后半段的路径。
但以下问题不应该交给这个脚本:
- 命令是否适合当前 Codex 版本;
- 引用页面是否真的支持正文结论;
- 教程是否值得收藏;
- 图是否帮助理解;
- 一段个人判断是否有足够上下文。
4.1 先写脚本合同
我没有直接从正则开始,而是先约定命令接口:
Usage: node article-preflight.mjs <article.md> [options]
Options:
--publish 要求 draft: false,并启用发布态警告
--remote 尝试检查外部 HTTP 链接是否可达
--json 输出结构化 JSON
--help 显示帮助
Exit codes:
0 没有验证错误
1 发现文章错误
2 参数错误或脚本内部失败
这个接口体现了 Agent 脚本应有的几个特征:
- 非交互式,调用后不等待用户输入。
- 默认只读,不修改文章。
--help能独立说明输入、输出与退出码。- 数据可以输出 JSON,诊断信息有明确错误代码。
- 失败可被上层工作流判断,而不是只打印一句“有问题”。
脚本依赖 Node.js 18+ 和当前博客仓库已经安装的 gray-matter。这个前提被写进 Skill;如果运行环境缺失依赖,Agent 应该披露“未运行”,不能假装通过。
这也是一个明确的可移植性取舍:本文做的是仓库级 Skill,它可以复用项目已有依赖;若准备通过 Plugin 分发给其他项目,应把脚本改成自包含实现,或使用可锁定版本的运行方式,并重新跑完整评估。把目录复制走不等于完成迁移。
4.2 预检脚本做了什么
实现逻辑可以压缩成下面这段伪代码:
parse frontmatter
check required fields, dates, draft state, tags and body
strip fenced code blocks
collect Markdown images and links
check local targets with the file system
if --remote:
check external URLs with timeout
report network failures as warnings
print JSON
exit 1 when deterministic errors exist
这里特意把远程链接失败设为 warning。原因很现实:限流、登录墙和反爬策略都可能让一个有效页面暂时返回 403 或超时。脚本可以证明“这次请求没确认成功”,不能直接宣布“文章引用失效”。
4.3 运行真实文章
从仓库根目录执行:
node .agents/skills/review-technical-article/scripts/article-preflight.mjs content/blog/2026-07-14-codex-skills-introduction.md --publish --json
正文优先给单行、正斜杠版本,因为它在 PowerShell、Bash 和多数命令执行器里都更容易直接复用。若在本地把命令拆成多行,再使用当前 shell 对应的续行语法,不要把 PowerShell 的反引号写进 Skill 的通用说明。
2026-07-15 的实际结果摘录:
{
"ok": true,
"mode": "publish",
"errors": [],
"warnings": [],
"counts": {
"errors": 0,
"warnings": 0,
"images": 3,
"local_links": 0,
"external_links": 7
}
}
这证明第 11 篇的 frontmatter、本地图片和发布状态通过了这套机械规则。它没有证明第 11 篇里每一条 Codex 事实都正确,也没有证明文章对每个读者都有价值。
4.4 故意让它失败
只测试成功样例,很容易把“脚本能运行”误当成“脚本能拦错”。我又创建了一个临时损坏样例:
---
title: "Broken fixture"
date: "2026-02-30"
updated: "2026-01-01"
draft: true
summary: "short"
category: "AI 与智能体"
tags: ["Codex"]
---

发布模式实际返回:
errors:
frontmatter.missing 缺少 series
frontmatter.date 日期不存在
publish.draft 发布检查仍为草稿
image.missing 本地图片不存在
warnings:
frontmatter.tags-range
publish.summary-length
exit code: 1
成功路径和失败路径都符合预期以后,这个脚本才算完成最小验证。
5. 不要重复造轮子:仓库检查与 Skill 脚本怎样共存
当前博客本来就有:
scripts/check-content.mjs
它一次检查整个 content/blog,适合 CI 或发布前全量校验。新的 article-preflight.mjs 一次检查一篇文章,适合 Skill 在 review 过程中获得结构化证据。
两者有部分重叠,但调用场景不同:
| 工具 | 范围 | 典型调用者 | 适合时机 |
|---|---|---|---|
scripts/check-content.mjs | 整个博客 | npm script、CI、部署流程 | 全站构建与发布 |
article-preflight.mjs | 单篇文章 | review Skill | 编辑、审稿、单篇发布判断 |
如果两个脚本以后开始复制大量业务逻辑,就应该提取共享模块。当前只共享少量基础规则,保持独立更容易理解,也不会让一个仓库级 Skill 反向控制整个站点构建。
这个判断很重要:增加 scripts/ 不代表要把仓库已有工具全部重写一遍。
6. evals:保存真实任务,不是保存一句“应该很好”
Agent Skills 的评估指南建议在 Skill 目录里使用:
evals/evals.json
测试用例至少包含任务 prompt 和 expected_output,也可以附带输入文件。指南还建议先从少量真实且有差异的任务开始,并把当前 Skill 与没有 Skill 或旧版本的基线作对照。
本文建立了 7 个用例,而不是只写一个最容易通过的正向 Prompt。
显式发布审查用例附带 evals/files/sample-codex-skill-tutorial.md。这篇 fixture 故意写错仓库级 Skill 路径和隐式调用默认值,并保留 draft: true 与一张缺失图片,用来检查事实审查与确定性预检是否都能发现问题。它不是参考资料,也不得发布。
| 类型 | 数量 | 代表场景 | 期望 |
|---|---|---|---|
| 正向 | 3 | 中英文技术教程审查、显式调用 | 应触发完整 review |
| 负向 | 3 | 从零写文章、只改错别字、单一事实核对 | 不应触发 |
| 模糊 | 1 | “帮我看看这篇文章” | 先结合上下文推断或澄清 |
其中一个用例的简化版本如下:
{
"id": "implicit-chinese-review",
"prompt": "从读者角度 review 这篇技术教程,判断是否值得收藏,命令能不能复现,没问题再告诉我是否适合发布。",
"expected_output": "Invoke the Skill for a substantive review.",
"assertions": [
"The response leads with concrete findings.",
"The response evaluates reproducibility and practical payoff.",
"The response states a publication gate."
],
"activation": "required",
"invocation": "implicit"
}
6.1 哪些字段是官方约定,哪些是本地扩展
这里必须把边界写清楚。
evals/evals.json 是 Agent Skills 创建与评估指南推荐的目录和文件。prompt、expected_output 与可选 files 属于通用测试任务信息。
本文额外使用了:
skill_version:记录这批用例对应的 Skill 版本;evaluated_on:记录检查日期;assertions:保存可观察的验收语句;activation:标记 required、forbidden 或 clarify;invocation:标记 explicit 或 implicit。
后面这些是本文评估工作流的本地元数据。不要因为它们写在 evals.json 里,就暗示 Codex 运行时会自动读取或执行。
6.2 断言要能被观察
差的断言:
输出应该高质量。
文章 review 应该有帮助。
这类句子几乎无法稳定判定。
更好的断言:
输出必须给出 Ready to publish、Ready after revision 或 Not ready。
Findings 使用 P0-P3 严重级别,并给出可执行修改。
只要求纠正错别字时,不应输出完整发布评分卡。
断言不是越细越好。若把每个标题、每个词序都锁死,Skill 只要做了合理表达调整也会被判失败。应该锁定读者真正依赖的行为和产物。
7. 先校验评估清单,再谈模型行为
手写 JSON 很容易出现重复 id、漏字段或只覆盖正向案例。于是我增加了第二个确定性脚本:
scripts/validate-evals.mjs
它检查:
skill_name、版本和日期格式;- 至少三个用例;
- id 唯一;
- prompt、expected output 和 assertions 非空;
- required、forbidden、clarify 三类触发覆盖;
- explicit、implicit 两种调用覆盖。
命令:
node .agents/skills/review-technical-article/scripts/validate-evals.mjs --json
实际结果:
{
"ok": true,
"cases": 7,
"errors": [],
"note": "Schema and coverage only; model behavior was not executed."
}
最后这句 note 不是客套话,而是最重要的边界。
校验脚本只证明“评估清单结构符合本文约定”。它没有向 Codex 发送 7 个任务,也没有判断输出是否满足断言。
8. 真正的行为评估需要一组对照
图 3:Codex Skill 评估闭环;正向、负向和模糊用例分别在当前 Skill 与基线的干净上下文中运行,再经过断言、脚本与人工审查
一轮完整评估应该这样做:
- 固定要测试的 Skill 版本和
evals.json。 - 为每个 case 创建一个全新任务,运行当前 Skill。
- 再创建另一个全新任务,在不加载该 Skill 或使用旧版 Skill 的条件下运行同一输入。
- 保存输出、工具调用、脚本结果和失败信息。
- 检查可机械判定的 assertions。
- 人工 review 事实质量、读者价值和风险。
- 记录当前版本比基线好在哪里,又引入了什么副作用。
为什么一定要干净上下文?
因为 Agent 会使用当前对话里已经出现的规则和结论。若在同一任务里先解释“这个 Skill 应该怎样工作”,再测试它是否知道怎样工作,测试本身已经把答案泄露给了模型。
8.1 把每次运行保存成可以比较的证据
不要只在聊天窗口里凭印象对比。先在 Skill 目录旁边建立独立工作区:
review-technical-article-workspace/
└── iteration-1/
└── implicit-chinese-review/
├── with_skill/
│ ├── output.md
│ └── notes.json
└── baseline/
├── output.md
└── notes.json
当前版本在一个全新任务里显式运行:
这是一次独立 eval run,不要修改 Skill 或输入文章。
使用 $review-technical-article。
测试任务:从读者角度 review 输入教程,判断是否值得收藏、
命令能否复现,以及是否适合发布。
输入:<article-path>
保存:<workspace>/iteration-1/implicit-chinese-review/with_skill/output.md
同时记录:
- 是否加载了目标 Skill
- 执行了哪些确定性脚本
- 脚本退出码
- 未能核对的事实
基线要在另一个全新任务里运行同一段测试任务。最稳妥的做法是使用一个不包含当前 Skill 的独立测试目录,并保持输入文章、项目规则和验收条件一致;若比较旧版本,则把旧 Skill 快照放在另一个隔离目录中。
不要让同名的新旧 Skill 同时处于一个发现范围。OpenAI 当前文档明确说明,同名 Skill 不会自动合并,选择器里可能同时出现两个,测试就无法确认究竟加载了哪一份。
如果当前环境无法保证隔离,就把结果标成“baseline 未运行”,不要用同一任务里的第二次回答代替基线。
8.2 触发评估和输出评估是两件事
一个 Skill 可能触发正确,但输出很差;也可能输出模板漂亮,却在不该出现时频繁抢任务。
| 维度 | 观察什么 | 失败例子 |
|---|---|---|
| 触发召回 | 正向任务是否加载 | 明确要求技术 review 却没使用 Skill |
| 触发精度 | 负向任务是否保持安静 | 只改错别字却跑完整审查 |
| 边界处理 | 模糊任务是否先推断或澄清 | 没有文章内容就给发布结论 |
| 输出合同 | 必要结构是否出现 | 没有严重级别和发布门槛 |
| 实际收益 | 相比基线是否减少遗漏 | 模板更长,但事实核对没有改善 |
因此,description 的评估不能只看“能不能触发”。它同时要控制召回率和误触发率。
如果 Skill 准备跨入口或跨模型使用,还要把运行环境作为评估变量,而不是把所有结果混在一起:
| 记录维度 | 示例 | 为什么要记 |
|---|---|---|
surface | Codex App、CLI、IDE、Claude Code | 不同入口的发现、权限和工具可能不同 |
model | 实际准备支持的模型和推理设置 | 同一个 description 的召回稳定性可能变化 |
runtime | Windows、macOS、Linux、容器 | 路径、命令和依赖可用性不同 |
skill_revision | Git commit、版本或目录快照 | 避免结果对应不到实际文件 |
trigger_result | invoked、skipped、clarified | 单独评价是否正确召回 |
output_result | assertions、人工评分、失败原因 | 单独评价输出是否真的改善 |
Anthropic 的当前建议是至少准备三个真实评估,并在计划使用的模型上分别测试;OpenAI 则强调用测试 Prompt 检查 description 的触发行为。两边的方法可以互相借鉴,但目前没有一个 evals.json 会被 Codex 和 Claude 自动用同一种 runner 执行。通用的是用例和证据组织方式,产品调用与隔离仍需要各自实现。
8.3 本文诚实停在哪里
本文已经完成:
- 7 个评估任务的设计;
- 正向、负向、模糊和显式、隐式覆盖;
- manifest 结构与覆盖校验;
- Skill 目录快速验证;
- 两个脚本的真实成功和失败运行。
本文没有声称完成:
- 7 个 case 的当前 Skill 与基线双跑;
- 自动判断全部行为 assertions;
- 跨 Codex App、CLI、IDE 的一致性比较;
- 跨 Codex 与 Claude Code、Claude API 的运行对照;
- 不同模型或推理设置下的统计稳定性。
写下“尚未验证”比给一张虚假的全绿表更有价值。下一轮维护可以从这里继续,而不是误以为质量问题已经被证明不存在。
9. agents/openai.yaml:把调用策略显式化
第 11 篇已经生成了界面元数据:
interface:
display_name: "技术文章 Review"
short_description: "Review technical tutorials for accuracy and practical value"
default_prompt: "请使用 $review-technical-article 审查这篇技术文章..."
这一篇增加:
policy:
allow_implicit_invocation: true
OpenAI 当前的 Skills 文档支持用这个策略控制隐式调用。默认值是 true,所以这里即使不写也能隐式触发;显式写出,是为了让维护者看到这是一个经过判断的策略,不是遗忘。
为什么本文保持 true?
- 这个 Skill 只读文章和资料;
- 脚本也是只读检查;
- 即使误触发,主要代价是输出变长,而不是修改外部系统;
- 用户经常会自然地说“review 一下是否适合发布”,隐式召回有实际价值。
如果 Skill 会删除文件、发布内容、发消息、操作付费资源或修改远程数据,我会优先考虑:
policy:
allow_implicit_invocation: false
这样它仍可被显式调用,但不会只因为一句含糊描述就自行启动高影响工作流。
9.1 从 GitHub 安装 Skill 前,先把它当软件审计
隐式调用只是运行阶段的一层控制。第三方 Skill 本身可以携带脚本、外部链接和工具指令;Plugin 还可能包含 MCP 配置、commands、hooks 与其他可执行入口。仓库公开不等于代码已经替你审计过,star 数也不能代替权限检查。
我会在安装前完成这份最小检查:
- 阅读完整的
SKILL.md,确认描述的任务与实际步骤一致。 - 检查
scripts/、依赖清单和所有外部 URL,特别留意网络请求、凭据读取、文件删除与远程写入。 - 若是 Plugin,继续检查
.codex-plugin/plugin.json、MCP、hooks、commands 和权限声明,不能只看skills/。 - 核对每个目录的许可证。一个仓库可以同时包含开源与仅源码可见的内容。
- 固定到可追踪的 commit 或 tag,不依赖会持续变化的远程默认分支。
- 先在测试仓库、最小权限和 sandbox 中运行,再逐步开放网络、凭据和写权限。
Anthropic 官方把 Skill 明确视为需要像软件一样审计的能力;OpenAI 的 Plugin 结构也说明一个分发包可以携带的不只是说明文件。对社区仓库,正确姿势不是拒绝使用,而是把来源、版本、代码和运行权限一起纳入验收。
10. 版本化:记录变化,不假装运行时懂 SemVer
本文在评估清单里使用:
{
"skill_name": "review-technical-article",
"skill_version": "1.1.0",
"evaluated_on": "2026-07-15"
}
这里的 1.1.0 是维护约定:
1.0.0:第 11 篇的 instruction-only 工作流;1.1.0:增加 references、只读 scripts、eval manifest 和调用策略;- 未来若输出合同发生不兼容变化,再考虑提升 major。
它帮助我把评估结果对应到具体文件状态,但不代表 Codex 运行时会读取 skill_version 或按 SemVer 选择 Skill。
Skill frontmatter 里也记录了 metadata.version: "1.1.0",Node.js、gray-matter 和可选网络依赖则写在主文件的资源说明中。Agent Skills specification 允许客户端使用自定义 metadata,但不保证每个客户端都按相同方式解释版本,所以它仍然只是维护信息。
这里还存在一个真实的生态差异:开放规范允许 metadata,而 OpenAI 当前 Skill Creator 的作者指引倾向让 frontmatter 只保留 name 与 description。本文实测当前 Codex quick validator 可以接受 metadata.version,但为了获得最大的客户端兼容性,也可以把版本只放在 evals.json、CHANGELOG 和 Git tag 中。无论选择哪种方式,都不要让业务逻辑依赖 Agent 自动理解这个版本号。
真正可追溯还需要:
- Git commit 或可比较的文件快照;
- 测试日期和运行环境;
- 用例、输出与断言结果;
- 已知未验证项;
- 修改原因,而不只是版本号。
版本号是索引,不是证据。
11. 升级后的 SKILL.md 只增加编排,不复制实现
主文件新增的关键内容不是一大段脚本源码,而是资源使用条件:
Bundled Resources
- 遇到版本敏感、争议、安全或产品事实时,读取 source-policy.md。
- 本地 Markdown 且 Node 与依赖可用时,运行 article-preflight.mjs。
- 维护 Skill 时才运行 validate-evals.mjs。
- 正常文章 review 不加载 evals/evals.json。
工作流最前面还增加了 deterministic preflight:
1. 按 Agent Skills 约定,以 Skill 根目录的相对路径引用脚本;若命令执行器使用其他工作目录,再解析实际路径。
2. 只有用户询问发布就绪时才加 --publish。
3. 只有确实需要网络检查时才加 --remote。
4. 把脚本错误与编辑判断分开报告。
5. 脚本通过不代表文章正确或值得发布。
这段编排解决了两个问题。
第一,Codex 不需要每次重新发明命令和参数。第二,脚本结果不会混进 editorial findings,让读者误以为“路径存在”与“内容合理”拥有相同证据等级。
12. 一份可以直接交给 Codex 的升级任务
如果你已经有第 11 篇那样的最小 Skill,可以从下面这份任务开始,而不是手工逐个建文件:
请升级 .agents/skills/<skill-name>,目标是让它可以被持续验证。
先阅读现有 SKILL.md 和项目 AGENTS.md,不要覆盖已有有效规则。
需要交付:
1. references/:只放按条件加载的详细知识,并在 SKILL.md 写清读取条件。
2. scripts/:只实现确定性、重复、非交互的步骤;提供 --help、结构化输出、明确退出码和安全默认值。
3. evals/evals.json:包含正向、负向、模糊案例,覆盖显式和隐式调用。
4. 一个只校验 eval manifest 的脚本,输出中明确它没有执行模型行为。
5. agents/openai.yaml:根据副作用决定是否允许 implicit invocation。
6. 兼容性说明:在 Skill 中写明目标 Agent、运行时、依赖、网络和平台限制。
验收:
- Skill 结构校验通过。
- 每个包含验证逻辑的脚本至少验证一个成功样例和一个失败样例。
- 区分官方字段与本项目自定义评估字段。
- 所有 Skill 内部路径使用正斜杠,并验证目标运行环境具备所需依赖。
- 不把脚本通过写成事实正确,不把 manifest 通过写成行为通过。
- 给出未完成的行为基线测试清单。
这份 Prompt 的重点不是目录名,而是每个文件的职责和验收边界。
13. 本地验证记录
本文写作前实际运行了以下检查:
| 检查 | 实际结果 | 能证明什么 | 不能证明什么 |
|---|---|---|---|
| Skill Creator quick validate | Skill is valid! | frontmatter 与基本目录有效 | Skill 在真实任务中表现好 |
validate-evals.mjs --json | 7 cases,0 error | 本地清单格式和覆盖完整 | 7 个任务已被 Codex 执行 |
| 损坏的 eval manifest | 7 errors,exit 1 | 版本、日期、断言和覆盖错误可被拦截 | 模型输出质量 |
第 11 篇 --publish --json | 0 error,0 warning | 发布元数据和本地资源通过 | 文章事实全部正确 |
| 损坏 fixture 发布检查 | 4 errors,2 warnings,exit 1 | 脚本能拦住设计中的错误 | 覆盖所有 Markdown 边缘语法 |
| SVG 转 PNG | 3 张均为 2400×1350 | 静态回退图可生成 | 页面响应式布局一定无误 |
这里仍有两个残余风险。
一是 Markdown 链接语法有很多边缘情况,当前脚本面向本博客常用写法,不是完整 Markdown parser。二是外链检查受网络和站点策略影响,所以只作 warning。
这两个限制已经写进文章和脚本行为,不需要为了“全绿”而隐藏。
14. 最常见的六种失败方式
14.1 一开始就创建所有目录
空 references/、scripts/ 和 assets/ 不会增加可靠性,只会增加维护噪声。先从最小 Skill 开始,遇到具体压力再拆。
14.2 把长说明移走,却不写召回条件
SKILL.md 只写“需要时参考资料”几乎没有帮助。应该写清“文章包含时间敏感或安全相关主张时读取 source-policy.md”。
14.3 用脚本处理主观判断
正则可以找缺图,不能判断图是否帮助理解。若脚本只能靠越来越多模糊启发式猜测,任务可能应该留给 Agent 和人。
14.4 只写正向 eval
“明确点名 Skill 时能触发”是最低难度。真正影响日常体验的往往是误触发,所以负向和模糊任务不可缺少。
14.5 在同一对话里测试当前版和基线
前一次输出会给后一次提供规则和答案,结果不能作为干净对照。每个 run 都要使用独立任务。
14.6 把每个绿色结果都叫作“验证通过”
最危险的表述是:
evals.json 校验通过,所以 Skill 已验证。
更准确的是:
评估清单结构与覆盖通过;
模型行为基线测试仍待执行。
证据说到哪,结论就停到哪。
15. 45-60 分钟跟做练习
不要一上来复制本文整个目录。选择你已经使用过至少两次的 Skill,再按这个节奏升级。
0-10 分钟:找出不稳定点
记录最近两次使用里重复出现的问题:
- 哪段知识总要重新解释?
- 哪个机械检查总会漏?
- 哪种相似任务不应该触发?
- 输出里哪个字段是下游真正依赖的?
- 最终准备在哪个 Agent、入口和运行环境里使用?
只选一个最明显的问题。
10-20 分钟:决定放置位置
用图 2 的决策顺序判断:
项目规则 → AGENTS.md
任务编排 → SKILL.md
详细知识 → references/
确定性处理 → scripts/
回归任务 → evals/
外部动作 → MCP / App
写不清职责就先不要拆。
20-35 分钟:实现一个最小扩展
二选一即可:
- 增加一份只在特定条件加载的 reference;
- 增加一个只读、非交互、带
--help和退出码的 script。
同时把调用条件写回 SKILL.md。
35-45 分钟:建立评估矩阵
至少写 4 个任务:
- 一个显式正向;
- 一个自然语言正向;
- 一个相似但不应触发的负向;
- 一个信息不足的模糊任务。
每个用例至少写一条可观察断言。
若计划跨入口使用,再为用例记录 surface、model、runtime 和 Skill revision;不需要第一轮就覆盖所有组合,但必须知道这次结果属于哪一个环境。
45-60 分钟:跑失败路径并记录边界
- 让脚本处理一个合法输入;
- 故意制造一个错误输入;
- 校验 eval manifest;
- 写下哪些行为没有运行;
- 在另一个干净任务里准备基线测试。
练习完成的标准不是“目录和本文一样”,而是你能回答:
每个新增文件解决了什么问题?
它的结果能证明什么?
它不能证明什么?
16. 收藏清单
以后升级 Skill,可以按这份顺序检查:
设计
- Skill 仍然只解决一个清楚的任务。
-
SKILL.md只保留触发、编排和输出合同。 - reference 有明确读取条件,没有复制
AGENTS.md。 - script 只承担确定性、重复处理。
- 外部实时动作没有被误塞进本地脚本。
脚本
- 非交互、默认只读、有
--help。 - 输入、输出、依赖和退出码明确。
- Skill 内部路径使用正斜杠,目标环境能够提供所需依赖。
- 结构化数据走 stdout,诊断信息清楚。
- 成功样例与失败样例都实际运行。
- 网络失败与内容错误没有混为一谈。
评估
- 有正向、负向和模糊任务。
- 同时覆盖显式与隐式调用。
- 断言可以观察,不依赖模糊的“高质量”。
- 当前 Skill 与基线使用独立干净任务。
- 结果记录了 surface、model、runtime 和 Skill revision。
- 机械断言、人工判断和未验证项分别记录。
安装与共享
- 已审查
SKILL.md、scripts、外部 URL 和依赖。 - Plugin 的 MCP、hooks、commands 和权限入口也经过检查。
- 已核对许可证,并固定到可追踪的 commit 或 tag。
- 第一次运行使用测试仓库、sandbox 和最小权限。
- 已区分通用 Skill 核心与 Codex、Claude 的产品专属配置。
发布
- 官方事实有来源和核对日期。
- 本地扩展没有写成 Codex 运行时保证。
- 版本号能对应具体文件状态。
- 没有用“清单通过”代替“行为通过”。
- 最终高影响动作仍由人验收。
写在最后
第 11 篇解决的是“怎样不再反复写同一段 Prompt”。第 12 篇解决的是更长期的问题:
当这套能力开始被重复使用,
怎样知道它今天仍然按预期工作?
答案不是把 SKILL.md 写到无限长,也不是给目录塞满文件。更可持续的做法是把职责拆清楚:
references保留需要时才加载的知识;scripts提供可重复的机械证据;evals保存真实任务与回归边界;- 人工 review 负责事实、质量和风险;
- 版本记录把结论对应到具体状态。
下一篇进入第 13 篇:Plugins、MCP 与 Apps:什么时候让 Codex 连接外部工具。届时会继续用同一套判断方法,区分“给 Agent 一套可复用说明”“把能力打包分发”和“连接需要认证的实时外部系统”。
参考资料
官方与规范
- OpenAI:Build skills
- OpenAI:Build plugins
- OpenAI Skills repository
- OpenAI Plugins repository
- Anthropic:Agent Skills overview
- Anthropic:Skill authoring best practices
- Claude Code:Agent Skills in the SDK
- Anthropic Skills repository
- Agent Skills specification
- Agent Skills specification repository
- Agent Skills:Evaluating skills
- Agent Skills:Using scripts in skills
社区案例
社区仓库只用于观察目录组织和应用场景,当前路径、字段和产品行为仍以官方资料与实际运行结果为准。使用 GitHub 示例时还要逐目录核对许可证:例如 Anthropic 官方仓库中的多数 Skill 使用 Apache 2.0,但 docx、pdf、pptx、xlsx 属于 source-available,并不能因为仓库公开就统一称为开源。