ブログ

Claude APIのAgent Skills:Claude Codeなしで既製スキルを使う方法

Markdown

Claude APIに既製のpptxスキルを接続する方法:container.skills、コード実行ツール、応答の確認、Files APIを使ったプレゼンテーションのダウンロード。

記事の全文を読む

重要なポイント

ドキュメント向けの既製Agent Skillsは、Claude APIを直接使って利用できます。container.skillsでスキルを指定し、code_executionを有効にしてください。最初のリクエストでは、公式の例に記載されている互換性のあるモデルとツールの組み合わせを使います。生成されたファイルはfile_idを使って別途ダウンロードします。この例では、リクエストとダウンロードに同じAPIキーを使います。

詳しくはAilootのドキュメントをご覧ください。

必要なもの

このガイドでは、Claude Codeにスキルをインストールするのではなく、プログラムからAPIを直接呼び出します。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を実行した報告ではありません。

必要なベータヘッダー

この例はクイックスタートの現在のHTTP仕様に従っています。ヘッダーはx-api-key、anthropic-version: 2023-06-01、content-type: application/jsonで、anthropic-betaは送信しません。異なる世代のドキュメントにあるモデル、呼び出し方法、応答形式を組み合わせないでください。

Files APIのドキュメントにも、APIはベータ版ではなくなったと記載されています。旧files-api-2025-04-14ヘッダーを使うと旧形式の応答が維持されますが、ヘッダーを削除すると現在の形式に切り替わります。そのため、client.beta.filesを使う古い例を新しい例とそのまま組み合わせることはできません。

ここでは、旧ベータ仕様から既存サービスを移行するわけではありません。そのようなサービスをすでにお使いの場合は、まずヘッダーと応答形式を確認してください。SDKのメソッド名にbetaが含まれているだけでは判断できません。Files APIのドキュメントでは、SDKのバージョンごとの動作について別途説明されています。

プレゼンテーションを作成してファイルをダウンロードする

この例では、再生可能エネルギーについて5枚のスライドを作成するタスクを送信し、応答全体を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、設定したAPIキーを除去して長さを制限したプロバイダーのエラーメッセージが記録されます。共有前に、ほかの私的な情報が含まれていないかも確認してください。認証、権限・モデル、請求上限、リクエスト形式のエラーを公式エラーリファレンスと照合し、どの失敗にも新しい有料生成が必要だとは判断しないでください。

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トークン使用量の計算方法をご覧ください。ターミナルエージェントで作業する場合は、Claude Codeスキルの選び方をお読みください。スキルの接続方法が異なります。

最初の確認には、既製のpptxスキルを1つ使えば十分です。応答を確認し、ダウンロードが許可されたファイルをダウンロードして開いてください。カスタムスキルやクライアント間のアクセス分離は、その確認を終えてから追加します。

準備ができたら、Ailootをご覧ください。

詳細を見る

Claude APIのAgent Skills:Claude Codeなしで既製スキルを使う方法:よくある質問

file_idが返ればプレゼンテーションは完成ですか?

IDはファイルが返されたことを示すだけです。メタデータのdownloadable: trueを確認し、ワークスペースへのアクセス権でダウンロードして開き、形式、スライド数、文章を確認してください。

Claude Files APIへアップロードされたファイルはすべてダウンロードできますか?

いいえ。このガイドは、生成ファイルのdownloadable: trueと、アップロードファイルのdownloadable: falseを区別しています。ワークスペースへのアクセス権と、そのファイルのダウンロード可否は別です。利用者から任意のファイルIDをそのまま受け付けないでください。

すべての記事