核心概念:复合Skill(Composite Skill)是将多个原子操作编排成一个可复用工作流的Skill类型。它通过定义步骤序列、数据传递规则和异常处理策略,将复杂任务拆解为可管理、可测试、可观测的子步骤。掌握复合Skill的设计与编排是构建可靠自动化工作流的核心能力。
一、复合Skill的设计模式
复合Skill的核心价值在于将多个原子操作组合成一个有意义的业务流程。不同的任务结构需要不同的编排模式。以下是四种最常用的设计模式:
串行模式 (Sequential)
步骤依次执行,前一步的输出作为后一步的输入。适用于有明确依赖关系的任务,如"先格式化代码,再运行测试"。串行模式是最直观、最容易调试的编排方式。
并行模式 (Parallel)
多个步骤同时执行,互不依赖。适用于无依赖关系的独立任务,如"同时运行单元测试和静态分析"。可显著缩短总执行时间,但需注意资源竞争问题。
条件分支模式 (Conditional)
根据上一步的执行结果或输出值,动态选择后续执行路径。适用于决策型工作流,如"如果测试通过则部署,否则发送告警"。需要提前定义好所有分支路径。
循环模式 (Loop/Iteration)
对多个项目重复执行相同操作,典型场景包括"批量处理文件"或"遍历目录中的所有图片并压缩"。循环模式需要定义迭代变量和终止条件以避免无限循环。
设计建议:实际项目中往往是多种模式的组合。例如,"批量处理文件"的内部是循环模式,每个文件的处理流程内部是串行模式,而不同文件之间可以并行执行。建议先在纸上画出工作流图,再确定每种模式的具体边界。
串行模式详解
串行模式是最基础也最常用的编排方式。每个步骤按顺序执行,上一步的成功是下一步的前提。典型结构如下:
Step 1: 读取配置文件
|
v
Step 2: 验证配置有效性
|
v
Step 3: 根据配置执行操作
|
v
Step 4: 输出执行结果
串行模式的优势在于简单可控——每个步骤的输入来自上一步的输出,数据流清晰,错误定位容易。缺点是总执行时间等于所有步骤耗时之和,各步骤间无法并行加速。
并行模式详解
并行模式的关键在于识别哪些步骤之间不存在数据依赖。使用并行模式时,需要关注以下要点:
- 依赖分析:确认各并行分支之间没有数据或资源冲突
- 资源规划:评估并行执行对系统资源(CPU、内存、网络)的峰值需求
- 汇合点设计:定义所有并行分支完成后如何汇合并汇总结果
- 超时策略:为每个并行分支设置独立的超时时间,防止某个分支卡住导致整个工作流挂起
条件分支模式详解
条件分支使复合Skill具备决策能力。常见的条件类型包括:
- 基于退出码:步骤成功(exit=0)走正常路径,失败走错误处理路径
- 基于输出内容:检查输出是否包含特定关键词,匹配时走不同路径
- 基于文件状态:检查某文件是否存在、内容是否变化等
- 基于环境变量:根据当前环境(开发/测试/生产)决定执行策略
二、步骤间数据传递
复合Skill的各个步骤之间需要共享数据和上下文信息。数据传递机制的设计直接影响Skill的灵活性和可维护性。以下是四种主要的数据传递方式:
1. 引用上一步结果:{{OUTPUT:stepN}}
最常见的传递方式,通过模板语法引用前置步骤的输出。这种方式直观且便于追踪数据来源。
# 示例:格式化后对代码运行静态分析
Step 1: 运行代码格式化工具
command: npx prettier --write src/
Step 2: 对格式化后的代码运行 ESLint
command: npx eslint {{OUTPUT:step1}} --format json
# {{OUTPUT:step1}} 会被替换为 step1 输出的文件列表
2. 临时变量存储和传递
在工作流内部使用命名变量,跨步骤共享中间数据。适用于需要在多个步骤中复用同一数据的场景。
# 使用临时变量传递数据
variables:
TARGET_DIR: "src/components"
OUTPUT_DIR: "dist"
steps:
- name: 编译组件
command: npx tsc {{TARGET_DIR}} --outDir {{OUTPUT_DIR}}
- name: 复制资源文件
command: cp -r {{TARGET_DIR}}/assets {{OUTPUT_DIR}}/assets
- name: 打包输出
command: tar -czf build.tar.gz {{OUTPUT_DIR}}
3. 通过文件系统传递
对于大数据量或二进制数据,将中间结果写入文件,后续步骤读取该文件。这种方式不受内存限制,且便于调试(可以检查中间文件内容)。
# 通过文件传递大数据
Step 1: 导出数据库
command: pg_dump mydb > /tmp/db_backup.sql
Step 2: 压缩备份
command: gzip /tmp/db_backup.sql
Step 3: 上传到云存储
command: aws s3 cp /tmp/db_backup.sql.gz s3://my-backup-bucket/
注意:文件系统传递方式需要关注文件命名冲突问题。建议为每个执行实例生成唯一的工作目录(如使用时间戳或UUID命名),防止并发执行时出现文件覆盖。
4. 结构化数据(JSON)提取与传递
当步骤输出为JSON格式时,可以使用查询语法精确提取所需字段。这种方式特别适合与API交互的场景。
# 从API响应中提取数据并传递给后续步骤
Step 1: 创建GitHub Issue
command: gh issue create --title "Bug report" --body "Description" --json number,url
# 输出: {"number": 42, "url": "https://github.com/.../issues/42"}
Step 2: 在Issue上添加标签
command: gh issue edit {{OUTPUT:step1.number}} --add-label "bug"
# 使用 .number 提取JSON中的数值字段
Step 3: 发送通知
command: curl -X POST $WEBHOOK_URL \
-H "Content-Type: application/json" \
-d '{"issue_url": "{{OUTPUT:step1.url}}"}'
# 使用 .url 提取JSON中的URL字段
最佳实践:选择数据传递方式时,遵循以下优先级:小数据用变量引用 → 结构化数据用JSON提取 → 大数据用文件系统。避免在多个步骤间传递过于庞大的数据,保持每个步骤的职责单一和清晰。
| 传递方式 |
适用场景 |
数据量级 |
优势 |
局限 |
| 引用({{OUTPUT}}) |
小数据引用 |
KB级 |
直观,可追踪 |
数据量受限 |
| 临时变量 |
配置共享 |
KB级 |
跨步骤复用 |
需要显式声明 |
| 文件系统 |
大数据/二进制 |
MB~GB级 |
无内存限制 |
需处理文件冲突 |
| JSON提取 |
API响应 |
KB级 |
精确提取 |
依赖JSON结构 |
三、错误处理和回滚
复合Skill的执行过程可能遇到各种异常情况:某步骤执行失败、输入数据不符合预期、外部服务不可用等。健壮的错误处理机制是复合Skill生产级部署的必要条件。
前置条件检查
在Skill开始执行前,验证所有前提条件是否满足。这可以避免执行到一半才发现环境不满足要求,浪费时间和资源。
# 前置条件检查示例
preconditions:
- check: 命令是否存在
command: which node
error_message: "Node.js 未安装,请先安装 Node.js"
- check: 目录是否存在
command: test -d src/
error_message: "src/ 目录不存在"
- check: 环境变量是否设置
command: test -n "$GITHUB_TOKEN"
error_message: "GITHUB_TOKEN 环境变量未设置"
步骤级错误捕获与处理
为每个步骤或步骤组配置独立的错误处理策略,而不是在整个Skill级别统一处理。这样可以实现更精细的异常管理。
# 步骤级错误处理示例
steps:
- name: 运行测试
command: npm test
on_error:
- action: retry
max_retries: 3
retry_interval: 5s
条件:仅网络相关错误重试
- action: notify
channel: slack
message: "测试阶段失败,需要人工介入"
- action: continue # 或 fail(默认)
- name: 部署到生产
command: npm run deploy
on_error:
- action: rollback
target: "上一版本"
重要:重试策略需要谨慎设计。对于幂等操作(如读取数据)可以安全重试;对于非幂等操作(如创建资源),需要先撤销已执行的操作再重试,否则可能导致重复创建。建议设置最大重试次数,避免无限重试导致资源耗尽。
部分成功状态的处理
在并行模式或批量处理场景中,可能出现部分步骤成功、部分失败的情况。需要明确的策略来应对这种部分成功状态。
- 全部成功才算成功(All-or-Nothing):任一子步骤失败,整个Skill标记为失败。适用于事务性操作。
- 部分成功可接受(Best-Effort):记录成功和失败的结果,整体标记为部分成功。适用于批量处理等场景。
- 阈值策略:设定成功率阈值(如80%),超过阈值算成功,否则整体失败。
回滚操作的设计
对于修改系统状态的操作,必须设计对应的回滚(Rollback)机制。回滚操作应该是幂等的——多次执行不会产生副作用。
# 带回滚操作的复合Skill示例
steps:
- name: 停止旧服务
command: systemctl stop myapp
rollback:
command: systemctl start myapp
condition: 始终执行
- name: 部署新版本
command: cp -r /tmp/new-version /opt/myapp/
rollback:
command: cp -r /opt/myapp/backup /opt/myapp/
condition: 当此步骤成功时才执行
- name: 启动新服务
command: systemctl start myapp
rollback:
command: systemctl stop myapp
condition: 始终执行
- name: 健康检查
command: curl -f http://localhost:8080/health
rollback:
command: |
systemctl stop myapp
cp -r /opt/myapp/backup /opt/myapp/
systemctl start myapp
condition: 始终执行
回滚设计原则:①每个可能失败的步骤都应该有对应的回滚操作;②回滚操作必须是幂等的;③回滚的执行顺序应该与正向操作的顺序相反(LIFO,后进先出);④回滚本身也可能失败,需要记录失败日志并人工介入;⑤在执行任何修改前,先创建备份。
四、实用复合Skill示例
以下三个完整示例展示了复合Skill在实际开发工作流中的应用。这些示例可以直接作为模板使用,根据项目需求进行调整。
示例一:完整代码提交流程
一个标准化的代码提交流程,在提交前自动完成格式化、lint检查和测试,确保进入版本库的代码质量。
Step 1: 代码格式化 (Prettier)
|
v
Step 2: 静态检查 (ESLint)
|
v
条件判断: Lint是否通过?
├── 是 ──→ Step 3: 运行测试
│ |
│ v
│ 条件判断: 测试是否通过?
│ ├── 是 ──→ Step 4: Git提交
│ └── 否 ──→ 输出测试失败报告
└── 否 ──→ 输出Lint错误并中止
# 完整代码提交复合Skill
name: "安全提交代码"
preconditions:
- command: git diff --stat
error_message: "没有需要提交的更改"
steps:
- name: 格式化代码
command: npx prettier --write "src/**/*.{js,ts,jsx,tsx}"
description: 使用Prettier统一代码风格
- name: 静态检查
command: npx eslint "src/**/*.{js,ts,jsx,tsx}" --max-warnings 0
description: ESLint检查代码质量
on_failure:
action: abort
message: "ESLint检查未通过,请手动修复后重试"
- name: 运行测试
command: npm run test -- --coverage
description: 运行单元测试并生成覆盖率报告
on_failure:
action: abort
message: "测试未通过,请检查测试失败原因"
- name: Git提交
command: |
git add .
git commit -m "{{commit_message}}"
description: 提交代码到本地仓库
on_success:
message: "代码提交成功!"
summary:
- 格式化文件数: {{OUTPUT:step1.file_count}}
- ESLint问题数: {{OUTPUT:step2.issue_count}}
- 测试覆盖率: {{OUTPUT:step3.coverage}}
- 提交哈希: {{OUTPUT:step4.commit_hash}}
示例二:项目初始化
从头初始化一个新项目,自动完成目录创建、配置文件生成、Git初始化和依赖安装的完整流程。
# 新项目初始化复合Skill
name: "初始化新项目"
variables:
PROJECT_NAME: "{{project_name}}"
PROJECT_TYPE: "{{project_type}}" # node, python, go 等
steps:
- name: 创建项目目录结构
command: |
mkdir -p {{PROJECT_NAME}}/src
mkdir -p {{PROJECT_NAME}}/tests
mkdir -p {{PROJECT_NAME}}/docs
mkdir -p {{PROJECT_NAME}}/scripts
description: 创建标准项目目录结构
- name: 生成配置文件
command: |
case {{PROJECT_TYPE}} in
node)
cd {{PROJECT_NAME}} && npm init -y
npm install --save-dev jest eslint prettier
;;
python)
cd {{PROJECT_NAME}} && python -m venv venv
echo "pytest\nblack\nflake8" > requirements-dev.txt
;;
go)
cd {{PROJECT_NAME}} && go mod init {{PROJECT_NAME}}
;;
esac
description: 根据项目类型生成初始配置文件
- name: 初始化Git仓库
command: |
cd {{PROJECT_NAME}}
git init
echo "node_modules/\nvenv/\n.env\n*.pyc\n__pycache__/" > .gitignore
git add .
git commit -m "初始项目结构"
description: 初始化Git仓库并完成首次提交
- name: 输出项目摘要
command: |
echo "================================="
echo "项目初始化完成!"
echo "项目名称: {{PROJECT_NAME}}"
echo "项目路径: $(pwd)/{{PROJECT_NAME}}"
echo "项目类型: {{PROJECT_TYPE}}"
echo "================================="
description: 输出初始化结果摘要
扩展建议:这个基础模板可以根据团队规范进一步扩展——添加CI配置文件(如.github/workflows/ci.yml)、添加Dockerfile和docker-compose.yml、创建README模板、配置commitlint和husky等Git钩子、设置代码所有者(CODEOWNERS)等。
示例三:Bug修复工作流
自动化的Bug修复流程:分析问题、定位根因、生成修复代码、运行测试验证、提交变更并创建Pull Request。
# Bug修复复合Skill
name: "自动化Bug修复流程"
variables:
BUG_ID: "{{bug_id}}"
BUG_DESCRIPTION: "{{bug_description}}"
preconditions:
- command: git status --porcelain
error_message: "工作区不干净,请先stash当前更改"
steps:
- name: 创建修复分支
command: |
git checkout -b fix/{{BUG_ID}}
description: 从主分支创建修复分支
- name: 分析问题根因
command: |
echo "开始分析 Bug #{{BUG_ID}}: {{BUG_DESCRIPTION}}"
# 执行日志分析、代码搜索等操作
# ... 具体分析逻辑
description: 分析Bug根因
- name: 生成修复代码
command: |
# 根据分析结果生成修复代码
# ... 修复逻辑
description: 编写修复代码
on_failure:
action: abort
message: "无法自动生成修复,请手动修复"
- name: 运行测试验证
command: |
npm run test -- --related "{{OUTPUT:step3.changed_files}}"
description: 运行相关测试验证修复
on_failure:
action: rollback
rollback_step: 2
- name: 提交修复并创建PR
command: |
git add .
git commit -m "fix({{BUG_ID}}): {{BUG_DESCRIPTION}}"
git push origin fix/{{BUG_ID}}
gh pr create \
--title "修复 Bug #{{BUG_ID}}" \
--body "## 修复内容\n\n{{BUG_DESCRIPTION}}\n\n## 测试验证\n\n- [x] 单元测试通过\n- [x] 回归测试通过" \
--label "bug"
description: 提交修复代码并创建Pull Request
rollback:
- name: 删除修复分支
command: |
git checkout main
git branch -D fix/{{BUG_ID}}
condition: 任何步骤失败时执行
要点总结:设计复合Skill时,应始终遵循以下原则——①每个步骤职责单一,便于测试和复用;②明确定义步骤间的数据契约(输入/输出格式);③为关键路径设计错误处理和回滚策略;④使用有意义的步骤名称和描述,使工作流自文档化;⑤添加summary/summary步骤,提供执行结果的概览信息。
复合Skill的设计本质上是"关注点分离"思想在工作流层面的实践。一个设计良好的复合Skill就像一个可靠的高级员工——你告诉它"做什么",它自己知道"怎么做",遇到问题知道"怎么处理",做完了还能给你"汇报结果"。