跳到主要内容
路径文档

技能系统

技能是按需加载的能力描述:模型在需要时把它的指令载入上下文,而不是把每个技能的全文塞进系统提示词。这就是渐进披露:尽量少占用 token,直到真正要用那一刻才展开完整内容。

DSH 的技能 seam 是 ctx.skills(@deepseek-ai/dsh-skill)。它是 host+per-scope 分层的注册表,与工具注册的 scope 模型一致。

三个观察/加载接口

接口作用token 量级
ctx.skills.list(...)当前工作区合并层的技能摘要(名字排序)
ctx.skills.snapshot({ cwd, signal, scope })调用无关的 { skills, complete } 观测
ctx.skills.get(name, ...)完整 技能定义(发现 + 加载)大,按需

模型通常:list() 拿名字列表 → 需要时 get(name) 展开全文。isModelInvocable(skill) / isUserInvocable(skill) 决定谁可以触发。

技能来自哪里

ctx.skills 自己不知道技能在本地文件、插件内嵌数据、还是远端:provider 提供来源:

  • @deepseek-ai/dsh-skill-filesystem:出厂的本地文件实现
  • ctx.skills.register(skill):把只读的运行时内嵌技能注册进当前作用域层
  • 任意第三方 provider 通过 ctx.skills.registerProvider(...) 接入

层语义(与工具一致):全局层 vs agent-preset 层;读时合并 global 层 + 查看 scope 的链,最近层同名即胜。

技能目录布局(skill-filesystem)

本地 provider 识别两种形态,发现只深一层:目录 bundle <root>/<name>/SKILL.md,或扁平文件 <root>/<name>.md。frontmatter 里 namedescription 必填,whenToUsemetadatadisable-model-invocationuser-invocable 选填,名字必须 kebab-case。

默认根按 rank 解析:

排名来源路径
100project-dsh<projectRoot>/.dsh/skills
200project-agents<projectRoot>/.agents/skills
300customConfig.customSkillDirs
400user-dsh<dshHome>/skills
500user-agents<agentsHome>/skills

项目根 = 最近的含 .git 祖先,没有则用当前 cwd;user DSH 根跳过 .system 子目录。

调用策略

SkillSummary.invocation 是必填的策略对象,两个正向布尔独立描述"模型面"与"用户面":

策略模型用户
{ modelInvocable: true, userInvocable: true }
{ modelInvocable: true, userInvocable: false }不含
{ modelInvocable: false, userInvocable: true }不含
{ modelInvocable: false, userInvocable: false }不含不含

ctx.skills.get() 仍是策略中立的加载原语;每个面向模型/用户的消费者都要在自己边界上执行对应谓词再暴露或加载。frontmatter 的 disable-model-invocation: true 把技能从模型目录/加载器排除,user-invocable: false 从面向人的命令排除;缺省都放行,无效拼写或非布尔值整条丢弃该技能(失效即关闭)。

运行时注册一个技能

import { Context } from '@deepseek-ai/cordis'

export const name = 'skill-demo'
export const inject = ['skills']

export function apply(ctx: Context) {
ctx.skills.register({
name: 'my-procedure',
description: '做某件事的固定流程',
// 省略 platform 等元数据
load: async () => ({
title: '我的流程',
instructions: [
'1. 先读配置文件',
'2. 再执行校验命令',
'3. 最后把结果写进会话',
],
}),
})
}

运行时技能用 rank 250:项目 provider 能覆盖它,它又能覆盖出厂本地 provider 的 custom/user 根;同层同名先到先得。

模型通过 tool-skill 调用

出厂带 @deepseek-ai/dsh-tool-skill:模型面向的技能加载工具,负责"按需展开"。

  • 目录生命周期:每次 eligible 的 agent/pre-step 按会话 cwd 调 snapshot(),渲染排序后的 name + description;首次非空给下游 enter 决策加一条持久的 user-role <system-reminder> 初始目录,摘要变化时追加整份替换。无模型可调用技能、或 skill 工具被限制/遮蔽时目录省略。
  • skill 工具:参数 name(string,必填,精确 kebab-case 名)。成功返回 { name, provider, resourceBase?, content },Native 渲染成一段 <skill_content>;未知名、非法名、modelInvocable: false 产生不同的错误结果。
  • 用户显式注入:用户消息里 whitespace 界定的 /name 记号命中 user-invocable 技能,就把完整 <skill_content> 作为 user 角色指令注入该步末尾。这是 disable-model-invocation 技能(目录与 skill 工具都不暴露)的唯一入口。

badge 包

@deepseek-ai/dsh-skill-badge 是可选的内嵌 provider,贡献 dsh-badge 技能:官方 "powered by dsh" Markdown 片段 + 打包 PNG(供无法可靠拉远程图的环境)。无配置,出厂 CLI 组合挂为 disabled: true,要显式启用才进目录。

事件与失效

  • skills/change未过滤的失效通知:任何 provider/runtime 注册或注销、或某 provider 内部控制失效,都会触发。它不携带目录或 diff,每个消费者自己 snapshot() 重新抓取。
  • 监听器抛错/拒绝只会被记录,不能否决注册变更

验证

# 当前工作区能列出哪些技能(通过模型对话或工具)
# 会话日志里看 skills 相关事件
zstdcat ~/.dsh/sessions/*/*/session.jsonl.zstd | grep -E "skills" | head

配置

字段默认作用
collectCacheMaxEntries128内存中缓存的 cwd/provider 目录数上限

skill-filesystem 的 provider 配置:

字段默认作用
providerNamelocalctx.skills 上注册的唯一名
includeDefaultRootstrue在 customSkillDirs 之外含项目与用户根;设 false 得隔离的 custom-root provider
customSkillDirs[]额外本地技能根,扫在项目根之后、用户根之前
dshHome$DSH_HOME~/.dshDSH 配置根,扫描其下 skills
agentsHome$DSH_AGENTS_HOME~/.agents共享 agent 配置根
watchtrue监听宿主根,目录成员/frontmatter 变化时失效 provider
watchUsePollingfalse用 Chokidar 轮询代替原生事件

下一步