数据截至 (上游 commit 69ef37e9424c)
第 3 章 · 客户端集成生命周期
第 1 章讲“文件长什么样”,第 2 章讲“怎么解析校验”。本章讲一个 agent 产品怎么真正支持 Skills——从扫描磁盘到把指令塞进模型、再到全程不丢失它。来源:
docs/client-implementation/adding-skills-support.mdx。规范只定义文件内部,不规定 skill 放在哪、怎么加载;这些落地决定都在客户端,本章就是这些决定的“最佳实践集合”。
3.0 五步全景
① 发现 ② 解析 ③ 披露 ④ 激活 ⑤ 上下文管理
扫目录 抽frontmatter name+desc 进 任务匹配→读 防压缩 / 去重 /
找 SKILL.md 存 location 系统提示 整篇正文进上下文 (可选)子 agent
───────────▶ ───────────▶ ──────────────▶ ──────────────▶ ──────────────▶
tier 1 准备 tier 1 投放 tier 2 维持 tier 2/3
两个决定整套实现差异的变量(docs/client-implementation/adding-skills-support.mdx:9-13):
- skill 住在哪? 本地 agent 扫文件系统;云端/沙箱 agent 得换机制(API、远程仓库、内置资产)。
- 模型怎么拿到内容? 模型能读文件就直接读
SKILL.md;不能就给它一个专用工具或直接注入。
3.1 第一步:发现 skill
扫哪些目录:.agents/skills/ 约定
本地 agent 通常至少扫两个作用域:项目级(相对工作目录)和用户级(相对家目录)。每个作用域里又常扫两类路径(docs/client-implementation/adding-skills-support.mdx:45-54):
| 作用域 | 路径 | 用途 |
|---|---|---|
| 项目 | <project>/.<your-client>/skills/ | 你客户端的原生位置 |
| 项目 | <project>/.agents/skills/ | 跨客户端互通 |
| 用户 | ~/.<your-client>/skills/ | 你客户端的原生位置 |
| 用户 | ~/.agents/skills/ | 跨客户端互通 |
关键点:.agents/skills/ 已成为跨客户端共享的事实约定——扫它意味着别的兼容客户端装的 skill 你也能看见,反之亦然(:54)。规范本身不规定位置,这个约定是生态自发形成的。很多实现还会额外扫 .claude/skills/,因为现存大量 skill 装在那(:56-58)。
扫什么:含 SKILL.md 的子目录
在每个 skills 目录里找“正好叫 SKILL.md 的文件所在的子目录”(:62-73)。实践规则:跳过 .git/、node_modules/;可选尊重 .gitignore;设深度/数量上限(如最深 4–6 层、最多 2000 目录)防失控扫描(:75-79)。
重名冲突:项目级压用户级
两个 skill 同名时要有确定的优先级。通行约定:项目级覆盖用户级(:82-87)。同作用域内部的冲突,first-found 或 last-found 都行,选一个保持一致,并打 warning 告诉用户某个 skill 被遮蔽了。
信任门槛(安全关键)
项目级 skill 来自“正在处理的仓库”,可能是刚 clone 的不可信开源项目。规范建议:把项目级 skill 的加载挂在“用户已信任该目录”的检查上(docs/client-implementation/adding-skills-support.mdx:89-91)。否则一个恶意仓库就能往 agent 上下文里静默注入指令——这是 skill 作为“可执行指令载体”的天然攻击面。
云端 / 沙箱的不同处理
沙箱里没有用户本地文件系统(:93-101):项目级 skill 随代码进沙箱、最好办;用户级/组织级要从外部源(配置仓库、上传)provision;内置 skill 打包成部署资产。
3.2 第二步:解析(以及“宽松校验”)
解析逻辑和第 2 章的 parse_frontmatter 一致:找两道 ---、parse 中间的 YAML、其余 strip 作正文(docs/client-implementation/adding-skills-support.mdx:108-115)。但生产集成多了两层现实容忍:
容错坏 YAML
最常见的坑是未加引号、值里带冒号(:117-126):
# 技术上非法的 YAML——冒号破坏解析
description: Use this skill when: the user asks about PDFs
建议加个 fallback:把这类值用引号包起来、或转成 YAML block scalar 再重试,低成本提升跨客户端兼容。
宽松校验:warn 而非拒(关键权衡)
这里和第 2 章的 skills-ref 有意不同。skills-ref 是严格校验器;而生产客户端建议宽 松(docs/client-implementation/adding-skills-support.mdx:128-141):
| 问题 | 严格校验器(skills-ref) | 生产客户端建议 |
|---|---|---|
| name 不匹配目录名 | 报错 | warn,照样加载 |
| name 超 64 字符 | 报错 | warn,照样加载 |
| description 缺失/空 | 报错 | 跳过(没描述就没法披露) |
| YAML 完全解析不了 | 报错 | 跳过 |
理由:别让“无伤大雅的瑕疵”挡住加载,尤其要兼容别家客户端写的 skill。规范自己点明:严格约束在 spec,这里是“为兼容性故意放松”(:139-141)。
存什么
每条 skill 记录至少三字段:name、description、location(指向 SKILL.md 的绝对路径),按 name 建内存索引(:144-153)。正文可以发现时就存(激活快)或激活时再读(省内存、能拾取文件变更)。
3.3 第三步:披露(tier 1)
把每个 skill 的 name/description/(可选)location 拼成目录清单塞进上下文(docs/client-implementation/adding-skills-support.mdx:159-184)。格式 XML/JSON/列表都行——和第 2 章 to_prompt 同源:
<available_skills>
<skill>
<name>pdf-processing</name>
<description>Extract PDF text, fill forms, merge files. Use when handling PDFs.</description>
<location>/home/user/.agents/skills/pdf-processing/SKILL.md</location>
</skill>
</available_skills>
要点:
location双重作用:既让模型能 file-read 激活,又给它解析相对路径(如scripts/x.py)的基准(:182)。- 放哪里:系统提示里一个小节(最简单、最通用),或塞进专用激活工具的描述(系统提示更干净)(
:186-194)。 - 过滤:被禁用/无权限/选择退出模型激活的 skill,直接不列,别列出来再在激活时拦——免得模型白白浪费回合(
:220-228)。 - 没有 skill 时:整块清单和说明都省掉,别塞空的
<available_skills/>(:230-232)。
3.4 第四步:激活(tier 2)
主力是“模型自己判断”
绝大多数实现不在 harness 侧做关键词匹配,而是让模型读完目录自己决定哪个 skill 相关(docs/client-implementation/adding-skills-support.mdx:238-240)。两种落地:
- file-read 激活:模型用标准读文件工具读
SKILL.md,零额外基础设施(:244)。 - 专用工具激活:注册个
activate_skill(name)工具返回内容。模型不能直接读文件时必需;能读时也有好处——可控制返回内容、包结构化标签、列出资源、做权限/分析(:246-256)。建议把工具的name参数约束成合法 skill 名的 enum,防模型瞎编(:254-256)。
用户也能显式激活
常见是 slash 命令 / mention 语法(/skill-name 或 $skill-name),由 harness 拦截直接注入,模型不用自己动手激活(:258-262)。具体语法由客户端自定。
模型收到什么:全文 vs 仅正文
两种都可行(:264-272):
- 全文(含 frontmatter):file-read 激活的自然结果;
compatibility等字段在激活时可能有用。 - 仅正文(剥掉 frontmatter):用专用工具的实现多数这么做。
结构化包裹
用专用工具时,建议把内容包进识别标签,并列出(但不预读)bundled 资源(docs/client-implementation/adding-skills-support.mdx:274-308):
<skill_content name="pdf-processing">
... SKILL.md 正文 ...
Skill directory: /home/user/.agents/skills/pdf-processing
<skill_resources>
<file>scripts/extract.py</file>
<file>references/pdf-spec-summary.md</file>
</skill_resources>
</skill_content>
好处:模型能区分 skill 指令与普通对话;harness 能在压缩时识别并保护;资源被“摆出来”但不急着读。
权限白名单(易踩的体验坑)
如果 agent 有文件访问权限系统,要把 skill 目录加白名单,否则每读一个 bundled 脚本都弹确认框,带资源的 skill 完全没法用(:310-312)。
3.5 第五步:全程管理 skill 上下文
防压缩(静默退化的隐患)
如果 agent 会在上下文满时截断/摘要旧消息,要把 skill 内容豁免出去(docs/client-implementation/adding-skills-support.mdx:316-326)。skill 指令是“持久行为指引”,中途被压没不会报错,模型继续跑但悄悄丢了专长——最阴险的失败。可用 §3.4 的结构化标签来识别并保护。
去重 + (可选)子 agent
- 去重:跟踪本会话已激活的 skill,重复激活就跳过,避免同一指令出现多份(
:328-329)。 - 子 agent 委派(高级、部分客户端):把 skill 放进独立子会话跑,完事只把摘要带回主对话——适合复杂工作流(
:331-335)。
3.6 代码地图
本章源在文档而非代码;锚点便于 grep:
| 主题 | 文件 | 锚点 |
|---|---|---|
| 五步生命周期总览 | docs/client-implementation/adding-skills-support.mdx | “progressive disclosure” 表(:22-30) |
| 扫描目录约定 | docs/client-implementation/adding-skills-support.mdx | “Where to scan” 表(:45-54) |
| 重名冲突 | docs/client-implementation/adding-skills-support.mdx | “Handling name collisions”(:82-87) |
| 信任门槛 | docs/client-implementation/adding-skills-support.mdx | “Trust considerations”(:89-91) |
| 坏 YAML 容错 | docs/client-implementation/adding-skills-support.mdx | “Handling malformed YAML”(:117-126) |
| 宽松校验 | docs/client-implementation/adding-skills-support.mdx | “Lenient validation”(:128-141) |
| 模型自主激活 | docs/client-implementation/adding-skills-support.mdx | “Model-driven activation”(:238-256) |
| 防压缩 | docs/client-implementation/adding-skills-support.mdx | “Protect skill content…”(:318-326) |