SKILL 开发

从单个 Skill 到技能组:协同开发需要考虑哪些问题

2026-07-244 min readSKILL工程化

多个 Skill 协同,重点已经不再是单个 SKILL.md 怎么写,而是研究:

多个职责独立的 Skill,如何像一组服务或函数一样,被正确调度、传递数据、处理失败并最终完成任务。

可以把整个技能组理解成一个工作流系统:

技能组设计开发中的对应概念
Skill函数、服务或任务节点
调度 SkillWorkflow Engine / Orchestrator
description路由条件和能力注册
references依赖配置和领域规则
共享文件持久化状态和数据协议
Skill 交接函数调用或消息传递
QA Skill测试和质量门禁
发布 Skill最终副作用和交付

一、技能职责设计

1. 每个 Skill 只负责一个阶段

多个 Skill 能协同的前提,是每个 Skill 的职责足够清楚。

例如一个完整任务可以拆成:

text
1需求确认 2→ 任务规划 3→ 内容生成 4→ 质量检查 5→ 产物发布

每个阶段由一个独立 Skill 负责。

最常见的问题是职责重叠:

text
1规划 Skill 顺手生成内容 2生成 Skill 又重新调整规划 3检查 Skill 检查时直接改变目标 4发布 Skill 发布前重新加工内容

一旦每个 Skill 都能修改所有内容,整个技能组就无法预测。

需要为每个 Skill 定义:

  • 它读取什么;
  • 它负责什么;
  • 它修改什么;
  • 它不负责什么;
  • 什么条件下算完成;
  • 完成后交给谁。

可以统一成一个职责声明:

text
1输入: 2处理: 3输出: 4允许修改: 5禁止修改: 6完成条件: 7下游 Skill:

二、调度设计

1. 谁负责决定下一个 Skill

多个 Skill 不能只依赖“执行完以后随便调用另一个 Skill”。

需要有一个明确的调度角色。

调度方式主要有三种。

固定流水线

text
1Skill A 2→ Skill B 3→ Skill C 4→ Skill D

适合步骤固定、分支较少的任务。

优点是简单、稳定,缺点是灵活性有限。

条件路由

text
1Skill A 2 ├── 条件满足 → Skill B 3 ├── 信息不足 → Skill C 4 └── 检查失败 → 返回 Skill A

适合存在审核、重试和人工确认的任务。

中央调度

text
1 ┌→ Skill A 2用户请求 → 调度 Skill ─→ Skill B 3 └→ Skill C

调度 Skill 负责:

  • 判断当前任务状态;
  • 选择需要调用的 Skill;
  • 检查调用前置条件;
  • 决定是否继续;
  • 处理失败和回退;
  • 判断整个任务是否完成。

技能数量较多、分支复杂时,应使用中央调度,避免 Skill 之间相互随意调用。

2. 调度 Skill 不做具体业务

调度 Skill 只负责:

text
1判断 2选择 3交接 4记录

不应该同时负责生成内容、修改文件或发布结果。

否则它会迅速变成一个包含所有逻辑的超级 Skill。

三、上下游数据协议

1. Skill 之间不能只传自然语言

如果上一个 Skill 只说:

已经规划好了,接下来可以开始执行。

下一个 Skill 仍然不知道:

  • 规划结果在哪里;
  • 当前任务是什么;
  • 哪些内容已经确认;
  • 哪些内容不能修改;
  • 当前处于什么状态。

多个 Skill 协同时,需要一份明确的数据协议。

例如:

yaml
1taskId: task-001 2status: planned 3currentStage: planning 4nextStage: generating 5 6input: 7 source: source.md 8 9result: 10 planFile: plan.md 11 12constraints: 13 confirmed: true 14 allowReplan: false

上游 Skill 写入结果,下游 Skill 按字段读取,而不是重新理解全部历史对话。

2. 区分业务数据和执行状态

共享数据中至少包含两部分:

text
1业务数据 2执行状态

业务数据是任务本身的内容:

text
1主题 2规划 3正文 4图片 5发布地址

执行状态描述流程运行到哪里:

text
1当前阶段 2完成时间 3执行结果 4失败原因 5重试次数 6下一个节点

两者不要混在一起。

四、状态机设计

多个 Skill 配合时,不能只通过“文件是否存在”判断任务进度。

需要定义明确的状态:

text
1pending 2→ planning 3→ planned 4→ generating 5→ generated 6→ reviewing 7→ approved 8→ publishing 9→ published

失败状态可以单独定义:

text
1review_failed 2publish_failed 3blocked 4cancelled

每个 Skill 只允许执行合法的状态转换:

text
1规划 Skill: 2pending → planning → planned 3 4生成 Skill: 5planned → generating → generated 6 7检查 Skill: 8generated → reviewing → approved 9generated → reviewing → review_failed 10 11发布 Skill: 12approved → publishing → published

这样可以防止:

  • 规划未确认就开始生成;
  • 检查未通过就直接发布;
  • 已发布内容被重复发布;
  • 失败任务被误认为完成。

五、交接设计

1. Skill 完成后必须留下交接记录

一个 Skill 执行完成,不能只回复一句“已经完成”。

至少要记录:

text
1完成了什么 2生成了哪些产物 3修改了哪些文件 4当前状态是什么 5下一个 Skill 是什么 6下一个 Skill 需要读取什么 7是否存在待确认项

可以统一交接格式:

yaml
1handoff: 2 from: planning 3 to: generating 4 status: ready 5 6artifacts: 7 - plan.md 8 9confirmed: 10 - scope 11 - structure 12 13pending: [] 14 15constraints: 16 allowPlanChange: false

2. 下游 Skill 先验证交接条件

下游 Skill 启动后,不应该立即执行。

它需要先检查:

  • 上游状态是否完成;
  • 必要文件是否存在;
  • 数据结构是否正确;
  • 是否还有待确认项;
  • 当前 Skill 是否有权修改这些产物。

这和函数调用前的参数校验类似。

六、共享产物设计

1. 明确唯一事实来源

同一个数据不能同时存在多个未经同步的版本。

例如规划信息同时存在于:

text
1聊天记录 2plan.md 3task.json 4文章 Frontmatter

后续 Skill 不知道应该相信哪一份。

必须规定唯一事实来源:

text
1聊天记录:用于交互 2状态文件:用于调度 3业务文件:用于最终内容 4交接记录:用于阶段传递

下游 Skill 只读取指定来源。

2. 区分中间产物和最终产物

text
1中间产物: 2分析结果 3任务规划 4检查报告 5临时提示词 6 7最终产物: 8正式文章 9生成图片 10发布页面 11发布地址

中间产物可以被后续 Skill 更新,最终产物默认不应被随意覆盖。

七、质量门禁设计

多个 Skill 协同不能只有生产节点,还需要检查节点。

text
1生成 Skill 2→ QA Skill 3 ├── 通过 → 进入发布 4 └── 失败 → 返回生成 Skill

QA Skill 应该输出结构化结果:

yaml
1result: failed 2 3checks: 4 structure: passed 5 content: failed 6 format: passed 7 8issues: 9 - location: section-2 10 reason: 缺少具体实现 11 action: 补充输入、处理和输出 12 13retryFrom: generating

QA 不应该只给一个笼统评价,也不应该在没有记录的情况下直接大幅修改产物。

八、失败、重试和回退设计

多个 Skill 一定会出现部分成功:

text
1规划成功 2内容生成成功 3检查成功 4发布失败

这时不能从头重新执行。

每个 Skill 都要定义:

  • 失败后是否可以重试;
  • 最大重试次数;
  • 从哪个状态恢复;
  • 已有产物是否可以复用;
  • 是否需要回退上游;
  • 什么情况下交给用户处理。

例如:

text
1工具调用超时 2→ 当前 Skill 重试 3 4输入数据缺失 5→ 返回上游补充 6 7质量检查失败 8→ 返回生成 Skill 局部修改 9 10发布权限不足 11→ 暂停任务,等待用户处理 12 13状态文件损坏 14→ 停止执行,不猜测状态

九、幂等性设计

同一个 Skill 可能因为重试、重复触发或人工操作执行多次。

因此需要提前定义:

text
1重复执行是跳过、覆盖、更新,还是创建新版本?

常见处理方式:

  • 根据 taskId 判断是否已经执行;
  • 根据内容哈希判断产物是否变化;
  • 已完成状态默认跳过;
  • 修改文件前读取当前版本;
  • 发布前检查远程是否已经存在;
  • 外部操作记录唯一请求 ID。

尤其是发布、上传、创建 Issue、发送消息等带副作用的操作,必须考虑幂等性。

十、权限和副作用设计

不同 Skill 应该拥有不同的操作权限。

例如:

Skill 类型允许操作
分析 Skill只读
规划 Skill修改规划文件
生成 Skill创建业务产物
检查 Skill读取产物、生成检查报告
发布 Skill修改外部系统
调度 Skill更新状态和交接记录

不要让所有 Skill 都能:

  • 修改全部文件;
  • 覆盖最终产物;
  • 发布外部内容;
  • 删除历史记录。

副作用越大的 Skill,前置条件应该越严格。

十一、可观测性设计

技能组运行后,要能够回答:

  • 当前运行到哪一步;
  • 哪个 Skill 正在执行;
  • 上一步生成了什么;
  • 为什么选择当前分支;
  • 哪个检查没有通过;
  • 失败发生在哪里;
  • 是否执行过重试;
  • 最终修改了哪些外部数据。

建议为每次任务保存执行记录:

yaml
1taskId: task-001 2status: reviewing 3currentSkill: quality-check 4startedAt: 2026-07-24T10:00:00Z 5updatedAt: 2026-07-24T10:05:00Z 6 7history: 8 - skill: planning 9 status: success 10 output: plan.md 11 12 - skill: generating 13 status: success 14 output: article.md 15 16 - skill: quality-check 17 status: running

这相当于工作流系统的运行日志。

十二、版本兼容设计

多个 Skill 独立演进后,会出现协议不兼容:

text
1上游输出 schema v2 2下游仍按 schema v1 读取

共享数据应该增加版本:

yaml
1schemaVersion: 1

Skill 也可以声明:

yaml
1accepts: 2 - schemaVersion: 1 3 4produces: 5 schemaVersion: 1

当结构升级时,需要明确:

  • 是否兼容旧版本;
  • 是否提供迁移;
  • 不兼容时是否停止执行;
  • 哪些 Skill 必须同时升级。