openinterpreter Skills 体系中的 agents/openai.yaml:interface、dependencies、policy 字段全解与源码级校验规则
本篇基于仓库中 skill-creator 技能自带的参考文档 openai_yaml.md,完整讲解技能目录下 agents/openai.yaml 这一“面向机器/harness 而非面向 Agent”的产品级配置文件的三大块字段(interface、dependencies、policy)的语义、约束与写法规范,并结合 codex-rs/skills 的 Rust 解析源码与仓库内真实技能样例,说明每个字段在校验失败时会发生什么、以及如何在创建技能时用配套脚本确定性地生成该文件。读完你可以独立为任意技能编写、审查和调试 openai.yaml,并准确预判哪些字段会被运行时静默忽略。
openai.yaml 的角色定位:写给 harness 看的扩展配置
原文档开头给出了一句话定位:
agents/openai.yamlis an extended, product-specific config intended for the machine/harness to read, not the agent. Other product-specific config can also live in theagents/folder.
也就是说,它和每个技能必需的 SKILL.md 分工完全不同:
SKILL.md的 frontmatter(name+description)是技能触发的唯一依据,由模型读取用于决定是否激活技能;其解析与长度约束实现在 parser.rs(例如name最长 64 字符、description必填)。agents/openai.yaml则是可选的 UI 元数据与策略声明,供界面(技能列表、chips、默认提示词)和运行框架读取,正文内容不进入模型上下文。skill-creator 技能的 SKILL.md 也把agents/描述为 "UI metadata for skill lists and chips",并要求"生成取值前先阅读references/openai_yaml.md并遵循其中的描述与约束"。
从源码结构看,这份文件的解析入口是 interface.rs 中的 SkillInterfaceFile 结构体,它用 serde 从 agents/openai.yaml 反序列化出 interface 子树;而 dependencies、policy 子树对应的数据结构则定义在 model.rs。
完整配置示例(可直接复制)
原文档给出的完整示例如下,它覆盖了当前文档支持的全部字段,可直接作为编写新技能时的模板:
interface:
display_name: "Optional user-facing name"
short_description: "Optional user-facing description"
icon_small: "./assets/small-400px.png"
icon_large: "./assets/large-logo.svg"
brand_color: "#3B82F6"
default_prompt: "Optional surrounding prompt to use the skill with"
dependencies:
tools:
- type: "mcp"
value: "github"
description: "GitHub MCP server"
transport: "streamable_http"
url: "https://api.githubcopilot.com/mcp/"
policy:
allow_implicit_invocation: true
三个顶层键全部是可选的。仓库内现有样例也印证了这一点:skill-creator 自己的 openai.yaml 只写了 interface 的四个字段,没有任何 dependencies 或 policy。
interface:面向 UI 的展示与调用元数据
字段语义总表
| 字段 | 语义 | 约束(文档 + 源码) |
|---|---|---|
interface.display_name |
人类可读标题,显示在 UI 的技能列表和 chips 中 | 字符串加引号;超过 64 字符会被忽略(见下节) |
interface.short_description |
人类可读的简短 UI 描述,供快速扫读 | 文档建议 25–64 字符;源码硬上限 1024 字符 |
interface.icon_small |
小图标资源路径,相对于技能目录 | 必须落在技能目录的 assets/ 下;建议写成 ./assets/... |
interface.icon_large |
大 logo 资源路径,相对于技能目录 | 同上 |
interface.brand_color |
用于 UI 强调色(如徽章)的十六进制颜色 | 必须是 #RRGGBB 格式,否则被忽略 |
interface.default_prompt |
调用该技能时插入的默认提示词片段 | 建议为一句话,且必须显式以 $skill-name 形式提及技能 |
其中 default_prompt 的写法要求最容易被忽略:文档明确要求"生成一个有帮助的、简短(通常一句话)的示例起始提示词,且必须显式地以 $skill-name 提及该技能",并给出示范句式 "Use $skill-name-here to draft a concise weekly status update."。仓库中两个系统技能的真实写法完全遵循这一规范,例如 imagegen 的 openai.yaml 中:
default_prompt: "Use $imagegen to make or edit an image for this project."
源码中的实际校验规则:逐字段容错而非整体失败
文档只给出了"字段描述",而字段写错时运行时到底怎么处理,则由 interface.rs 的 resolve_skill_interface 决定。结合源码与测试 interface_tests.rs,可归纳出以下可验证的规则:
- 空白规范化:所有字符串字段(
display_name、short_description、default_prompt)会先经resolve_str做split_whitespace().join(" ")处理,即" Demo skill "会被规范化为"Demo skill"(测试resolves_local_interface_fields_and_assets中有此断言)。 - 长度上限:
display_name超过 64 字符(MAX_NAME_LEN)或short_description/default_prompt超过 1024 字符(MAX_DESCRIPTION_LEN,见 interface.rs#L10-L11)时,该字段被丢弃并记录tracing::warn!,而不是让整个文件解析失败。测试drops_invalid_fields_without_discarding_valid_interface_fields明确验证了"坏字段单独丢弃、好字段保留"的行为。 brand_color严格校验:resolve_color_str(interface.rs#L180-L197)要求值恰为 7 个字节且形如#+ 6 位十六进制(即#RRGGBB),blue这类颜色名会被忽略并告警。short_description的 25–64 字符只是 UI 建议:它不是硬校验,硬校验是 1024 上限;真正强制 25–64 区间的是生成脚本(见"实践建议"一节)。
图标路径解析:必须锚定在 assets/ 内
icon_small/icon_large 的解析规则是这份文档中最"隐蔽"的约束,完整逻辑在 resolve_asset_path(interface.rs#L77-L122):
- 路径必须相对于技能目录;绝对路径会被忽略并告警(测试
rejects_absolute_asset_paths覆盖)。 - 路径组件会被归一化(
./前缀被剥掉),归一化后第一段必须是assets,否则会收到 "icon path must be under assets/" 的告警并被丢弃。这也解释了为什么文档建议"默认使用./assets/,把图标放在技能的assets/文件夹里"。 - 含
..的路径在普通技能(SkillInterfaceAssetPolicy::LocalOnly)中一律被拒绝;只有当技能属于某个插件、且运行时以PluginShared策略解析时,..才被允许——但归一化后仍必须落在插件根目录的assets/之内(resolve_plugin_shared_asset_path,interface.rs#L124-L148;正反用例见 interface_tests.rs#L118-L165)。 - 全部字段都被判无效(或本来就为空)时,
resolve_skill_interface返回None,即该技能没有 interface 元数据,UI 侧回退到基于SKILL.md的展示。
dependencies.tools:声明 MCP 依赖
文档对 dependencies.tools[] 各字段的定义是:
type:依赖类别,目前仅支持mcp("Onlymcpis supported for now");value:工具或依赖的标识符;description:人类可读的依赖说明;transport:当type为mcp时的连接类型,示例中使用"streamable_http";url:当type为mcp时的 MCP 服务器 URL。
对照 model.rs,运行时结构 SkillDependencies { tools: Vec<SkillToolDependency> } 中的 SkillToolDependency 还包含一个 command 可选字段,与 transport/url 并列。从源码结构看,这是为命令式(本地命令)依赖预留的字段位,但文档口径明确当前仅 mcp 类别受支持,因此在 openai.yaml 中手写依赖时,仍应以文档示例的 type: "mcp" + transport + url 组合为准。
dependencies 整体也是可选的:仓库中 skill-creator、imagegen、openai-docs 等系统技能的 openai.yaml 均未声明依赖,对应 model_tests.rs 中 dependencies: None 的常见形态。
policy.allow_implicit_invocation:控制技能是否默认注入上下文
policy.allow_implicit_invocation 是文档中唯一的策略字段,语义为:
- 设为
false时,该技能默认不会被注入到模型上下文,但仍可通过$skill显式调用; - 默认为
true(不写policy或留空即视为允许隐式调用)。
源码层面这一默认值由 SkillMetadata::allows_implicit_invocation 实现(model.rs#L22-L36):policy.allow_implicit_invocation 是 Option<bool>,取值时 unwrap_or(true)。
仓库中有一个现成的 false 用法样例——review-agent 技能的 openai.yaml:
interface:
display_name: "Review Agent"
short_description: "Find actionable bugs in code changes"
default_prompt: "Use $review-agent to review the requested code changes and return actionable findings."
policy:
allow_implicit_invocation: false
这是一个典型场景:代码评审技能不希望每次普通对话都被动注入,只应在用户明确 $review-agent 时激活。
补充一点源码事实:SkillPolicy 结构体(model.rs#L62-L68)中还解析了一个 products 产品限定字段,但源码中的 TODO 注释表明目前只做了解析与存储,尚未在技能选择/注入环节强制执行,且该字段未出现在参考文档中,编写时不建议依赖它。
顶层书写规范:引号与键名
文档"Top-level constraints"给出三条全局规则,写 YAML 时应逐条遵守:
- 所有字符串值必须加引号(Quote all string values)。
- 键名保持不加引号(Keep keys unquoted)。
interface.default_prompt:基于该技能生成一句简短(通常一句话)的示例起始提示词,且必须显式以$skill-name提及技能,例如 "Use $skill-name-here to draft a concise weekly status update."。
之所以强制加引号,与技能生态的现实有关:parser.rs 对 SKILL.md frontmatter 专门做了"修复式"解析来兼容第三方技能中未加引号的散文化描述;而 openai.yaml 作为机器读取的配置文件,规范写法可以避免依赖这类容错逻辑。
解析链路:这份文件如何被读入与消费
把文档字段与源码串起来,一条完整的消费链路是:
- 嵌入与安装:系统技能(含本文引用的 skill-creator 等样例)通过
include_dir!编译进二进制,启动时由install_system_skills写入CODEX_HOME/skills/.system(lib.rs#L49-L95),因此agents/openai.yaml会随技能一起落盘。 - 反序列化:
interface子树 →SkillInterfaceFile(serdeDeserialize);dependencies、policy→SkillDependencies、SkillPolicy(model.rs)。 - 逐字段校验与路径解析:
resolve_skill_interface对每个字段独立校验,坏字段告警丢弃(前文已述),图标路径归一化为绝对路径后写入SkillInterface。 - 消费:
SkillMetadata聚合 name/description/interface/dependencies/policy(model.rs#L6-L20),allows_implicit_invocation决定技能是否参与默认上下文注入,interface供 UI 展示层使用。
仓库内真实样例对照
以下样例均在 codex-rs/skills/src/assets/samples 下,可对照字段用法:
| 技能 | 文件 | 用到的字段 | 说明 |
|---|---|---|---|
| skill-creator | agents/openai.yaml | display_name、short_description、icon_small、icon_large |
最小形态:无 default_prompt、无 policy |
| imagegen | agents/openai.yaml | interface 全五字段 | default_prompt 用 "Use $imagegen to ..." 规范句式 |
| openai-docs | agents/openai.yaml | interface 全五字段 | 展示了较长的 default_prompt 写法 |
| review-agent | agents/openai.yaml | interface + policy |
唯一使用 allow_implicit_invocation: false 的样例 |
可以看出,文档示例中的 dependencies 块在内置样例里尚未被使用,它更多面向需要声明 MCP 服务器依赖的第三方技能。
实践建议:用配套脚本确定性生成 openai.yaml
skill-creator 技能明确要求:display_name、short_description、default_prompt 应由 Agent 读完技能内容后生成,再作为 --interface key=value 参数传给脚本确定性生成,而不是手写 YAML。对应 SKILL.md 中的流程:
# 初始化技能时一并生成(含 agents/openai.yaml)
scripts/init_skill.py my-skill --path "${CODEX_HOME:-$HOME/.codex}/skills" \
--interface display_name="My Skill" \
--interface short_description="One line within 25 to 64 chars" \
--interface default_prompt="Use $my-skill to ..."
# 或对已有技能目录单独(重新)生成
scripts/generate_openai_yaml.py <path/to/skill-folder> --interface key=value
生成脚本 generate_openai_yaml.py 值得细看两点:
- 它只接受白名单内的 interface 键(
ALLOWED_INTERFACE_KEYS:display_name、short_description、icon_small、icon_large、brand_color、default_prompt,见 generate_openai_yaml.py#L40-L47),与文档字段一一对应; - 当没有显式传入
short_description时,脚本会按 25–64 字符区间自动伸缩兜底文案(generate_openai_yaml.py#L74-L101)——这正是文档"25–64 chars"建议在工具链中的落地位置。
配套约定还包括:
- 图标等其他可选字段:SKILL.md 要求"仅当用户显式提供时才写入 icons、brand color",避免臆造资产路径导致图标解析告警;
- 更新时保持同步:修改
SKILL.md后应校验agents/openai.yaml是否仍然匹配,过期则重新生成; - 验证:整个技能目录可用 skill-creator 自带的
scripts/quick_validate.py做基础校验(frontmatter 格式、必填字段、命名规则),openai.yaml本身则依赖上文所述的运行时逐字段校验与tracing::warn!日志来暴露问题——调试图标不显示、颜色不生效时,优先检查告警日志中 "ignoring interface.xxx" 的条目,按提示修正格式即可。
小结
agents/openai.yaml 是技能体系中唯一"面向机器与 UI"的配置文件:interface 决定技能在列表、chips 中如何展示以及调用时插入什么默认提示词;dependencies.tools 用于声明(当前仅支持的)MCP 依赖;policy.allow_implicit_invocation 控制技能是否默认注入上下文。它的字段约束既有文档层的书写规范(值加引号、键不加引号、$skill-name 句式),也有源码层的硬性校验(64/1024 字符上限、#RRGGBB 颜色格式、assets/ 路径锚定、逐字段容错丢弃)。理解了 openai_yaml.md 与 interface.rs、model.rs 两侧的对应关系,就能保证写出的 openai.yaml 既符合文档规范,又不会在运行时被静默忽略。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00