Skills 设计模式与最佳实践
经过大量实践总结出来的 Skill 设计模式,能帮助你在面对新需求时快速做出合理决策。
本篇汇总了最常用的 7 种设计模式及其适用场景。
模式一:单一职责 Skill
每个 Skill 只做一件事,做好一件事。
功能越聚焦,description 越精准,触发准确率越高,维护也越容易。
| 做法 | 示例 |
|---|---|
| 正确:职责单一 | pdf-to-text(只负责提取文字) |
| 错误:职责混杂 | pdf-tools(读取、转换、合并、加水印……全放一起) |
如果你发现一个 Skill 的 description 需要写很长才能说清楚它"不做什么",通常意味着它承担了太多职责,应该拆分。
模式二:防御性输入检查
在 Skill 脚本入口处集中完成所有输入验证,通过后再进入业务逻辑。
实例
# 文件路径:scripts/defensive_entry.py
# 模式:防御性输入检查——入口处集中验证,通过后放行
import sys
import os
import json
def validate(args) -> list:
"""集中验证所有输入,返回错误列表(空列表表示全部通过)"""
errors = []
if not args.file:
errors.append("缺少参数 --file")
elif not os.path.exists(args.file):
errors.append(f"文件不存在:{args.file}")
elif os.path.getsize(args.file) == 0:
errors.append("文件内容为空")
if args.limit < 1:
errors.append("--limit 必须大于 0")
return errors
def run(args):
"""业务逻辑入口,只在验证通过后才会被调用"""
# 执行实际处理...
return {"status": "success", "file": args.file}
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--file", required=False, default="")
parser.add_argument("--limit", type=int, default=100)
args = parser.parse_args()
# 1. 集中验证
errors = validate(args)
if errors:
result = {"status": "input_error", "errors": errors}
print(json.dumps(result, ensure_ascii=False))
sys.exit(1)
# 2. 通过验证后执行业务逻辑
result = run(args)
print(json.dumps(result, ensure_ascii=False))
# 模式:防御性输入检查——入口处集中验证,通过后放行
import sys
import os
import json
def validate(args) -> list:
"""集中验证所有输入,返回错误列表(空列表表示全部通过)"""
errors = []
if not args.file:
errors.append("缺少参数 --file")
elif not os.path.exists(args.file):
errors.append(f"文件不存在:{args.file}")
elif os.path.getsize(args.file) == 0:
errors.append("文件内容为空")
if args.limit < 1:
errors.append("--limit 必须大于 0")
return errors
def run(args):
"""业务逻辑入口,只在验证通过后才会被调用"""
# 执行实际处理...
return {"status": "success", "file": args.file}
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--file", required=False, default="")
parser.add_argument("--limit", type=int, default=100)
args = parser.parse_args()
# 1. 集中验证
errors = validate(args)
if errors:
result = {"status": "input_error", "errors": errors}
print(json.dumps(result, ensure_ascii=False))
sys.exit(1)
# 2. 通过验证后执行业务逻辑
result = run(args)
print(json.dumps(result, ensure_ascii=False))
模式三:渐进式输出
对于耗时超过 5 秒的 Skill,应当分阶段输出进度,而不是让用户面对沉默的等待。
## 执行流程(渐进式输出示例) ### 步骤一:读取文件 运行读取脚本后,立即告知用户: > 已读取文件:runoob_data.csv(1,024 行,8 列)正在进行数据清洗... ### 步骤二:数据清洗 清洗完成后,告知用户: > 数据清洗完成:删除重复行 3 条,空值填充 12 处。正在生成报告... ### 步骤三:生成报告 报告生成后,展示下载链接并说明内容: > 报告已生成,包含统计摘要和数据质量评估。
模式四:幂等操作
Skill 的执行结果应该是幂等的:同样的输入,无论执行多少次,输出都应当一致。
非幂等操作(如每次执行都追加内容)会导致用户重试时产生混乱。
实例
# 文件路径:scripts/idempotent_output.py
import os
from datetime import datetime
OUTPUT_DIR = "/mnt/user-data/outputs"
def get_output_path(base_name: str, suffix: str = ".xlsx") -> str:
"""
生成确定性的输出路径
幂等方式:每次执行覆盖同名文件(而不是追加或生成新文件)
确保同样的输入始终对应同样的输出文件
"""
# 方式 A:固定文件名(同一输入始终覆盖)
return os.path.join(OUTPUT_DIR, f"{base_name}_report{suffix}")
# 方式 B(时间戳):每次生成新文件(非幂等,但保留历史)
# ts = datetime.now().strftime("%Y%m%d_%H%M%S")
# return os.path.join(OUTPUT_DIR, f"{base_name}_{ts}{suffix}")
import os
from datetime import datetime
OUTPUT_DIR = "/mnt/user-data/outputs"
def get_output_path(base_name: str, suffix: str = ".xlsx") -> str:
"""
生成确定性的输出路径
幂等方式:每次执行覆盖同名文件(而不是追加或生成新文件)
确保同样的输入始终对应同样的输出文件
"""
# 方式 A:固定文件名(同一输入始终覆盖)
return os.path.join(OUTPUT_DIR, f"{base_name}_report{suffix}")
# 方式 B(时间戳):每次生成新文件(非幂等,但保留历史)
# ts = datetime.now().strftime("%Y%m%d_%H%M%S")
# return os.path.join(OUTPUT_DIR, f"{base_name}_{ts}{suffix}")
模式五:明确的结束信号
Skill 执行完毕后,应当给出明确的"完成信号",让用户知道任务已结束,不需要继续等待。
## 任务完成后的输出规范 无论成功还是失败,最后一步必须输出一个明确的状态行: 成功时: > 任务完成:报告已生成,共处理 1,024 行数据,耗时约 3 秒。 失败时: > 任务中断:在"数据清洗"步骤遇到错误(原因:文件编码不是 UTF-8)。 > 建议:将文件另存为 UTF-8 格式后重新上传。
模式六:版本兼容性声明
在 SKILL.md 的 frontmatter 中声明兼容的模型版本,防止在不支持的环境中产生不可预期的行为。
---
name: data-analyzer
version: 1.2.0
description: 分析 CSV/Excel 数据,生成统计报告。
compatibility:
claude_models:
- claude-sonnet-4-20250514 # 经过验证的模型版本
- claude-opus-4 # 同样支持
python: ">=3.8"
platforms:
- linux # Claude 计算机使用环境
---
模式七:Fail Fast(快速失败)
一旦发现无法继续的错误,立即停止并清晰报告,而不是带着错误继续执行产生无意义的输出。
实例
# 文件路径:scripts/fail_fast.py
import sys
import json
def assert_condition(condition: bool, message: str, hint: str = ""):
"""如果条件不满足,立即输出错误并退出(Fail Fast 模式)"""
if not condition:
result = {
"status": "error",
"message": message,
"hint": hint
}
print(json.dumps(result, ensure_ascii=False))
sys.exit(1)
# 在脚本开头集中做所有前置检查
import os
file_path = sys.argv[1] if len(sys.argv) > 1 else ""
assert_condition(bool(file_path),
"缺少文件路径参数",
"用法:python script.py <文件路径>")
assert_condition(os.path.exists(file_path),
f"文件不存在:{file_path}",
"请检查路径是否正确,或重新上传文件")
assert_condition(file_path.endswith(".csv"),
"文件格式不支持,期望 .csv",
f"实际文件:{file_path}")
# 所有检查通过,执行实际逻辑
print(json.dumps({"status": "ok", "file": file_path}))
import sys
import json
def assert_condition(condition: bool, message: str, hint: str = ""):
"""如果条件不满足,立即输出错误并退出(Fail Fast 模式)"""
if not condition:
result = {
"status": "error",
"message": message,
"hint": hint
}
print(json.dumps(result, ensure_ascii=False))
sys.exit(1)
# 在脚本开头集中做所有前置检查
import os
file_path = sys.argv[1] if len(sys.argv) > 1 else ""
assert_condition(bool(file_path),
"缺少文件路径参数",
"用法:python script.py <文件路径>")
assert_condition(os.path.exists(file_path),
f"文件不存在:{file_path}",
"请检查路径是否正确,或重新上传文件")
assert_condition(file_path.endswith(".csv"),
"文件格式不支持,期望 .csv",
f"实际文件:{file_path}")
# 所有检查通过,执行实际逻辑
print(json.dumps({"status": "ok", "file": file_path}))
模式速查表
| 模式 | 核心原则 | 解决的问题 |
|---|---|---|
| 单一职责 | 一个 Skill 只做一件事 | 触发不准确、难以维护 |
| 防御性输入检查 | 入口处集中验证 | 运行到一半才报错 |
| 渐进式输出 | 分阶段汇报进度 | 用户不知道是否在执行 |
| 幂等操作 | 同输入同输出 | 重试产生混乱结果 |
| 明确结束信号 | 完成后给出终态通知 | 用户不知道任务是否完成 |
| 版本兼容声明 | 声明支持的运行环境 | 环境不匹配时行为异常 |
| Fail Fast | 遇到错误立即停止 | 带着错误继续产生无效输出 |
