第 28 期 | 告别 Prompt 工程,步入 Skills 工程

更新于 2026/4/5

🎯 学习目标

  1. 理解传统 Prompt Engineering 的局限性及其难以持续的根本原因。
  2. 掌握 Agent Skills 的核心概念,以及它如何实现模块化和可复用性。
  3. 深入解析 Agent Skills 中 YAML Frontmatter (元数据) 和 Instructions (指令) 的底层运行机制。
  4. 学会如何设计和实现高效的 Agent Skills,以应对复杂任务。

📖 核心概念讲解

28.1 为什么 Prompt 工程难以为继?

在大型语言模型 (LLM) 发展的早期阶段,"Prompt Engineering" 是一个炙手可热的领域。我们通过精心构造的文本指令(即 Prompt)来引导模型完成特定任务。然而,随着模型能力的增强和应用场景的复杂化,Prompt Engineering 的局限性日益凸显,使其成为一个难以规模化和维护的瓶颈:

  1. 可维护性差与脆性 (Brittleness):

    • 单个 Prompt 通常是长而复杂的文本块,其中包含了任务描述、角色设定、约束条件、示例等。一旦任务需求发生微小变化,可能需要对整个 Prompt 进行大量修改,甚至导致模型行为的剧烈变化。
    • 不同模型版本、不同上下文窗口大小、甚至微小的措辞调整都可能影响 Prompt 的效果,使得维护变得异常困难。
  2. 可复用性低:

    • 每个 Prompt 都是针对特定任务定制的。即使两个任务有相似的子步骤,也很难直接复用 Prompt 中的某个片段。这意味着每当需要新功能时,都倾向于从头开始编写 Prompt。
  3. 可组合性差:

    • 对于需要多步操作或结合多种工具的复杂任务,传统的 Prompt Engineering 往往需要将所有逻辑硬编码在一个巨大的 Prompt 中,或者通过外部编排逻辑进行多次 Prompt 调用。这使得任务流难以清晰表达,且容易超出模型的上下文窗口限制。
  4. 上下文窗口限制与成本:

    • 随着任务复杂度的增加,Prompt 往往会变得越来越长,这不仅消耗更多的 Token 导致更高的成本,也更容易触及模型的上下文窗口上限,从而影响模型理解和表现。
  5. 调试与可解释性挑战:

    • 当模型输出不符合预期时,很难准确判断是 Prompt 的哪个部分出了问题,或者模型在哪个环节理解偏差。庞大的文本块使得调试如同大海捞针。
  6. 人机协作效率低下:

    • Prompt Engineering 本质上是人类通过自然语言与模型进行“对话”,试图让模型理解并执行任务。这种方式效率较低,且高度依赖工程师的经验和直觉。

简而言之,Prompt Engineering 是一种非结构化的、命令式的编程方式。它就像在没有函数、模块和类概念的语言中编写所有代码。当项目规模变大时,这种方式必然会走向崩溃。我们需要一种更结构化、模块化、可编程的方式来与 LLM 交互,这就是 Agent Skills 出现的原因。

28.2 Agent Skills: 模块化智能的基石

Agent Skills 是 Anthropic 推出的一种新的范式,旨在解决传统 Prompt Engineering 的诸多痛点,将 LLM 的能力从简单的指令执行者提升为智能代理 (Agent)。一个 Agent Skill 可以被理解为 Agent 所掌握的特定能力或工具,它封装了执行某个任务所需的所有知识和逻辑。

每个 Agent Skill 主要由两部分组成:

28.2.1 YAML Frontmatter (元数据)

YAML Frontmatter 是技能的声明性部分,它以结构化的数据格式(YAML)描述了技能的“是什么”以及“能做什么”。这部分信息是机器可读的,主要用于 Agent 的规划 (Planning) 阶段。

它通常包含以下关键信息:

  • name: 技能的唯一标识符。
  • description: 技能的简短描述,清晰地说明了技能的用途和适用场景。这是 Agent 在决定是否使用该技能时的主要参考。
  • parameters: 技能所需的输入参数列表,包括每个参数的名称、类型、描述以及是否为必需项。Agent 会根据用户请求从对话中提取这些参数。
  • returns: 技能可能返回的数据类型和描述,帮助 Agent 理解技能的输出。
  • dependencies (可选): 技能可能依赖的其他技能或工具。

示例结构:

# skill_name.yaml
name: get_github_repo_info
description: 获取指定 GitHub 仓库的详细信息,例如星标数、描述、最新版本等。
parameters:
  repo_owner:
    type: string
    description: GitHub 仓库的所有者(例如:Anthropic)。
    required: true
  repo_name:
    type: string
    description: GitHub 仓库的名称(例如:claude-sdk-python)。
    required: true
returns:
  type: object
  description: 包含仓库名称、描述、星标数、URL 和最新发布版本等信息的 JSON 对象。

28.2.2 Instructions (指令)

Instructions 是技能的指令性部分,通常以 Markdown 格式编写。它详细描述了技能“如何做”的执行逻辑。这部分信息是人类和 LLM 可读的,主要用于 Agent 的执行 (Execution) 阶段。

Instructions 通常包含:

  • 详细的任务步骤:如何一步步地完成任务。
  • 工具调用指南:明确指出需要调用哪些外部工具(Tools),以及如何构造工具调用的参数。这通常通过 tool_code 或特定的语法糖来表示。
  • 内部推理逻辑:在没有外部工具的情况下,Agent 应该如何进行内部推理、数据处理或生成内容。
  • 错误处理:当工具调用失败或遇到异常情况时,Agent 应该如何响应。
  • 输出格式要求:明确最终结果应该以何种格式呈现给用户。

示例结构:

# skill_name_instructions.md
当用户需要获取 GitHub 仓库的详细信息时,请使用此技能。

**步骤:**
1.  从用户输入中提取 `repo_owner` 和 `repo_name` 参数。
2.  使用 `get_github_repo_data` 工具来查询 GitHub API。
    *   **工具签名:** `get_github_repo_data(owner: str, repo: str)`
    *   **用途:** 调用 GitHub REST API 获取仓库数据。
    *   **示例:** `get_github_repo_data(owner="Anthropic", repo="claude-sdk-python")`
3.  如果工具调用成功,解析返回的 JSON 数据,并提取以下关键信息:
    *   `full_name`
    *   `description`
    *   `stargazers_count` (星标数)
    *   `html_url` (仓库链接)
    *   `latest_release_tag` (最新发布版本标签,如果存在)
4.  将提取的信息以简洁明了的方式呈现给用户,例如:
    ```
    仓库名称:[full_name]
    描述:[description]
    星标数:[stargazers_count] ⭐
    仓库链接:[html_url]
    最新版本:[latest_release_tag]
    ```
5.  如果工具调用失败(例如,仓库不存在或 API 错误),请告知用户查询失败,并提示他们检查仓库名称是否正确。

概念图:Agent Skills 的双重角色

+---------------------+      +---------------------+
|   用户请求 / 目标   |<-----+   Agent (Claude)    |
+---------------------+      |                     |
           |                 |  1. 感知 (Perceive) |
           v                 +----------^----------+
+---------------------+                 |
|   所有可用 Skills   |                 |
|                     |                 |
| [ Skill A ]         |                 |
|   - YAML Frontmatter|                 |
|     (name, desc,    |                 |
|      params, ...)   |                 |
|                     |                 |
| [ Skill B ]         |                 |
|   - YAML Frontmatter|                 |
|     (name, desc,    |                 |
|      params, ...)   |                 |
|                     |                 |
|       ...           |                 |
+---------------------+                 |
           |                            |  2. 规划 (Plan)
           | (Agent 阅读 YAML descriptions)
           v                            |
+-------------------------------------------------+
| Claude 内部决策:哪个 Skill 最适合当前任务?     |
|   - 匹配用户意图                                |
|   - 提取并绑定参数                              |
|   - 确定执行顺序                                |
|   -> 生成 tool_code / tool_use 意图             |
+-------------------------------------------------+
           |                                |
           | (Agent 根据规划,加载选定 Skill 的 Instructions)
           v                                |  3. 执行 (Execute)
+-------------------------------------------------+
| Claude 执行选定 Skill 的 Instructions            |
|   - 调用外部工具 (根据 Instructions 中的 tool_code) |
|   - 根据 Instructions 进行内部推理和数据处理      |
|   - 生成响应或进一步的行动                        |
+-------------------------------------------------+
           |                                |
           v                                |  4. 观察 (Observe)
+-------------------------------------------------+
| 工具输出 / 内部处理结果                          |
+-------------------------------------------------+
           |                                |
           v                                |  5. 反思 / 完善 (Reflect / Refine)
+-------------------------------------------------+
| Claude 评估结果,决定是否完成任务或需要下一步行动 |
+-------------------------------------------------+

通过这种分离,Agent Skills 实现了:

  • 规划与执行的分离:YAML 元数据用于高效的规划和工具选择,而详细指令用于精确的执行。
  • 模块化和可复用性:每个 Skill 都是一个独立的功能单元,可以在不同的任务中被调用。
  • 可维护性:修改一个 Skill 的内部逻辑不会影响其他 Skill,易于更新和调试。
  • 可组合性:复杂任务可以通过组合多个 Skill 来完成,提高了 Agent 的能力边界。

28.3 Skills 的底层运行机制:规划与执行

现在我们更深入地探讨 Agent 是如何利用 Skills 的 YAML Frontmatter 和 Instructions 来工作的。这个过程可以被概括为一个迭代的“感知-规划-执行-观察-反思”循环。

28.3.1 规划阶段:基于 YAML Frontmatter 的决策

当 Agent 收到用户请求时,它首先进入规划 (Planning) 阶段。在这个阶段,Agent 会:

  1. 感知用户意图 (Perception): Agent 解析用户输入的自然语言,理解其核心目标和所需信息。
  2. 遍历可用 Skills 的 YAML Frontmatter: Agent 会查看其所有已注册 Skills 的 namedescription 字段。这些字段是高度概括性的,旨在帮助 Agent 快速判断哪个 Skill 可能与当前用户意图相关。
    • 例如,如果用户问“给我 Anthropic 的 GitHub 仓库信息”,Agent 会在所有 Skills 的 description 中寻找能“获取 GitHub 仓库信息”的 Skill。
  3. 参数提取与绑定: 一旦 Agent 识别出潜在的 Skill,它会进一步检查该 Skill 的 parameters 定义。Agent 尝试从用户请求中提取所需参数的值,并将其与 Skill 的参数进行绑定。
    • 例如,对于 get_github_repo_info Skill,Agent 会从“Anthropic 的 GitHub 仓库”中提取 repo_owner="Anthropic"repo_name(如果用户明确给出)。如果 repo_name 未明确给出,Agent 可能会尝试从上下文推断或请求用户提供。
  4. 生成工具使用意图 (Tool Use Intent): 基于匹配的 Skill 和提取的参数,Agent 内部会生成一个结构化的“工具使用意图”。这通常以 tool_code 或类似的内部表示形式体现,指示 Agent 接下来需要调用哪个工具,以及带什么参数。
    • 例如,它可能生成 tool_code: get_github_repo_data(owner="Anthropic", repo="claude-sdk-python")
    • 关键点: 在这个阶段,Agent 还没有真正“读取”或“执行”Skill 的 Instructions 部分。它只依赖于 YAML 中的 name, description, parameters 来进行高层决策。这大大提高了规划效率,避免了每次都加载并解析冗长的自然语言指令。

28.3.2 执行阶段:遵循 Instructions 的具体操作

一旦 Agent 确定了要使用的 Skill 并生成了工具使用意图,它就进入执行 (Execution) 阶段。在这个阶段,Agent 会:

  1. 加载并理解 Skill 的 Instructions: Agent 会加载并深入理解所选 Skill 的完整 Instructions 内容。这些 Instructions 提供了详细的执行蓝图。
  2. 执行指令中的步骤:
    • 工具调用: 如果 Instructions 中包含 tool_code 或明确的工具调用指示,Agent 会根据这些指示实际调用外部工具(例如,通过 API 网关执行 get_github_repo_data 函数)。
    • 内部推理与数据处理: Instructions 也可以指导 Agent 进行纯粹的内部思考、数据转换、条件判断等,而无需调用外部工具。例如,对工具返回的数据进行解析、过滤、格式化。
    • 生成响应: 最终,Instructions 会指导 Agent 如何将处理后的信息组织成一个用户友好的响应。
  3. 观察工具输出 (Observation): Agent 接收并处理工具调用的返回结果或内部处理的中间状态。这些结果成为 Agent 下一步行动的输入。
  4. 反思与完善 (Reflection/Refinement): Agent 根据 Observations 评估任务是否完成。如果任务完成,则生成最终响应。如果需要进一步操作(例如,根据工具输出决定调用另一个 Skill,或者需要用户提供更多信息),Agent 会回到规划阶段,开始新的循环。

规划与执行的“对话”

Agent Skills 的核心在于,它将 LLM 的能力从一个被动的“Prompt 处理器”转变为一个主动的“问题解决者”。YAML Frontmatter 赋予 Agent 快速理解和选择工具的能力,而详细的 Instructions 则指导 Agent 精确地执行任务。这种分层设计,使得 Agent 能够更高效、更可靠地处理复杂多变的用户请求,真正实现“告别 Prompt 工程,步入 Skills 工程”。


💻 模拟 Claude TUI 交互

以下模拟展示了 Claude Agent 如何利用 get_github_repo_info Skill 来响应用户请求。

场景 1: 查询特定 GitHub 仓库信息

用户希望获取 Anthropic 的 Claude SDK Python 仓库信息。

> 请帮我查询 GitHub 上 'Anthropic/claude-sdk-python' 项目的最新版本号和星标数。

╭─ Claude ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ 正在分析您的请求,并匹配合适的技能...                                                                                     │
│                                                                                                                           │
│ 🧐 **思考 (Planning):**                                                                                                  │
│ 我检测到用户需要查询 GitHub 仓库的详细信息。我有一个名为 `get_github_repo_info` 的技能,其描述是 "获取指定 GitHub 仓库 │
│ 的详细信息,例如星标数、描述、最新版本等。" 这与用户的请求高度匹配。                                                     │
│                                                                                                                           │
│ **参数提取:**                                                                                                             │
│   - `repo_owner`: "Anthropic"                                                                                             │
│   - `repo_name`: "claude-sdk-python"                                                                                      │
│                                                                                                                           │
│ **行动 (Action):**                                                                                                        │
│ 决定调用 `get_github_repo_info` 技能。                                                                                    │
│                                                                                                                           │
│ 🛠️ **调用工具 (Executing Skill 'get_github_repo_info'):**                                                                │
│ ```tool_code                                                                                                             │
│ print(get_github_repo_data(owner="Anthropic", repo="claude-sdk-python"))                                                │
│ ```                                                                                                                       │
│                                                                                                                           │
│ **工具输出 (Observation):**                                                                                               │
│ ```json                                                                                                                   │
│ {                                                                                                                         │
│   "name": "claude-sdk-python",                                                                                            │
│   "full_name": "Anthropic/claude-sdk-python",                                                                             │
│   "description": "The official Python SDK for interacting with the Claude API.",                                          │
│   "stargazers_count": 2540,                                                                                               │
│   "forks_count": 310,                                                                                                     │
│   "html_url": "https://github.com/Anthropic/claude-sdk-python",                                                          │
│   "latest_release_tag": "v0.12.0"                                                                                         │
│ }                                                                                                                         │
│ ```                                                                                                                       │
│                                                                                                                           │
│ ✨ **整理与回应 (Reflection):**                                                                                          │
│ 工具已成功获取仓库信息。我将根据技能的 Instructions 格式化这些信息并回复用户。                                            │
│                                                                                                                           │
│ Anthropic 的 GitHub 仓库 `Anthropic/claude-sdk-python` 的详细信息如下:                                                 │
│ 仓库名称:claude-sdk-python                                                                                               │
│ 描述:The official Python SDK for interacting with the Claude API.                                                        │
│ 星标数:2540 ⭐                                                                                                            │
│ 仓库链接:https://github.com/Anthropic/claude-sdk-python                                                                  │
│ 最新版本:v0.12.0                                                                                                         │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

场景 2: 查询不存在的 GitHub 仓库信息 (错误处理)

用户查询一个不存在的仓库,展示技能的错误处理机制。

> 查一下 GitHub 上 'NonExistentOrg/NonExistentRepo' 的信息。

╭─ Claude ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ 正在分析您的请求,并匹配合适的技能...                                                                                     │
│                                                                                                                           │
│ 🧐 **思考 (Planning):**                                                                                                  │
│ 用户请求查询 GitHub 仓库信息,`get_github_repo_info` 技能最匹配。                                                        │
│                                                                                                                           │
│ **参数提取:**                                                                                                             │
│   - `repo_owner`: "NonExistentOrg"                                                                                        │
│   - `repo_name`: "NonExistentRepo"                                                                                        │
│                                                                                                                           │
│ **行动 (Action):**                                                                                                        │
│ 决定调用 `get_github_repo_info` 技能。                                                                                    │
│                                                                                                                           │
│ 🛠️ **调用工具 (Executing Skill 'get_github_repo_info'):**                                                                │
│ ```tool_code                                                                                                             │
│ print(get_github_repo_data(owner="NonExistentOrg", repo="NonExistentRepo"))                                             │
│ ```                                                                                                                       │
│                                                                                                                           │
│ **工具输出 (Observation):**                                                                                               │
│ ```error                                                                                                                 │
│ Error: GitHub API returned 404 Not Found. Repository 'NonExistentOrg/NonExistentRepo' not found.                         │
│ ```                                                                                                                       │
│                                                                                                                           │
│ ✨ **整理与回应 (Reflection):**                                                                                          │
│ 工具调用失败,根据技能的 Instructions,我需要告知用户查询失败。                                                           │
│                                                                                                                           │
│ 抱歉,我无法查询到 GitHub 仓库 'NonExistentOrg/NonExistentRepo' 的信息。                                                  │
│ 请检查仓库所有者和仓库名称是否正确,或者该仓库可能不存在。                                                                │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

💻 代码演示

这里我们将演示一个完整的 Agent Skill 定义,包括 YAML Frontmatter 和 Instructions,以及一个模拟的 Python 工具函数,用于理解其工作原理。

1. Skill YAML Frontmatter (get_github_repo_info.yaml)

# skills/get_github_repo_info.yaml
name: get_github_repo_info
description: 获取指定 GitHub 仓库的详细信息,例如星标数、描述、最新版本等。
parameters:
  repo_owner:
    type: string
    description: GitHub 仓库的所有者(例如:Anthropic)。
    required: true
  repo_name:
    type: string
    description: GitHub 仓库的名称(例如:claude-sdk-python)。
    required: true
returns:
  type: object
  description: 包含仓库名称、描述、星标数、URL 和最新发布版本等信息的 JSON 对象。

2. Skill Instructions (get_github_repo_info_instructions.md)

# skills/get_github_repo_info_instructions.md
当用户需要获取 GitHub 仓库的详细信息时,请使用此技能。

**任务目标:**
获取用户指定的 GitHub 仓库的公开信息,并以结构化、用户友好的方式呈现。

**步骤:**

1.  **参数校验与准备:**
    *   确保 `repo_owner` 和 `repo_name` 参数已从用户请求中成功提取。
    *   如果任一参数缺失,告知用户需要提供完整的仓库所有者和名称。

2.  **工具调用:**
    *   使用 `get_github_repo_data` 工具来查询 GitHub API。
    *   **工具签名:** `get_github_repo_data(owner: str, repo: str)`
    *   **用途:** 调用 GitHub REST API 获取指定仓库的详细数据。
    *   **示例调用 (内部表示):**
        ```tool_code
        print(get_github_repo_data(owner=repo_owner, repo=repo_name))
        ```
    *   **注意:** Claude 会自动将 `repo_owner` 和 `repo_name` 的值替换为实际提取到的参数。

3.  **结果处理与格式化:**
    *   **成功场景:**
        *   如果 `get_github_repo_data` 工具返回一个 JSON 对象,表示查询成功。
        *   从返回的 JSON 中提取以下关键信息:
            *   `full_name` (完整仓库名,例如 `Anthropic/claude-sdk-python`)
            *   `description` (仓库描述)
            *   `stargazers_count` (星标数)
            *   `forks_count` (Fork 数)
            *   `html_url` (仓库的 Web 链接)
            *   `latest_release_tag` (最新发布版本标签,如果工具能提供)
        *   将提取的信息以以下格式呈现给用户:
            ```
            GitHub 仓库信息:
            - 完整名称:[full_name]
            - 描述:[description]
            - 星标数:[stargazers_count] ⭐
            - Fork 数:[forks_count]
            - 仓库链接:[html_url]
            - 最新版本:[latest_release_tag]
            ```
        *   如果 `latest_release_tag` 不存在,则在输出中省略该行。

    *   **失败场景:**
        *   如果 `get_github_repo_data` 工具返回错误(例如,HTTP 404 Not Found),表示仓库不存在或查询失败。
        *   向用户报告查询失败,并建议他们检查仓库所有者和名称是否正确。
        *   **示例错误信息:** "抱歉,我无法查询到 GitHub 仓库 '[repo_owner]/[repo_name]' 的信息。请检查仓库所有者和仓库名称是否正确,或者该仓库可能不存在。"

3. 模拟的 Python 工具函数 (tools.py)

这个 Python 函数模拟了 get_github_repo_data 工具,它会被 Agent 在执行 get_github_repo_info 技能时调用。在真实场景中,这会是一个与外部 API 交互的实际函数。

# tools.py
import requests
import json

def get_github_repo_data(owner: str, repo: str) -> str:
    """
    模拟一个通过 GitHub API 获取仓库数据的工具。
    在真实场景中,这会调用 requests.get 并处理认证。
    """
    url = f"https://api.github.com/repos/{owner}/{repo}"
    headers = {
        "Accept": "application/vnd.github.v3+json",
        # 在实际应用中,这里需要添加 GitHub Personal Access Token 进行认证
        # "Authorization": "token YOUR_GITHUB_TOKEN"
    }

    try:
        response = requests.get(url, headers=headers)
        response.raise_for_status() # 如果是 4xx 或 5xx 状态码,抛出 HTTPError

        data = response.json()
        
        # 模拟获取最新 release tag
        release_url = f"https://api.github.com/repos/{owner}/{repo}/releases/latest"
        release_response = requests.get(release_url, headers=headers)
        latest_release_tag = None
        if