Блог

Claude API Skills: готовые навыки docx, xlsx и pptx

Markdown

Подключение готового навыка pptx к Claude API: настройка container.skills и code_execution, проверка ответа и скачивание презентации через Files API.

Читать статью полностью

Коротко о главном

Готовый Agent Skill для работы с документами можно использовать через прямой вызов Claude API: добавьте навык в container.skills и активируйте code_execution. Для первого запроса выберите совместимую модель и инструмент из официального примера. Созданный документ скачивается отдельно по file_id; в этом примере один API-ключ служит и для запроса, и для загрузки файла.

Другие инструкции собраны в документации Ailoot.

Что потребуется

Материал посвящён прямому обращению к API из программы, а не установке навыка в Claude Code. Подготовьте API-ключ Claude, доступ к выбранной модели и Python 3. Приведённый пример использует встроенные средства Python для HTTP-запросов, поэтому ему не нужна конкретная версия SDK. Ключ задаётся в переменной окружения ANTHROPIC_API_KEY; не записывайте его в исходный код или сообщения.

Anthropic предоставляет готовые навыки для API: pptx для презентаций, xlsx для электронных таблиц, docx для документов Word и pdf для файлов PDF. Для подключения используется тип "anthropic". Ниже применяется готовый навык, без загрузки собственного.

В официальном quickstart, изученном при подготовке этого материала, приведено сочетание claude-opus-5-5 и code_execution_20260521. Ниже используется именно оно. Это пример, сверенный с документацией, а не свидетельство выполненного платного API-вызова.

Нужные beta-заголовки

В HTTP-примере сохранены параметры текущего контракта из quickstart: 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-контракта. Если такой сервис у вас есть, сперва проверьте его заголовки и структуру ответов. Наличие слова beta в названии метода SDK само по себе ничего не решает: документация Files API отдельно описывает различия между версиями SDK.

Создайте презентацию и скачайте её

В примере API получает задание подготовить пять слайдов о возобновляемой энергетике. Весь ответ сохраняется в skill-response.json, после чего программа находит идентификаторы созданных файлов. Путь для сохранения не берётся из ответа модели: файлы записываются в новую локальную папку 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": "Create a presentation about renewable energy with 5 slides in my preferred language.",
    }],
    "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-статус, идентификатор запроса, если он был передан, и ограниченное сообщение провайдера с удалённым значением настроенного API-ключа. Перед передачей другим людям проверьте сообщение на иные приватные данные. Сопоставляйте ошибки аутентификации, прав или модели, платёжных лимитов и формата запроса с официальным справочником ошибок; не всякая ошибка требует новой платной генерации.

Если в skill-response.json уже есть созданные file_id, а сбой произошёл при скачивании, сохраните ответ и проверяйте метаданные и загрузку файла с тем же разрешённым доступом к workspace. Не запускайте генерацию заново ради существующего результата. При неизвестном исходе запроса сохраните его ID и выясните статус до новой генерации. Только если новая генерация действительно нужна, задайте свободный путь, например out = Path("skill-output-attempt-2"), сохранив exist_ok=False. Новая папка предотвращает локальную перезапись, но сама по себе не предотвращает повторный платёж. FileExistsError — локальная коллизия папки, а не ответ API.

Какие разрешения требуются для работы с файлами

Files API связывает файлы с workspace — рабочим пространством, — а не с пользователем вашего приложения или конкретной перепиской. Как сказано в документации, доступ к файлам этого workspace может получить любой API-ключ, которому разрешено работать с ним. Для генерации и скачивания в примере применяется один и тот же ключ. Не принимайте file_id от конечного пользователя или из другого недоверенного источника. Храните идентификаторы на сервере, а связь между пользователями и их файлами учитывайте в собственном приложении.

Проверьте и метаданные файла: в них должно быть downloadable: true. Документация допускает скачивание файлов, созданных навыками или инструментом выполнения кода. Для загруженных вами файлов значение downloadable: false, и попытка получить такой файл вернёт ошибку 400. Доступ к workspace сам по себе не гарантирует возможности скачать каждый файл.

Для приложения с несколькими клиентами документация Files API советует выделять каждому клиенту отдельный workspace, чтобы разделять доступ к файлам. Это рекомендация касается именно файлов; не распространяйте её на другие сущности и разрешения без проверки документации соответствующего API.

Готовый навык и пользовательский навык

Для готовых навыков предусмотрены идентификаторы pptx, xlsx, docx и pdf. В справке по списку навыков указано, что источник custom означает навык, закрытый для workspace, а anthropic — общий, доступный только для чтения.

В первом примере version: "latest" обозначает последнюю опубликованную версию навыка. Для интеграции, результаты которой должны воспроизводиться, учитывайте: выбранная версия со временем может измениться. Чтобы закрепить конкретную версию, укажите существующую версию используемого навыка, а не придумывайте идентификатор по образцу.

Ограничения и дальнейшие действия

Этот пример показывает только создание презентации с готовым навыком и скачивание результата. Загрузка и удаление собственных навыков, а также обращение к сторонним сервисам здесь не рассматривались. Для каждого такого сценария отдельно проверьте соответствующий API.

Если вы работаете с файлами через интерфейс Claude, ознакомьтесь с отдельным материалом о загрузке файла в Claude. Для прямых API-вызовов пригодится руководство по расчёту расхода токенов. Если же вы используете терминальный агент, прочитайте о выборе навыков Claude Code: это другой способ подключения.

Для начальной проверки возьмите один готовый навык pptx, проверьте ответ API, скачайте файл с разрешённым доступом и откройте его. Подключайте собственные навыки и настраивайте разделение доступа между клиентами после этой проверки.

Когда будете готовы, познакомьтесь с Ailoot.

Подробнее

Вопросы и ответы

Почему file_id недостаточно для завершения задачи с презентацией?

Идентификатор подтверждает возврат файла, но не пригодность презентации. Проверьте метаданные downloadable: true, скачайте файл с доступом к рабочему пространству и откройте его. Проверьте формат, число слайдов и текст.

Можно ли скачать любой файл, загруженный в Claude Files API?

Нет. Руководство различает созданные файлы с downloadable: true и загруженные с downloadable: false. Доступ к рабочему пространству и разрешение скачивать конкретный файл проверяются отдельно. Не принимайте от конечных пользователей произвольные идентификаторы файлов.

Все статьи