第 30 期 | 从 0 构建你的第一个专属 Skill(五步法)

更新于 2026/4/5

🎯 学习目标

  • 理解并掌握构建 Claude Skill 的“五步法”核心流程。
  • 学会如何清晰、准确地定义 Skill 的用例(Use Case),确保其满足实际需求。
  • 掌握编写高质量触发器描述(description)的技巧,提升 Claude 对 Skill 意图的理解力。
  • 学习在 Skill 中构建健壮的排错和故障处理机制,提升 Skill 的可靠性和用户体验。

📖 核心概念讲解

30.1 Skill 设计的“五步法”概览

在 Claude 的生态中开发一个高效且可靠的 Skill,并非仅仅是编写一段代码那么简单。它需要一个系统化的方法论来指导我们从构思到落地的全过程。我们在这里提出一个“五步法”,帮助你构建第一个专属 Skill:

  1. 定义用例(Define Use Case): 明确 Skill 解决什么问题,为谁解决,以及在什么场景下解决。这是所有后续工作的基础。
  2. 编写触发器描述(Craft Trigger Description): 设计一个清晰、准确的 description,让 Claude 能够理解 Skill 的功能和何时应该调用它。
  3. 实现核心逻辑(Implement Core Logic): 编写实际执行任务的代码,这可能涉及调用外部 API、处理数据等。
  4. 构建排错机制(Build Troubleshooting): 预测并处理可能出现的错误,提供有用的反馈和建议,提升用户体验。
  5. 测试与迭代(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 的最佳实践:

  1. 清晰、简洁、直接: 开门见山地说明 Skill 的功能。避免模糊不清的表述。
  2. 包含关键词: 思考用户在请求该功能时可能使用的关键词,并将其融入 description
  3. 明确输入和输出: 如果 Skill 需要特定输入(如参数),或产生特定类型的输出,请在 description 中提及。
  4. 提供示例(可选但推荐): 在 description 中包含一两个用户可能说的话的例子,能进一步帮助 Claude 理解。
  5. 强调独特卖点: 如果你的 Skill 有独特之处,可以简要提及。

示例分析:

假设我们有一个 Skill 叫做 pypi_package_info,用于查询 PyPI 包信息。

| 差的 description | 好的 description | | "这个 Skill 能够查询 Python 包的 PyPI 信息。" | "这个 Skill 能够查询 PyPI 上任何 Python 包的最新版本安装命令(如 pip install <package_name>==<version>)及其简要描述。它提供关于包的基本信息,例如:requestsnumpypandas 等。例如,你可以问:'查询 requests 包的信息' 或 '告诉我 pandas 的最新版本和安装方法'。" |

好的 description 能够清晰地告知 Claude 它的功能范围和输入预期,从而在用户提出相关问题时,精确地调用该 Skill。

30.4 构建健壮的排错机制:让 Skill 更加智能可靠

即使是最完美的 Skill 也可能遇到意料之外的情况,比如网络错误、API 不可用、用户输入无效等。一个没有排错机制的 Skill 会在这些情况下崩溃,或者提供生硬、无用的错误信息,极大地损害用户体验。

为什么排错机制至关重要?

  • 提升用户体验: 提供友好的错误提示和解决方案,而不是技术性错误代码。
  • 增加 Skill 可靠性: 即使面对异常情况也能优雅地降级或恢复。
  • 帮助调试: 详细的错误日志有助于开发者快速定位问题。

构建排错机制的策略:

  1. 输入验证(Input Validation):

    • 在执行核心逻辑之前,首先检查用户输入的参数是否符合预期。例如,如果需要一个包名,确保它不是空的或包含非法字符。
    • 在 Skill 的 input_schema 中使用 JSON Schema 强制进行类型和格式检查。
  2. API 调用异常处理:

    • 当 Skill 依赖外部 API 时,API 调用可能会失败(网络问题、API 限流、认证失败、资源不存在等)。
    • 使用 try-except 或类似机制捕获这些异常。
    • 针对不同类型的错误,提供不同的处理逻辑和用户反馈。
  3. 提供有意义的错误信息:

    • 避免直接抛出技术栈错误信息。
    • 错误信息应该清晰地解释发生了什么,以及用户可以尝试什么操作来解决问题。
    • 如果可能,提供下一步操作的建议或备选方案。
  4. 日志记录:

    • 在 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 将包含 descriptioninput_schematool_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 库可能抛出的各种异常(HTTPErrorConnectionErrorTimeout 等)。
  • 针对不同的错误类型,返回结构化的错误信息,包含