블로그

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 스킬 하나면 충분합니다. 응답을 살펴보고, 다운로드가 허용된 파일을 내려받아 열어 보세요. 이 점검을 마친 뒤 사용자 지정 스킬과 고객 간 액세스 분리를 추가하세요.

준비되면 Ailoot을 알아보세요.

자세히 보기

Claude API의 Agent Skills: Claude Code 없이 미리 만들어진 스킬 사용하기: 자주 묻는 질문

file_id를 받았는데도 프레젠테이션 완료라고 할 수 없는 이유는 무엇인가요?

ID는 파일이 반환됐음을 보여줄 뿐 사용 가능한 프레젠테이션임을 입증하지 않습니다. 메타데이터의 downloadable: true를 확인하고 워크스페이스 접근 권한으로 내려받아 파일 형식, 슬라이드 수, 텍스트를 열어 확인하세요.

Claude Files API에 업로드한 파일은 모두 내려받을 수 있나요?

아닙니다. 이 안내서는 downloadable: true인 생성 파일과 downloadable: false인 업로드 파일을 구분합니다. 워크스페이스 접근 권한과 특정 파일의 다운로드 권한은 별개이므로 최종 사용자가 보낸 임의의 파일 ID를 허용하지 마세요.

모든 글