D1|Skills:先给目录,点名再给正文
先别把 Skill 当成又一个 tool
读完 C 章,下一件错事是把 SKILL.md 写进 AGENTS.md,或在 ToolRouter 里给每个技能注册一个函数。技能看起来像能力,其实是 给模型读的程序:一份有名字的说明书,外加可选脚本和参考资料。它不经过 Orchestrator 的审批-沙箱流水线(除非模型随后去 exec 那些脚本)。
判断先说清楚:Skill 是渐进披露的说明书包。 第一枪模型只看见 name + description;$skill、/skills 或隐式匹配之后,才注入 SKILL.md 正文;references/ 和 scripts/ 仍要模型自己按需打开或执行。这和 B2 的 tool_search、C2 的 MEMORY.md 是同一哲学,和 C1 的 AGENTS.md 相反——AGENTS.md 未信任闸打开后每轮都在。
跨工具的文件夹约定对齐 agentskills.io。本仓库的解析器认 YAML frontmatter 的 name / description(skills/src/parser.rs:6-27, 43-47),不实现网站上的每一条扩展字段。
1. 一个文件夹,不是一篇博客
内置 skill-creator 把解剖写死了(skills/src/assets/samples/skill-creator/SKILL.md:30-44):
skill-name/
|-- SKILL.md 必填:frontmatter + 被选中后才加载的正文
|-- agents/openai.yaml 可选:UI 名、默认 prompt、调用政策
|-- scripts/ 可选:可执行帮手,优先跑而不是贴进 prompt
|-- references/ 可选:按模式再读的文档
`-- assets/ 可选:输出要用的模板/图,不当指令frontmatter 必填 name、description(:48-52)。name 最长 64(parser.rs:4)。description 决定 目录里怎么被选中,要写清何时适用、何时不要用;细节放正文或 references,不要在 description 里列完全部能力(skill-creator :24, 189-197)。
本仓库还会把系统样例装进 $CODEX_HOME/skills/.system(skills/src/lib.rs:55-76):imagegen、openai-docs、plugin-creator、skill-creator、skill-installer、review-agent。这是 System 范围的捆绑技能,不是你的项目政策。
产品侧的 hatch-pet 是同一格式的用法:用 Skill 教模型怎么孵一只宠物。本 git 树里没有这个文件夹;Pets 本体是 TUI 装饰,不准进 ToolRouter(C4 / E8)。hatch-pet 若存在,也只是说明书,不是把宠物变成工具。
没有「文件夹 + 渐进三层」会怎样?要么把 PDF 旋转脚本全文塞进每轮 prompt,要么模型永远不知道有这份技能。三层披露就是为了两者都不发生。
2. 渐进披露:目录 → 正文 → 参考,不要一次倒完
会话里先出现的是 可用技能目录 fragment(AvailableSkillsInstructions,ext/skills/src/fragments.rs:11-15):name、description、短路径。目录有 token 预算:默认约窗口的 2%,硬顶 10_000(config/src/skills_config.rs:40-42)。这是 B4 的有界注入,不是无限列表。
选中之后,HostSkillsSnapshot::load_skill_prompts 才读 SKILL.md 正文做成 SkillInstructions fragment(host_prompt.rs:60-80,extension.rs:482-499)。超长会截断并警告(:468-480)。references/ 不会自动跟进来——catalog 的 How to use 写明:主 agent 必须自己读完 SKILL.md,再按里面的路由打开需要的 reference;禁止把读说明书交给子代理(catalog_prompt.rs:10-13, 27-32)。
脚本优先执行而不是重打代码(:32)。assets 复制进产物,不当指令(skill-creator :82-88)。
[skills] include_instructions 可关掉自动目录块(skills_config.rs:36-38)。关了等于模型看不见菜单,只能靠你已经 $ 点名的结构化 UserInput::Skill。
没有目录/正文分离会怎样?几十个 MCP 式技能的全文每轮进 prompt,compact 永远在救火;或者只有 name 没有 description,隐式匹配全靠瞎猜。
3. 发现路径:repo / user / admin / system
SkillScope 四个值(protocol/src/protocol.rs:3817-3821)。根从配置层折出来(ext/skills/src/host_roots.rs:73-120):
| Scope | 典型目录 |
|---|---|
| Repo | 项目 .codex/skills;另外从 cwd 走到 git root 的 .agents/skills(:136-154) |
| User | $CODEX_HOME/skills(注释写明是兼容位置);~/.agents/skills |
| System | $CODEX_HOME/skills/.system(捆绑样例缓存) |
| Admin | 系统配置目录下的 skills/(如 /etc/codex/skills) |
排序:Repo → User → System → Admin(host_merge.rs:262-268, 244-249),同名再按 path。插件带的 skills 挂在 User 根上(host.rs:66-69),D2 再讲包装。
未信任项目:AGENTS.md / hooks 会跳过项目层;skill 根列表仍包含 disabled Project 的 .codex/skills(测试 layer_roots_preserve_scope_precedence_and_disabled_projects,host_roots_tests.rs:245-288)。发现比执行宽。不要假设「未信任 = 仓库里的技能文件不存在」;要禁某个技能,用 [[skills.config]] 的 name/path + enabled = false(skills_config.rs:18-27, 70-87)。
捆绑技能可用 [skills.bundled] enabled = false 关掉(:49-54)。
没有分层发现会怎样?要么只认 ~/.codex/skills,团队无法把技能推进 git;要么系统管理员的技能被用户目录盖掉却查不出 scope。Admin 排在最后,是「本机覆盖企业」还是「企业垫底」取决于同名去重——合并按 path 去重、再按 scope 排序,不同 path 的同名技能可以并存,点名时 plain name 必须无歧义(下一节)。
4. $skill、/skills、隐式匹配,以及怎么关掉自动选
三条进门,不要混:
显式 $name。 collect_explicit_skill_mentions 先解析结构化 UserInput::Skill,再扫文本里的 $skill-name(selection.rs:31-37)。plain 名字只在全局唯一时命中;冲突要路径。这是用户点名。
/skills。 TUI 打开技能菜单(slash_command.rs:109,slash_dispatch.rs:468-469),选中后变成结构化 mention,仍走显式注入。
隐式。 目录说明要求:任务对得上 description,模型必须用该 skill(catalog_prompt.rs:8, 25)。另一条是命令行里读到了 SKILL.md 或跑了 scripts/ 下的文件——detect_implicit_skill_invocation_for_command(skills/src/invocation.rs:26-42)。默认允许;agents/openai.yaml 里:
policy:
allow_implicit_invocation: false之后只在 $name 点名时可用,不进自动上下文(skill-creator :93-100;SkillMetadata::allows_implicit_invocation 默认 true,model.rs:23-27)。
skill-creator 写得很白:不要因为「这个技能会改生产」就推断成 explicit-only;该发现的还是发现,真正突变前再要审批(:153)。隐式关的是「别自动塞进 prompt」,不是「别走 B3」。
没有这三条通道会怎样?只有菜单的技能没人发现;只有隐式的技能会在无关任务里抢上下文。点名、目录匹配、跑脚本触发,覆盖「我知道名字 / 我描述了任务 / 我已经在用它的脚本」。
5. 目录会变:FileWatcher 推 skills/changed,当前枪仍冻住
发现不是启动时拍一张死照片。app-server 的 SkillsWatcher(app-server/src/skills_watcher.rs)用 file-watcher 递归盯可监视的 skill 根,默认 10s 节流(WATCHER_THROTTLE_INTERVAL)。事件来了先 skills_service.clear_cache(),再发 ServerNotification::SkillsChanged(线上名 skills/changed,app-server-protocol/src/protocol/common.rs 的 notification 表)。$CODEX_HOME/skills/.system 捆绑根上的抖动故意丢掉;远程 environment 不注册监视。
下一枪的技能目录 fragment 和 build_tool_router 会看见新菜单。这一枪已经冻住的 Router、已经注入的 SkillInstructions 不改写——和 B2「工具计划按 Step 重算」是同一条纪律。插座怎么扇出这条通知,见 G3。
没有「通知变、当前枪冻」会怎样?IDE 长连接下你加了一个 skill,正在飞的那一枪按新目录点名;或者改了文件却永远要重启 daemon 才看得到。热更新是下一枪的事,不是改过去。
6. 和 AGENTS.md、Memories、工具的边界
| 每轮都在? | 进 git? | 典型内容 | |
|---|---|---|---|
| AGENTS.md | 是(未信任除外) | 应该 | 测试命令、禁区 |
| Skill | 否,选中才注入正文 | 可以(repo)或本机(user) | 某一类任务的程序 |
| Memories | 否,还要 feature | 不应该 | 个人偏好 |
| Tool | call 才跑 | 代码 | 副作用 |
长流程、分模式的手册放 Skill 的 references/,不要放进 AGENTS.md(C1)。Skill 正文里的「请不要 sudo」仍然挡不住 argv——硬拦截继续 ExecPolicy(B3)。脚本跑起来才进 Orchestrator。
7. 结语:带走一句话
Skill 是按 agentskills 文件夹约定写的说明书包:目录里只暴露 name+description,点名或匹配后再加载 SKILL.md;目录变了推 skills/changed,当前枪仍冻住——它扩展的是模型怎么做事,不是 ToolRouter 里又多了一个函数。