CH 14 · Skill 与工作流
本章目标
前面你让 Agent 干一件事,得每次把要求说清楚。可有些活步骤是固定的——像 CH 05 跑过的"总结一个仓库、落成文档",或写周报、整理资料这类日常任务,每次都从头把流程交代一遍,太浪费了。这类事值得做成"一张说明书":告诉 Agent"遇到这类活,就按这套步骤来"。这就是 Skill。这一章讲清楚 dsh 的 Skill 是什么、从哪来、模型怎么用它,然后装一个现成的 Skill 拿来即用——只要给 Agent 一句话,它自己会装——让它变成你专属的"可复用能力"。
一个关键事实:Skill 也是插件
跟 MCP、子代理一样,Skill 在 dsh 里不是独立入口,它又是一组插件的组合。看配置树,skill 能力族由四块拼成:
| 插件 | 干什么 | 对应角色 |
|---|---|---|
dsh-skill | 注册表:合并各来源的 skill 目录,按名称解析出"胜出"的那个 | 仓库管理员 |
dsh-skill-filesystem | 从项目、用户目录里发现本地 skill,并监视文件变化 | 采买员 |
dsh-tool-skill | 把可用 skill 列表展示给模型,提供 skill 加载工具 | 前台 |
dsh-skill-badge | 随包附带的官方徽章 skill,默认禁用 | 装饰 |
web 配置里这四件默认都装了——所以你现在就能用,不用装任何东西。这又是 CH 08 那句"一切皆插件":连"可复用能力"本身,也是由插件拼出来的。
Skill 是什么:写好的"怎么做一件事"
工具是 Agent 能"调用"的动作(读写文件、搜网页);Skill 是写给 Agent 的"说明书"——一组任务专项指令,告诉它"遇到这类任务,按这套步骤做"。
两者最大的区别在复用性:工具是系统提供的,Skill 是你自己攒的。你在某件事上摸索出一套好用的做法,写成一个 Skill,从此 Agent 遇到同类活,不用你再把要求重复一遍。而且 skill 跟着存放位置走:放在项目里的 .dsh/skills 是项目级,只对这个项目生效;放在用户目录 ~/.dsh/skills 是全局级,任何项目都能用——下一节"五个货架"会看到完整的层级。
那"工作流"呢:Skill 就是最简单的工作流
一句话先记住:一个 Skill 就是最简单的工作流。工作流的本质是"把人的经验固化成流程"——而一个 Skill 恰好就是这么干的:输入明确、输出明确、步骤固定,可以被单独复用。它就是一个被压缩到单个任务的小流程。
区别在粒度。Skill 管"单个任务怎么做"(一道菜的菜谱);Workflow 管"多个任务怎么按顺序串起来"(从买菜、洗菜到上桌的整条流水线,还要管分支、衔接、谁先谁后)。dsh 的 Standard 预设里,"技能"和"工作流"就是并列的两个工具——单步固定套路用 Skill 就够,多步串联才需要 Workflow。
把两者摆一起,差异更清楚:
| 维度 | Skill | Workflow |
|---|---|---|
| 管什么 | 单个任务"怎么做" | 多个任务"按什么顺序串" |
| 粒度 | 一步固定套路 | 多步流水线 |
| 分支与衔接 | 不负责 | 负责(条件、衔接、谁先谁后) |
| 复用方式 | 单个独立复用 | 需要时重新编排 |
| 类比 | 一道菜的菜谱 | 从买菜到上桌的整条流水线 |
所以当你只为一个固定任务写了一个 Skill,你其实已经搭好了"最简单的工作流"。
Skill 长什么样
一个 Skill 就是一个带 frontmatter 的 Markdown 文件,标准形态是一个目录 bundle:文件夹名就是 skill 名,里面必须有 SKILL.md——适合要带配套资源(脚本、参考文档、素材)的 skill。
名字必须是小写 kebab-case(^[a-z0-9]+(?:-[a-z0-9]+)*$),比如 code-review、weekly-report。
一个标准的目录 bundle 长这样:
code-review/
├── SKILL.md # 必需:frontmatter + 指令正文
├── scripts/ # 可选:配套脚本(.py / .sh 等)
├── references/ # 可选:参考文档、检查清单模板
└── assets/ # 可选:素材、示例文件SKILL.md 是唯一必需的文件,正文里可以用相对路径引用 bundle 里的脚本和参考文档。最小结构长这样:
---
name: code-review
description: 按统一清单审查一段代码,输出结构化评审意见
whenToUse: 用户要求"审查代码"或"code review"时
---
# 代码审查
按下面的顺序来:
1. 先读相关 README,弄清这段代码要解决的问题;
2. 列出公开的导出与主要函数,标注每个的作用;
3. 找明显的风险点:错误处理、边界条件、敏感信息;
4. 用表格输出:文件 / 问题 / 严重程度 / 建议。frontmatter 里只有 name 和 description 是必填的,其他字段都可选:
| 字段 | 作用 |
|---|---|
name | Skill 的唯一名字(kebab-case,必填) |
description | 一句话描述,模型靠它判断"这活跟我不对口"(必填) |
whenToUse | 额外的使用时机提示(可选) |
disable-model-invocation | 设为 true:只许用户用 /名字 调,模型不能自动加载(可选) |
user-invocable | 设为 false:只许模型调,用户不能 /名字 调(可选) |
两个开关:谁能调它
frontmatter 里两个字段管"谁能调":
| 组合 | 效果 |
|---|---|
| 都不写 | 模型和用户都能调(默认) |
disable-model-invocation: true | 只许用户 /名字 调;模型目录和 skill 工具里看不到它——适合放敏感或高成本流程,防止模型随手触发 |
user-invocable: false | 只许模型调;用户 /名字 无效 |
| 两个都关 | 只能被可信代码直接调用,模型和用户都碰不到 |
Skill 从哪来:五个"货架"
先分清两件事:这一节讲的是 dsh 从哪里读到 skill——即 skill 放在本地的哪个目录能被发现。dsh 用 dsh-skill-filesystem 这个插件按固定顺序扫几个根目录,把里面的 skill 收进注册表。"安装新 skill"是另一件事:去社区技能市场(比如腾讯的 SkillHub)或 dsh 插件市场,把别人写好的 SKILL.md 安装进这些货架目录——后面"装一个现成的 Skill"一节就是干这个。
dsh-skill-filesystem 会按固定顺序扫几个根目录,把里面的 skill 收进注册表。越靠上的优先级越高——同名 skill 出现在多个货架时,排前面的赢:
| 优先级 | 目录 | 谁放这 |
|---|---|---|
| 1 | <项目根>/.dsh/skills | 跟着项目走,随仓库一起分发 |
| 2 | <项目根>/.agents/skills | 兼容其他工具(如 Claude Code)的共享位置 |
| 3 | 配置里的自定义目录 customSkillDirs | 你手动指定的其他位置 |
| 4 | <dshHome>/skills(~/.dsh/skills) | 用户级,所有工作区都能用 |
| 5 | <agentsHome>/skills(~/.agents/skills) | 与其他 Agent 工具共享的用户级目录 |
模型怎么"看见" Skill
Skill 是写给模型看的,它怎么知道有哪些?这套机制由 dsh-tool-skill 插件提供,web 配置默认装好(dsh --profile web --dump-config 里能看到它)。靠三个机制配合:
- 会话目录:会话开始、第一个请求之前,模型会收到一条持久消息,列出所有可用 skill 的名字和一句话描述,并被告知"动手前先加载匹配的 skill,不要凭摘要瞎猜"。
skill工具:模型判断某个 skill 对口时,用skill({ name })加载完整指令正文——正文以<skill_content>块返回,作为工具结果留在历史里。下图是一张真实轨迹:模型先调skill工具加载aihot的完整指令,再调pwsh跑验证请求,两条工具调用在轨迹里一目了然。
3. 用户手势 /名字:你在输入框直接打 /code-review,该 skill 的指令会以用户消息的形式注入这一轮,模型照着做就行。注意:这个名字必须真实存在于工作区货架里、且允许用户调用——输入一个不存在的 skill 名,会被当成普通文字,没有任何反应。
一个细节:用户 /名字 注入后,模型不会再用 skill 工具重复加载一次——避免同一个指令被塞两遍、白烧 token。
如果问模型"你现在有哪些 skill"它报不出来,先确认 web 配置里有 @deepseek-ai/dsh-tool-skill 这个插件(dsh --profile web --dump-config 一看便知)。
动手:装一个现成的 Skill(拿来用)
装现成 skill 的本质,就是把别人写好的 SKILL.md 放进 dsh 的"货架"目录(通常是项目级 .dsh/skills)——社区里已经有一大批写好的 skill 可以直接拿。
这里以 aihot 为例:它是由知名 AI 科技博主"数字生命卡兹克"出品的检索类 skill,擅长抓取每日 AI 新闻与行业动态。
第 1 步:给 Agent 一句话,让它帮你装
不用自己找文件、建目录。回到对话,直接说:
请安装 AIHOT Skill:https://aihot.virxact.com/aihot-skill/README.md 装完告诉我是否需要开启新会话。 安装器支持时请追加:--actor 你的Actor ID
--actor 后面的 ID 是 AIHOT 平台注册后分配给你的,装完会写进本地的 .aihot-actor-id,Agent 请求时会带上做身份标识。
Agent 会自己动手:去官网把 AIHOT 的 SKILL.md 和配套文件下载下来,放进 DSH 的标准发现位置(~/.agents/skills/aihot/),逐一做 SHA-256 完整性校验、写入 actor 配置、生成 .gitignore,装完把结果报告给你。装完后不需要新开会话——它已经出现在当前会话的 <available_skills> 目录里,直接就能用。

第 2 步:让模型报出新 Skill
回到对话,问:
你现在有哪些 skill?aihot 这个 skill 是干什么的?
模型会把它报出来——你一行代码没写,就多了一个可复用能力。它还会顺带告诉你这个 skill 具体能干什么(aihot 通过 AIHOT 的匿名只读 API 查询当下真实的中文 AI 资讯,而不是用训练记忆"编"新闻)。

第 3 步(可选):让模型用它
直接说"用 aihot 抓一下今天有哪些 AI 新闻",模型会加载这个 skill 去抓取。别人沉淀好的检索流程,现在变成了你的能力——这就是"拿来用"。

常见坑
| 坑 | 怎么避开 |
|---|---|
忘了 description | 必填字段缺失,整个 skill 会被警告后丢弃 |
| 目录层级放错 | 只认 <根>/<名字>/SKILL.md 或 <根>/<名字>.md 一层,别往深层嵌套 |
| 模型不主动用 | description 写清楚"什么时候用它"(whenToUse 也有帮助);实在不放心就用 /名字 直接注入 |
| 改了正文但模型没反应 | 正文改动不影响目录摘要,要让模型"重新加载"一次;改 description 这种摘要字段才会立刻反映到目录 |
这一章你学到了什么
能自己完成下面几条,就算过关:
- [ ] 说出 Skill 是"写给 Agent 的可复用指令",与工具的区别在复用性
- [ ] 知道 Skill 在 dsh 里也是插件(skill 注册表 + 文件系统发现 + 工具消费方)
- [ ] 能说出一个 Skill 的结构(frontmatter 里
name/description必填,whenToUse可选) - [ ] 知道五个货架的优先级(项目
.dsh/skills最优先,用户目录次之,可跨项目使用) - [ ] 跑通过一次"给 Agent 一句话 → 它装好 skill → 模型报出 → 模型加载使用"
- [ ] 分清
disable-model-invocation和user-invocable两个开关
