第 30 期 | 从 0 构建你的第一个专属 Skill(五步法)
🎯 学习目标
- 理解并掌握构建 Claude Skill 的“五步法”核心流程。
- 学会如何清晰、准确地定义 Skill 的用例(Use Case),确保其满足实际需求。
- 掌握编写高质量触发器描述(
description)的技巧,提升 Claude 对 Skill 意图的理解力。 - 学习在 Skill 中构建健壮的排错和故障处理机制,提升 Skill 的可靠性和用户体验。
📖 核心概念讲解
30.1 Skill 设计的“五步法”概览
在 Claude 的生态中开发一个高效且可靠的 Skill,并非仅仅是编写一段代码那么简单。它需要一个系统化的方法论来指导我们从构思到落地的全过程。我们在这里提出一个“五步法”,帮助你构建第一个专属 Skill:
- 定义用例(Define Use Case): 明确 Skill 解决什么问题,为谁解决,以及在什么场景下解决。这是所有后续工作的基础。
- 编写触发器描述(Craft Trigger Description): 设计一个清晰、准确的
description,让 Claude 能够理解 Skill 的功能和何时应该调用它。 - 实现核心逻辑(Implement Core Logic): 编写实际执行任务的代码,这可能涉及调用外部 API、处理数据等。
- 构建排错机制(Build Troubleshooting): 预测并处理可能出现的错误,提供有用的反馈和建议,提升用户体验。
- 测试与迭代(Test & Iterate): 在不同场景下测试 Skill,收集反馈并不断优化。
本期课程,我们将重点聚焦于前两步和第四步:定义用例、编写触发器描述和构建排错机制,因为它们是确保你的 Skill 能被正确理解和可靠执行的关键。
30.2 定义你的 Skill 用例:从需求到意图
一个好的 Skill 始于一个清晰、明确的用例。用例定义了你的 Skill 将解决的特定问题、目标用户以及预期的结果。模糊的用例会导致 Skill 功能不聚焦,难以被 Claude 正确识别和调用。
为什么用例如此重要?
- 指导开发: 明确了 Skill 的边界和目标。
- 提升识别率: 清晰的用例能帮助你编写更精准的
description。 - 用户满意度: 确保 Skill 真正解决了用户的痛点。
如何定义一个高效的用例? 你可以尝试使用以下模板来构建你的用例:
作为一名 [用户角色], 我希望 [Skill 的功能目标], 以便 [解决什么问题/获得什么好处]。
示例:
| 属性 | 糟糕的用例 | 优秀的用例 |
|---|---|---|
| 用户角色 | 任何人 | 作为一名 Python 开发者或数据科学家 |
| 功能目标 | 查询包信息 | 我希望能够快速查询 PyPI 上任何 Python 包的最新版本、安装命令及其简要描述 |
| 解决问题 | 知道包的信息 | 以便我能高效地更新项目依赖、了解新包的功能或排查版本兼容性问题 |
| 完整用例 | "查询 Python 包信息。" | "作为一名 Python 开发者或数据科学家,我希望能够快速查询 PyPI 上任何 Python 包的最新版本、安装命令及其简要描述,以便我能高效地更新项目依赖、了解新包的功能或排查版本兼容性问题。" |
通过明确用例,我们不仅知道了 Skill 要做什么,更知道了它为什么要做以及做好的标准是什么。这为我们后续编写 description 和实现逻辑打下了坚实的基础。
30.3 精雕细琢触发器描述(Description):Claude 理解力的核心
description 字段是 Claude 决定何时调用你的 Skill 的“说明书”。它告诉 Claude 你的 Skill 能做什么、需要什么信息以及它的核心功能是什么。一个好的 description 能够极大地提高 Skill 的调用准确率和用户体验。
description 的作用:
当用户与 Claude 交互时,Claude 会根据用户的意图和所有可用 Skill 的 description 来判断哪个 Skill 最符合用户的需求。它就像一个智能的路由,将用户请求导向最合适的工具。
编写高质量 description 的最佳实践:
- 清晰、简洁、直接: 开门见山地说明 Skill 的功能。避免模糊不清的表述。
- 包含关键词: 思考用户在请求该功能时可能使用的关键词,并将其融入
description。 - 明确输入和输出: 如果 Skill 需要特定输入(如参数),或产生特定类型的输出,请在
description中提及。 - 提供示例(可选但推荐): 在
description中包含一两个用户可能说的话的例子,能进一步帮助 Claude 理解。 - 强调独特卖点: 如果你的 Skill 有独特之处,可以简要提及。
示例分析:
假设我们有一个 Skill 叫做 pypi_package_info,用于查询 PyPI 包信息。
| 差的 description | 好的 description |
| "这个 Skill 能够查询 Python 包的 PyPI 信息。" | "这个 Skill 能够查询 PyPI 上任何 Python 包的最新版本、安装命令(如 pip install <package_name>==<version>)及其简要描述。它提供关于包的基本信息,例如:requests、numpy、pandas 等。例如,你可以问:'查询 requests 包的信息' 或 '告诉我 pandas 的最新版本和安装方法'。" |
好的 description 能够清晰地告知 Claude 它的功能范围和输入预期,从而在用户提出相关问题时,精确地调用该 Skill。
30.4 构建健壮的排错机制:让 Skill 更加智能可靠
即使是最完美的 Skill 也可能遇到意料之外的情况,比如网络错误、API 不可用、用户输入无效等。一个没有排错机制的 Skill 会在这些情况下崩溃,或者提供生硬、无用的错误信息,极大地损害用户体验。
为什么排错机制至关重要?
- 提升用户体验: 提供友好的错误提示和解决方案,而不是技术性错误代码。
- 增加 Skill 可靠性: 即使面对异常情况也能优雅地降级或恢复。
- 帮助调试: 详细的错误日志有助于开发者快速定位问题。
构建排错机制的策略:
输入验证(Input Validation):
- 在执行核心逻辑之前,首先检查用户输入的参数是否符合预期。例如,如果需要一个包名,确保它不是空的或包含非法字符。
- 在 Skill 的
input_schema中使用 JSON Schema 强制进行类型和格式检查。
API 调用异常处理:
- 当 Skill 依赖外部 API 时,API 调用可能会失败(网络问题、API 限流、认证失败、资源不存在等)。
- 使用
try-except或类似机制捕获这些异常。 - 针对不同类型的错误,提供不同的处理逻辑和用户反馈。
提供有意义的错误信息:
- 避免直接抛出技术栈错误信息。
- 错误信息应该清晰地解释发生了什么,以及用户可以尝试什么操作来解决问题。
- 如果可能,提供下一步操作的建议或备选方案。
日志记录:
- 在 Skill 内部记录详细的错误日志,包括时间戳、错误类型、输入参数等。这对于后续的调试和监控非常重要。
排错机制的 ASCII 流程图:
用户请求 -> Claude -> Skill 被调用
|
V
[输入验证]
| 成功?
|---是---> [执行核心逻辑 (e.g., 调用外部 API)]
| | 成功?
| |---是---> [返回成功结果]
| |---否---> [捕获 API 异常] -> [生成友好错误信息 + 建议] -> [返回错误信息]
|
|---否---> [生成输入验证错误信息 + 建议] -> [返回错误信息]
通过以上策略,我们可以将一个脆弱的 Skill 转化为一个能够自我修复并提供良好用户体验的智能工具。
💻 模拟 Claude TUI 交互
场景一:成功查询 Python 包信息
> 帮我查一下 requests 包的最新版本和安装命令。
╭─ Claude ──────────────────────────────╮
│ 好的,我正在为您查询 `requests` 包在 PyPI 上的最新版本和安装信息。 │
│ │
│ **Tool Call:** `pypi_package_info` │
│ **Arguments:** `{"package_name": "requests"}` │
│ │
│ **Tool Output:** │
│ ```json │
│ { │
│ "package_name": "requests", │
│ "latest_version": "2.31.0", │
│ "install_command": "pip install requests==2.31.0", │
│ "description_snippet": "Python HTTP for Humans." │
│ } │
│ ``` │
│ │
│ `requests` 包的最新版本是 `2.31.0`。您可以使用命令 `pip install requests==2.31.0` 来安装它。该包的简要描述是:"Python HTTP for Humans." │
╰───────────────────────────────────────╯
场景二:查询不存在的 Python 包信息(排错机制生效)
> 我想查一个叫做 `non_existent_package_123` 的 Python 包。
╭─ Claude ──────────────────────────────╮
│ 好的,我正在为您查询 `non_existent_package_123` 包在 PyPI 上的信息。 │
│ │
│ **Tool Call:** `pypi_package_info` │
│ **Arguments:** `{"package_name": "non_existent_package_123"}` │
│ │
│ **Tool Output:** │
│ ```json │
│ { │
│ "error_type": "PackageNotFound", │
│ "message": "抱歉,在 PyPI 上未能找到名为 'non_existent_package_123' 的包。请检查包名是否正确。", │
│ "suggestion": "您可以尝试在 PyPI 官网 (pypi.org) 上搜索,或检查是否存在拼写错误。" │
│ } │
│ ``` │
│ │
│ 抱歉,我未能找到名为 `non_existent_package_123` 的 Python 包。请检查包名是否正确。 │
│ 您可以尝试在 PyPI 官网 (pypi.org) 上搜索,或检查是否存在拼写错误。 │
╰───────────────────────────────────────╯
场景三:用户输入无效参数(输入验证生效)
> 帮我查包,但是没有告诉我包名。
╭─ Claude ──────────────────────────────╮
│ 您想查询哪个 Python 包呢?请提供包的名称。例如,您可以说:"查询 requests 包的信息"。 │
│ │
│ **Tool Call:** `pypi_package_info` │
│ **Arguments:** `{}` │
│ │
│ **Tool Output:** │
│ ```json │
│ { │
│ "error_type": "InputValidationError", │
│ "message": "缺少必要的参数 'package_name'。请提供要查询的 Python 包名称。", │
│ "suggestion": "请提供一个有效的 Python 包名称,例如 'requests' 或 'numpy'。" │
│ } │
│ ``` │
│ │
│ 我需要知道您想查询哪个 Python 包。请提供包的名称,例如:"查询 `requests` 包的信息" 或 "告诉我 `pandas` 的最新版本"。 │
╰───────────────────────────────────────╯
💻 代码演示
我们将创建一个名为 pypi_package_info 的 Skill,它能够查询 PyPI 包的最新版本和安装命令。这个 Skill 将包含 description、input_schema 和 tool_code,其中 tool_code 会演示如何处理包不存在的错误。
Skill 定义文件: pypi_package_info.yaml
# pypi_package_info.yaml
name: pypi_package_info
description: |
这个 Skill 能够查询 PyPI (Python Package Index) 上任何 Python 包的**最新版本**、**安装命令**及其**简要描述**。
它适用于需要快速获取 Python 包信息的场景,例如:'查询 requests 包的信息' 或 '告诉我 pandas 的最新版本和安装方法'。
如果包不存在,它会返回一个友好的错误提示。
input_schema:
type: object
properties:
package_name:
type: string
description: 要查询的 Python 包的名称,例如 "requests" 或 "numpy"。
required:
- package_name
tool_code: |
import requests
import json
import sys
def get_pypi_package_info(package_name: str):
"""
从 PyPI 获取指定 Python 包的最新版本、安装命令和描述。
"""
if not package_name or not isinstance(package_name, str):
# 输入验证 (虽然 input_schema 已经做了大部分,但这里是代码层面的二次确认)
return json.dumps({
"error_type": "InputValidationError",
"message": "缺少必要的参数 'package_name'。请提供要查询的 Python 包名称。",
"suggestion": "请提供一个有效的 Python 包名称,例如 'requests' 或 'numpy'。"
})
pypi_url = f"https://pypi.org/pypi/{package_name}/json"
try:
response = requests.get(pypi_url, timeout=5) # 设置超时
response.raise_for_status() # 检查 HTTP 错误状态
data = response.json()
info = data.get('info', {})
latest_version = info.get('version')
summary = info.get('summary')
if latest_version:
install_command = f"pip install {package_name}=={latest_version}"
result = {
"package_name": package_name,
"latest_version": latest_version,
"install_command": install_command,
"description_snippet": summary
}
return json.dumps(result)
else:
# 理论上不会走到这里,因为有 info.get('version')
return json.dumps({
"error_type": "DataParsingError",
"message": f"未能从 PyPI 获取到 '{package_name}' 的版本信息,可能是数据格式异常。",
"suggestion": "请稍后再试或访问 PyPI 官网手动查询。"
})
except requests.exceptions.HTTPError as e:
if e.response.status_code == 404:
return json.dumps({
"error_type": "PackageNotFound",
"message": f"抱歉,在 PyPI 上未能找到名为 '{package_name}' 的包。请检查包名是否正确。",
"suggestion": "您可以尝试在 PyPI 官网 (pypi.org) 上搜索,或检查是否存在拼写错误。"
})
else:
return json.dumps({
"error_type": "APIError",
"message": f"查询 PyPI 时发生 HTTP 错误:{e.response.status_code} - {e.response.reason}。请稍后再试。",
"suggestion": "请检查您的网络连接,或稍后再试。如果问题持续存在,可能是 PyPI 服务暂时不可用。"
})
except requests.exceptions.ConnectionError:
return json.dumps({
"error_type": "NetworkError",
"message": "无法连接到 PyPI 服务。请检查您的网络连接。",
"suggestion": "请检查您的网络连接是否正常,并尝试再次查询。"
})
except requests.exceptions.Timeout:
return json.dumps({
"error_type": "TimeoutError",
"message": "连接 PyPI 服务超时。可能网络较慢或 PyPI 服务繁忙。",
"suggestion": "请稍后再试,或检查您的网络连接。"
})
except Exception as e:
# 捕获所有其他未预料的错误
return json.dumps({
"error_type": "UnexpectedError",
"message": f"查询 '{package_name}' 时发生未知错误:{str(e)}。",
"suggestion": "请联系 Skill 开发者报告此问题,或尝试查询其他包。"
})
# 主函数,Claude 会调用这个函数
def main(package_name: str):
return get_pypi_package_info(package_name)
部署 Skill
将上述内容保存为 pypi_package_info.yaml 文件。然后使用 Claude CLI 工具部署:
# 创建并部署你的 Skill
claude deploy skill --file pypi_package_info.yaml
测试 Skill
# 在本地测试 Skill
# 成功情况
claude test skill pypi_package_info --input '{"package_name": "requests"}'
# 包不存在情况
claude test skill pypi_package_info --input '{"package_name": "non_existent_package_123"}'
# 缺少参数情况 (会由 input_schema 层面或 tool_code 层面捕获)
claude test skill pypi_package_info --input '{}'
这段代码演示了如何:
- 在
description中提供详细的 Skill 功能描述和使用示例。 - 使用
input_schema定义和验证输入参数。 - 在
tool_code中使用requests库调用外部 API。 - 通过
try-except块捕获requests库可能抛出的各种异常(HTTPError、ConnectionError、Timeout等)。 - 针对不同的错误类型,返回结构化的错误信息,包含