部落格

Claude API 中的 Agent Skills:如何不使用 Claude Code 就能使用現成技能

Markdown

如何將現成的 pptx 技能連接至 Claude API:container.skills、程式碼執行工具、檢查回應,以及透過 Files API 下載簡報。

閱讀全文

重點

文件用的現成 Agent Skills 可以透過直接呼叫 Claude API 使用:在 container.skills 中指定技能,並啟用 code_execution。第一次提出請求時,請使用官方範例中的相容模型與工具組合。生成的檔案需另外使用其 file_id 下載;範例使用相同的 API 金鑰提出請求並下載檔案。

您可以在 Ailoot 文件中找到更多指引。

透過 Claude API 使用 Agent Skills 需要準備什麼?

本指南說明如何從程式直接呼叫 API,而不是在 Claude Code 中安裝技能。您需要 Claude API 金鑰、所選模型的使用權限,以及 Python 3。以下程式碼使用 Python 標準函式庫和 HTTP,因此不受您安裝的 SDK 版本影響。金鑰透過 ANTHROPIC_API_KEY 環境變數傳入;請勿將金鑰放在程式文字或對話中。

API 提供 Anthropic 的現成技能:簡報用的 pptx、試算表用的 xlsx、Word 文件用的 docx,以及 PDF 用的 pdf。這些技能透過 type: "anthropic" 連接。本指南使用現成技能,不會上傳自訂技能。

撰寫本資料時開啟的官方快速入門指南,使用的是 claude-opus-5-5 與 code_execution_20260521 的組合。以下範例也使用這組搭配。此範例已對照文件檢查,並非付費 API 執行結果的報告。

需要哪些 Beta 標頭

範例遵循快速入門指南目前的 HTTP 契約:標頭為 x-api-key、anthropic-version: 2023-06-01 和 content-type: application/json;不會傳送 anthropic-beta。請勿混用不同版本文件中的模型、呼叫方式和回應格式。

Files API 文件也指出,API 已不再處於 Beta 階段。舊的 files-api-2025-04-14 標頭會保留舊版回應格式;移除該標頭則會切換為目前的格式。因此,使用 client.beta.files 的舊範例不能直接與新範例混用。

本指南不會將既有服務從舊版 Beta 契約遷移。如果您已經有這類服務,請先檢查它使用的標頭和回應格式。SDK 方法名稱中有 beta 一字,本身並不足以判斷;Files API 文件另有說明不同 SDK 版本的行為。

建立簡報並下載檔案

此範例會送出一項建立五張再生能源主題投影片的任務,將完整回應儲存到 skill-response.json,接著擷取生成檔案的 ID。它不會從模型回應中取得儲存路徑,而是將檔案存到新建的本機資料夾 skill-output。如果該資料夾已存在,程式就會停止,以免覆寫先前的結果。 資料夾在 API 呼叫之前就會建立,因此呼叫失敗後也會留下。再次執行前,先檢查並保留該次嘗試;不要只為了讓重跑通過就刪除資料夾。

import json
import os
from pathlib import Path
from urllib.error import HTTPError
from urllib.request import Request, urlopen

API = "https://api.anthropic.com/v1"
KEY = os.environ["ANTHROPIC_API_KEY"]
HEADERS = {
    "x-api-key": KEY,
    "anthropic-version": "2023-06-01",
    "content-type": "application/json",
}

def request(path, payload=None, binary=False):
    data = None if payload is None else json.dumps(payload).encode("utf-8")
    req = Request(API + path, data=data, headers=HEADERS)
    try:
        with urlopen(req, timeout=600) as result:
            body = result.read()
    except HTTPError as error:
        try:
            details = json.loads(error.read().decode("utf-8"))
        except (ValueError, UnicodeError):
            details = {}
        info = details.get("error", {})
        request_id = error.headers.get("request-id") or details.get("request_id")
        diagnostic = {
            "http_status": error.code,
            "request_id": request_id,
            "error_type": info.get("type"),
            "message": str(info.get("message", "No JSON error message")).replace(KEY, "[REDACTED]")[:1000],
        }
        (out / "skill-error.json").write_text(
            json.dumps(diagnostic, ensure_ascii=False, indent=2), encoding="utf-8"
        )
        raise RuntimeError(
            f"Claude API HTTP {error.code}; request_id={request_id}; inspect skill-error.json"
        ) from None
    return body if binary else json.loads(body)

out = Path("skill-output")
out.mkdir(exist_ok=False)
response = request("/messages", {
    "model": "claude-opus-5-5",
    "max_tokens": 16000,
    "container": {
        "skills": [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}]
    },
    "messages": [{
        "role": "user",
        "content": "請以我偏好的語言製作一份包含 5 張投影片、主題為再生能源的簡報。",
    }],
    "tools": [{"type": "code_execution_20260521", "name": "code_execution"}],
})
(out / "skill-response.json").write_text(
    json.dumps(response, ensure_ascii=False, indent=2), encoding="utf-8"
)
print("stop_reason:", response.get("stop_reason"))

file_ids = []
for block in response["content"]:
    if block["type"] == "bash_code_execution_tool_result":
        result = block["content"]
        if result["type"] == "bash_code_execution_result":
            for item in result["content"]:
                file_id = item.get("file_id")
                if file_id and file_id not in file_ids:
                    file_ids.append(file_id)
if not file_ids:
    raise RuntimeError("No generated file_id; inspect skill-response.json")

for index, file_id in enumerate(file_ids, start=1):
    from urllib.parse import quote
    file_path = "/files/" + quote(file_id, safe="")
    metadata = request(file_path)
    if metadata.get("downloadable") is not True:
        raise RuntimeError("This file cannot be downloaded through Files API")
    target = out / f"result-{index}.pptx"
    target.write_bytes(request(file_path + "/content", binary=True))
    print("Saved:", target)

result-1.pptx 等名稱是為這項簡報任務選用的。檔案副檔名無法證明檔案內容;請開啟下載的文件,檢查其格式、投影片數量和文字。不要只憑 HTTP 200 或出現 file_id 就認定結果成功。

程式會輸出 stop_reason,也就是回應停止的原因,並儲存完整回應供檢查。它不會繼續對話,也不會自動重複付費請求。下載到檔案不代表整項任務已完成:在決定是否需要再次提出請求前,請檢查儲存的回應和文件本身。

若嘗試失敗,請檢查是否有 skill-error.json。其中記錄 HTTP 狀態、供應商提供的請求 ID,以及已移除設定金鑰並限制長度的供應商錯誤訊息。分享前仍須檢查是否含有其他私人資料。請將驗證、權限/模型、帳務上限與請求格式錯誤,對照官方錯誤參考文件,不要認為所有失敗都需要重新付費生成。

如果 skill-response.json 已有生成的 file_id,只有下載失敗,請保留回應,以相同且獲授權的工作區權限檢查中繼資料及下載步驟。不要為了下載既有輸出就重跑生成區塊。若請求結果不明,先保留請求 ID 並查清狀態,再送出另一個生成請求。只有確認需要新生成後,才將本機輸出行改為未使用的路徑,例如 out = Path("skill-output-attempt-2"),並保留 exist_ok=False。新資料夾能避免覆寫,不能防止 API 重複收費。

需要哪些檔案權限

Files API 會將檔案關聯至工作區,而非您應用程式中的個別使用者或對話。根據文件說明,任何具有該工作區存取權的 API 金鑰,都能存取上傳到其中的檔案。範例使用相同金鑰進行下載與生成。請勿接受終端使用者或其他不受信任來源提供的 file_id。請將這些 ID 儲存在伺服器上,並在自己的應用程式中維護使用者與其檔案之間的對應關係。

請另外檢查檔案中繼資料:其中必須包含 downloadable: true。文件允許下載由技能或程式碼執行工具生成的檔案;您上傳的檔案則會是 downloadable: false,嘗試下載時會回傳 400。工作區存取權和下載特定檔案的權限是兩項不同的要求。

對於多使用者應用程式,Files API 文件建議為每位客戶使用獨立工作區,作為檔案隔離邊界。這項建議只針對檔案;未檢查相關 API 前,不要將其延伸到所有實體和權限。

現成技能與自訂技能

現成技能的 ID 為 pptx、xlsx、docx 和 pdf。在技能清單參考文件中,custom 來源被描述為工作區專用,而 anthropic 則是共用且唯讀。

第一個範例中的 version: "latest" 代表該技能最新發布的版本。若要建立可重現的整合,請留意所選版本可能會變動;若要切換到固定版本,必須選擇您所用技能的一個既有版本,而非使用範例中虛構的 ID。

限制與下一步

此範例只涵蓋使用現成技能建立簡報並下載。這裡尚未測試上傳或刪除自訂技能,以及存取第三方服務。這些工作需要另外檢查相關 API。

若您的任務涉及 Claude 介面中的檔案,請參閱另一篇指南:如何將檔案上傳至 Claude。若要直接呼叫 API,請參閱如何計算 API Token 使用量。若要在終端機代理程式中工作,請閱讀如何選擇 Claude Code 技能:它們的連接方式不同。

初步檢查只需使用一個現成的 pptx 技能:檢查回應、下載允許下載的檔案,並開啟檔案。完成這項檢查後,再新增自訂技能和客戶間的存取隔離。

準備好之後,來認識 Ailoot。

更多詳情

常見問題:Claude API 中的 Agent Skills:如何不使用 Claude Code 就能使用現成技能

為什麼拿到 file_id 還不能宣告簡報完成?

ID 只代表有檔案回傳,不能證明它是可用簡報。請確認中繼資料為 downloadable: true,透過工作區權限下載,再開啟檢查檔案格式、投影片數量與文字。

Claude Files API 上傳的任何檔案都可以下載嗎?

不是。指南區分標示 downloadable: true 的生成檔案,與標示 downloadable: false 的上傳檔案。工作區存取與特定檔案的下載權限需分開確認,不要接受終端使用者任意提供的檔案 ID。

所有文章