Skip to content

3.10 技能 Skill

本章概览:技能(skill)能力族发现可复用的任务特定指令,并经 provider 中立的目录与加载器暴露给模型。本章拆解 ctx.skills 提供方注册表、host+per-scope 分层合并、发现快照的完整性语义、调用策略(invocation policy)、模型侧 skill 工具与目录生命周期。

概述 / 定位

skill 是"可复用的任务特定指令",位于核心控制脊柱之外:ctx.skills 是纯提供方注册表,不关心技能来自本地文件、嵌入插件数据还是 HTTP 后端;模型可见的目录与加载工具由消费端 dsh-tool-skill 提供。能力族四包拆分:dsh-skill(Service Definition,ctx.skills)、dsh-skill-filesystem(本地提供方)、dsh-skill-badge(可选打包徽章技能)、dsh-tool-skill(Consumer,注册在 ctx.tools)。技能是可选的指令而非会话事件,因此其词汇在子系统页而非 core。

提供方注册表

SkillProvider 只有两个方法:list(options) 返回候选(候选数组是"发现完整"的简写;显式不完整发现返回 { candidates, complete: false } 观察),get(candidate, options) 加载完整技能体。注册是同步的,远程初始化、认证与发现放在 awaited list() 内。registerProvider(create) 的同步工厂收到注册作用域的控制对象:control.signal 在注册失败或释放时 abort;control.invalidate() 仅当该确切注册仍存活时清除已完成目录并通知消费者,迟到的回调不能影响同名替换者。注册即副作用,返回精确的 Cordis disposer。

出处:packages/skill/skill/README.mddeepseek-harness-src/packages/skill/skill/README.md

分层合并:global → scope 链

注册表是 host+per-scope 分层(tools 注册表在 dsh-scope 上建立的形状):一次注册落入调用上下文 scope 的层——宿主行与仓库插件进全局(global)层,agent preset 常驻组合挂载的插件进该 preset 层;提供方名按层唯一,而非进程全局。一次读取合并全局层与查看 scope 的链:最近层的同名技能直接获胜(遮蔽更远层),rank(数字越小越优先)只在同一层内决定重复。层内重复名按 rank → 提供方注册序 → 提供方本地序解析,摘要按名排序。发现缓存以解析出的 scope 链为键,所以 scope 重新挂父(空会话重组)无需注册表变更即可被下次读取看到。

发现快照的完整性

snapshot() 返回 SkillCatalogSnapshot { skills, complete }complete 为 true 仅当每个注册提供方都完成发现且无并发目录修订。被拒绝的 list() 记录并从未完整观察中省略;显式不完整观察的候选仍可直接加载,但使聚合快照不完整且不可缓存;一次目录修订使在途发现重试一次,第二次修订则返回最新候选且不完整不缓存——持续失效的提供方无法独占调用者。消费端据此保留 last-good 过滤目录并在下一个请求边界重试。

失效通知:提供方或运行时贡献注册/释放、活动提供方调用 invalidate() 后发出无 diff 的 skills/change(emit)事件——消费者各自以自身 lookup options 重取 snapshot(),监听器失败被包含、不能否决注册表变更。

调用策略:四组合

SkillInvocationPolicy 是两个独立正布尔 modelInvocableuserInvocablectx.skills.list() 保留全部四种组合:

modelInvocableuserInvocable语义
truetrue模型目录与人类命令都含
truefalse仅模型
falsetrue仅人类(disable-model-invocation
falsefalse两者皆否,仅受信任的 ctx.skills.get() 调用者可加载

本地提供方读取精确的 frontmatter 键 disable-model-invocationuser-invocable,缺省均 true;非法(非布尔、驼峰拼写)的调用值把整个技能从发现中丢弃——策略 fail closed,因为忽略无效数据可能把技能暴露到禁用表面。ctx.skills.get() 是策略中立的加载原语,每个面向模型或用户的消费方必须在自己的边界强制匹配的谓词。

本地提供方与打包提供方

dsh-skill-filesystem 按 rank 扫描根:project-dsh<projectRoot>/.dsh/skills,rank 100)→ project-agents<projectRoot>/.agents/skills,200)→ customConfig.customSkillDirs,300)→ user-dsh<dshHome>/skills,400)→ user-agents<agentsHome>/skills,500)→ bundled(配置 bundledSkillDir 时,600)。项目根 = 最近含 .git 的祖先;有 ctx.fs 时经文件系统服务探测 .git,远程/沙箱工作区不回落宿主边界。格式:单层目录包 <name>/SKILL.md 或扁平 <name>.md(嵌套 **/SKILL.md 不发现),名字必须 kebab-case。Chokidar 监视既有根的直接增删改;write/edit 工具经 fs/observed 同步失效提供方,使下一次模型步骤无需等宿主 watcher。dsh-skill-badge 注册单个不可变 bundled 候选(dsh-badge 徽章技能,暴露打包 assets/ 为资源基),出厂 composition 声明 disabled: true,显式启用才入目录。

模型侧目录与加载工具

dsh-tool-skill 在每个 agent/pre-stepsnapshot()(转发步骤的 abort signal),首次观察到非空完整视图时向后续 enter 决策注入持久的 user 角色 <system-reminder> 会话前缀目录:<available_skills> 内仅 name + 归一化并截断的 descriptioncatalogDescriptionMaxLength 默认 500),不含 body、路径、source、provider 与 whenToUse。目录 digest 基于持久 entries 而非渲染文本;digest 变化时整表替换(agent.inject() 追加完整替换消息),删空时显式空替换;不完整快照保留 last-good 模型视图,compaction 隐藏全部历史目录后由下一次完整观察重建。

skill({ name }) 工具校验 kebab-case 名,拒绝非 modelInvocable 技能(userInvocable 不限制该工具),按调用 agent 的 session.header.cwd 重读完整定义并复检策略,返回含 <skill_content name> / <skill_resources> / <skill_instructions> 的文本结果。资源按 resourceBase(directory/url/opaque)只解析指令实际引用的路径或 URL,不枚举技能目录;body-only 编辑改变后续调用而不产生目录消息或改写旧工具结果。

用户显式手势是另一条加载路径:用户消息中出现命名用户可调用技能的 /name 记号时,注入该技能完整 <skill_content> 渲染(user 角色指令上下文,置于该步所有注入之后);这是 disable-model-invocation 技能的唯一入口。

出处:packages/skill/tool-skill/README.mddeepseek-harness-src/packages/skill/tool-skill/README.md

与工具、提示词的关系

技能目录是持久会话历史而非系统提示词:首次请求前作为 durable 消息注入,变更走整表替换;技能体作为工具结果保留(append-only,直到压缩)。技能本身不是工具、也不是提示词段:它通过 ctx.tools 上的 skill 工具按需加载,目录消息是 session history 而非 World State。

关键设计决策

  • 注册表是纯提供方抽象:本地/嵌入/远程提供方可互换而不改模型侧契约;Consumer 与提供方解耦(dsh-tool-skill 是唯一模型侧消费者)。
  • 分层合并"最近者胜 + 层内 rank":与 tools 注册表同形,agent preset 可遮蔽全局技能。
  • complete 是缓存资格而非数据完整性:不完整观察宁可保留 last-good,也不发布误导性的删除。
  • 调用策略双布尔独立:一个发现结果同时服务模型工具、人类命令与受信任内部调用者。
  • 目录与 body 生命周期分离:digest 只盯目录,body 每次 get() 现读,编辑无需版本协议。

扩展点

  • 新提供方ctx.skills.registerProvider(create) 同步注册,返回 disposer;远程认证/发现放 list(),按需调 control.invalidate()
  • 运行时技能ctx.skills.register(definition) 注册嵌入技能(rank 250,层内同名 first-wins),省略 invocation/provider 时补全为全可调用 + provider: 'runtime'
  • 新消费面:在自身边界强制 isModelInvocable / isUserInvocable 后消费 list()/snapshot()collectCacheMaxEntries(默认 128)限制注册表内存中的完成目录数。
  • 打包技能:配置 bundledSkillDir 或启用 dsh-skill-badge 行,把出厂技能带入目录。

参考资料

本文档为学习用途的原创讲解,基于 MIT 许可的开源项目 deepseek-ai/deepseek-harness 编写;所有引用均注明出处。