Blog

Agent Skills dans l’API Claude : connecter un skill prêt à l’emploi

Markdown

Comment connecter un skill pptx prêt à l’emploi à l’API Claude : container.skills, l’outil d’exécution de code, la vérification de la réponse et le téléchargement de la présentation via l’API Files.

Lire l’article complet

À retenir

Les Agent Skills prêts à l’emploi pour les documents peuvent être utilisés via l’API Claude directe : indiquez le skill dans container.skills et activez code_execution. Pour votre première requête, utilisez l’association compatible modèle et outil de l’exemple officiel. Téléchargez séparément le fichier généré à l’aide de son file_id ; l’exemple utilise la même clé API pour la requête et le téléchargement.

Vous trouverez d’autres instructions dans la documentation Ailoot.

Ce dont vous aurez besoin

Ce guide explique comment appeler directement l’API depuis un programme, plutôt que d’installer un skill dans Claude Code. Vous aurez besoin d’une clé API Claude, d’un accès au modèle choisi et de Python 3. Le code ci-dessous utilise la bibliothèque standard de Python et HTTP ; il ne dépend donc pas de la version du SDK que vous avez installée. La clé est transmise via la variable d’environnement ANTHROPIC_API_KEY ; ne l’inscrivez ni dans le code du programme ni dans une conversation.

L’API propose des skills Anthropic prêts à l’emploi : pptx pour les présentations, xlsx pour les feuilles de calcul, docx pour les documents Word et pdf pour les PDF. Ils sont associés à l’aide de type: "anthropic". Ce guide utilise un skill prêt à l’emploi, sans en téléverser un personnalisé.

Le guide de démarrage rapide officiel, consulté lors de la préparation de ce contenu, utilise l’association claude-opus-5-5 et code_execution_20260521. C’est celle utilisée ci-dessous. Cet exemple a été vérifié par rapport à la documentation ; il ne s’agit pas du compte rendu d’un appel API payant.

Quels en-têtes utiliser pour Agent Skills dans l’API Claude ?

L’exemple suit le contrat HTTP actuel du guide de démarrage rapide : les en-têtes sont x-api-key, anthropic-version: 2023-06-01 et content-type: application/json ; il n’envoie pas anthropic-beta. Ne combinez pas un modèle, une méthode d’appel et un format de réponse provenant de différentes générations de la documentation.

La documentation de l’API Files indique également que l’API n’est plus en bêta. L’ancien en-tête files-api-2025-04-14 conserve les anciens formats de réponse ; sa suppression active les formats actuels. C’est pourquoi un ancien exemple utilisant client.beta.files ne peut pas être combiné tel quel avec un nouvel exemple.

Nous ne migrons pas ici un service existant depuis l’ancien contrat bêta. Si vous disposez déjà d’un tel service, vérifiez d’abord ses en-têtes et ses formats de réponse. Le mot beta dans le nom d’une méthode du SDK ne suffit pas à lui seul : la documentation de l’API Files explique séparément le comportement des différentes versions du SDK.

Créer une présentation et télécharger le fichier

L’exemple envoie une tâche visant à créer cinq diapositives sur les énergies renouvelables, enregistre la réponse complète dans skill-response.json, puis extrait les identifiants des fichiers générés. Il ne reprend pas de chemin d’enregistrement depuis la réponse du modèle : les fichiers sont enregistrés dans un nouveau dossier local nommé skill-output. Si ce dossier existe déjà, le programme s’arrête afin de ne pas écraser le résultat précédent. Le dossier est créé avant l’appel API et reste donc présent même après un échec. Inspectez et conservez cette tentative avant de recommencer ; ne supprimez pas le dossier simplement pour faire passer une nouvelle exécution.

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": "Crée une présentation sur les énergies renouvelables avec 5 diapositives dans ma langue préférée.",
    }],
    "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)

Les noms result-1.pptx, etc., ont été choisis pour cette tâche de présentation. Une extension de fichier ne prouve pas ce que contient le fichier : ouvrez le document téléchargé et vérifiez son format, le nombre de diapositives et le texte. Ne considérez pas un statut HTTP 200 à lui seul, ni la présence d’un file_id, comme la preuve d’un résultat réussi.

Le programme affiche stop_reason, la raison pour laquelle la réponse s’est arrêtée, et enregistre la réponse complète pour inspection. Il ne poursuit pas la conversation et ne répète pas automatiquement la requête payante. Le téléchargement d’un fichier ne signifie pas que la tâche est entièrement terminée : examinez la réponse enregistrée et le document lui-même avant de décider si une autre requête est nécessaire.

Après un échec, consultez skill-error.json s’il existe. Il conserve le statut HTTP, l’identifiant de requête lorsqu’il est fourni et un message fournisseur borné dont la clé API configurée a été retirée. Vérifiez que le message ne contient pas d’autres informations privées avant de le partager. Distinguez les erreurs d’authentification, de droits ou de modèle, de facturation et de format à l’aide de la référence officielle ; tout échec ne nécessite pas une nouvelle génération payante.

Si skill-response.json contient déjà les file_id générés et que seul un téléchargement a échoué, conservez cette réponse et diagnostiquez les métadonnées ou le téléchargement avec le même accès autorisé au workspace. Ne relancez pas la génération pour télécharger un fichier existant. Si l’issue de la requête est inconnue, conservez son identifiant et résolvez son statut avant toute nouvelle génération. Seulement si une nouvelle génération est justifiée, choisissez un dossier inutilisé, par exemple out = Path("skill-output-attempt-2"), en conservant exist_ok=False. Un nouveau dossier empêche l’écrasement ; il n’empêche pas une double facturation API.

Quelles autorisations sont nécessaires pour les fichiers ?

L’API Files associe les fichiers à un espace de travail plutôt qu’à un utilisateur individuel de votre application ou à une conversation. Selon la documentation, toute clé API ayant accès à cet espace de travail peut accéder aux fichiers qui y sont téléversés. L’exemple utilise la même clé pour le téléchargement et la génération. N’acceptez jamais un file_id fourni par des utilisateurs finaux ou d’autres sources non fiables. Conservez ces identifiants sur le serveur et gérez dans votre propre application l’association entre les utilisateurs et leurs fichiers.

Vérifiez séparément les métadonnées du fichier : elles doivent contenir downloadable: true. La documentation autorise le téléchargement des fichiers générés par des skills ou par l’outil d’exécution de code ; les fichiers que vous téléversez ont downloadable: false, et une tentative de téléchargement renvoie le code 400. L’accès à l’espace de travail et l’autorisation de télécharger un fichier donné sont deux conditions distinctes.

Pour une application multi-utilisateur, la documentation de l’API Files recommande un espace de travail distinct pour chaque client afin d’isoler les fichiers. Cette recommandation concerne précisément les fichiers ; ne l’étendez pas à toutes les entités et autorisations sans consulter l’API concernée.

Skills prêts à l’emploi et skills personnalisés

Les skills prêts à l’emploi ont les identifiants pptx, xlsx, docx et pdf. Dans la référence de la liste des skills, la source custom est décrite comme privée à l’espace de travail, tandis que anthropic est partagée et en lecture seule.

Dans le premier exemple, version: "latest" désigne la dernière version publiée du skill. Pour une intégration reproductible, gardez à l’esprit que ce choix peut changer ; pour passer à une version fixe, vous devez choisir une version existante du skill utilisé, plutôt que d’inventer un identifiant à partir d’un exemple.

Limites et prochaine étape

L’exemple couvre uniquement la création d’une présentation avec un skill prêt à l’emploi et le téléchargement du fichier. Le téléversement de skills personnalisés, leur suppression et l’accès à des services tiers n’ont pas été testés ici. Ces tâches nécessitent une vérification distincte de l’API concernée.

Si votre tâche porte sur des fichiers dans l’interface Claude, consultez le guide consacré à l’envoi d’un fichier dans Claude. Pour les appels directs, consultez le calcul de l’utilisation des jetons API. Pour travailler avec un agent dans un terminal, lisez comment choisir des skills Claude Code : ils sont associés d’une autre manière.

Pour une première vérification, un seul skill pptx prêt à l’emploi suffit : inspectez la réponse, téléchargez un fichier dont le téléchargement est autorisé et ouvrez-le. Ajoutez des skills personnalisés et une séparation des accès entre clients après cette vérification.

Quand vous serez prêt, découvrez Ailoot.

Plus de détails

Questions fréquentes : Agent Skills dans l’API Claude : connecter un skill prêt à l’emploi

Pourquoi un file_id ne suffit-il pas à déclarer la présentation terminée ?

Il prouve qu’un fichier a été retourné, pas qu’il constitue une présentation utilisable. Vérifiez downloadable: true dans ses métadonnées, téléchargez-le avec l’accès au workspace, puis ouvrez-le pour contrôler format, nombre de diapositives et texte.

Puis-je télécharger tout fichier importé dans Claude Files API ?

Non. Le guide distingue les fichiers générés marqués downloadable: true des fichiers importés marqués downloadable: false. L’accès au workspace et le droit de télécharger un fichier sont distincts ; n’acceptez pas d’identifiants arbitraires fournis par les utilisateurs.

Tous les articles