Construire une bonne compétence d’agent Claude : leçons tirées d’un projet de coach sportif
Créer une compétence d’agent Claude fiable : descriptions précises, évaluations avant la rédaction, SKILL.md en tant que routeur, scripts au lieu de texte et une séparation des identifiants dans CI.
Sources: I Missed My Running Goal Three Years Running. So I Built an Agent Skill.
Imaginez un coureur qui, pendant trois années consécutives, s’est fixé comme objectif de parcourir 1 000 km, mais qui l’a manqué à chaque fois, terminant ces trois années avec un total d’environ 2 500 km. L’effort n’était pas le problème : les blessures, les reprises et des semaines chargées étaient inévitables. Ce qui manquait, c’était un plan, car la motivation fonctionne bien jusqu’au jour où elle cesse de le faire.
Une solution consiste à cesser de compter sur la force de volonté et à mettre en place un système. C’est ainsi que running-coach a vu le jour : une compétence d’agent Claude qui planifie les courses individuelles ainsi que toute une semaine en se basant sur l’historique personnel de la personne, transforme chaque semaine en un résumé par e-mail de style éditorial, et adapte même la longueur d’une playlist en fonction de la distance à parcourir.
La course n’est ici qu’un contexte de fond. Le sujet plus utile concerne les éléments nécessaires pour développer une bonne compétence, c’est-à-dire une compétence qui s’active de manière fiable, utilise le contexte avec modération, se comporte de la même façon sur toutes les plateformes et ne se dégrade pas avec le temps. Ci-dessous, chaque pratique d’écriture publiée par Anthropic est associée à la manière dont ce projet l’applique.
Qu’est-ce qu’une compétence d’agent
Une compétence correspond à un dossier. Dans son répertoire racine se trouve un fichier SKILL.md qui commence par du frontmatter YAML contenant un name et un description, puis continue avec des instructions en Markdown. Des documents de référence, des scripts, des modèles et d’autres ressources peuvent être placés à côté de ce fichier.
Le point intéressant réside dans la manière dont une compétence est chargée, grâce à un modèle à trois niveaux appelé divulgation progressive :
- Niveau 1, métadonnées. Chargées systématiquement au démarrage, soit environ 100 tokens par compétence. Elles ne comprennent que le nom et la description.
- Niveau 2, instructions. Chargées uniquement lorsque la compétence est déclenchée, et leur longueur reste inférieure à environ 5 000 tokens. Il s’agit du contenu de
SKILL.md. - Niveau 3, ressources. Téléchargées sur demande via bash, et disponibles tant qu’elles ne sont pas modifiées. Les fichiers de référence, les scripts et les assets se trouvent ici.
Anthropic compare une compétence à un guide d’intégration destiné à un nouveau collègue. Le principe fondamental est que la fenêtre de contexte est partagée, constituant ainsi un bien public. Tout ce que consomme une compétence n’est pas disponible pour le reste des informations que l’agent tente de garder en mémoire.
1. La description agit comme API
Au niveau 1, tout ce que le modèle voit d’une compétence, c’est sa description. Celle-ci fonctionne moins comme une documentation et plus comme un tableau de routage, puisque le modèle compare une demande entrante avec elle pour décider s’il faut activer la compétence. Elle doit indiquer à la fois ce que fait la compétence et quand l’utiliser, être rédigée à la troisième personne, et rester inférieure à 1 024 caractères, avec environ 200 comme limite pratique si l’on souhaite qu’elle soit affichée sans troncature dans le sélecteur de Claude.ai. Voici la description utilisée par running-coach :
name: running-coach
description: >-
Plans a run or a whole week as Google Calendar blocks, builds a weekly
running digest email, and makes playlists — from Strava, Spotify, AccuWeather
and Calendar. Use for pacing or planning a week.
Regardez comment c’est construit. Il commence par décrire ce que cette compétence permet de faire (planifier une course, créer un résumé, créer des listes de lecture), indique ensuite quand l’utiliser (pour gérer le rythme ou planifier une semaine), et précise les sources de données afin qu’une question de type Strava renvoie à cette compétence. Le nom est en minuscules et identique au nom du répertoire : c’est une règle stricte, pas une préférence stylistique, et c’est ce que le processus de construction du projet impose, comme on le verra plus loin.
On ne peut pas savoir à l’avance si une description fonctionnera. Elle doit être testée, c’est pourquoi la première chose dans le répertoire est un ensemble d’évaluations et non de la documentation.
2. Écrire les évaluations avant de peaufiner le texte
Le conseil d’Anthropic est de créer d’abord des mécanismes d’évaluation, afin de résoudre des problèmes réels plutôt que des problèmes imaginaires. Le projet contient quatre fichiers de scénarios dans un dossier evals/, un par capacité. Il ne s’agit pas de tests unitaires, mais de contrats de comportement rédigés en langage simple. Voici celui utilisé pour planifier une exécution :
{
"skills": ["running-coach"],
"query": "I'm heading out for 8k this evening, how long is that going to take me?",
"expected_behavior": [
"Triggers the running-coach skill without being named, and routes to the planning capability",
"Reads references/run-planning.md and does not read the digest or playlist references",
"Runs scripts/cache.py status before projecting…",
"Executes scripts/planning/plan_run.py 8 rather than reading its source or reimplementing Riegel inline",
"Reports the projected finish time, the pace, and the recommended effort band…",
"Never asks for or uses a Strava access token"
]
}
Chaque affirmation encode une meilleure pratique. Déclencher des tests sans qu’ils soient nommés évalue la qualité de la description des tests. Ne lire que les tests de référence pertinents correspond à une divulgation progressive. Exécuter le script sans le réimplémenter teste la préférence pour les scripts par rapport au texte narratif. Ne jamais toucher à un token teste les limites des droits d’accès. Ensemble, ils transforment toute la philosophie de conception en comportements observables, et le reste du processus de construction vise à s’assurer qu’ils fonctionnent correctement.
3. Conserver SKILL.md en tant que routeur
La manière la plus courante de consommer entièrement le budget de contexte d’une compétence est de tout mettre dans SKILL.md. Une approche plus efficace consiste à traiter ce fichier comme un sommaire qui renvoie vers des documents plus détaillés lorsque c’est nécessaire. Gardez-le court et limitez les références à un niveau de profondeur, car les agents ont tendance à ne lire que partiellement les fichiers fortement imbriqués, ce qui ne leur donne qu’une vision incomplète.
Chacune des quatre capacités mentionnées ici nécessiterait une page entière pour être expliquée, donc SKILL.md n’en décrit aucune. Il redirige vers :
| Capability | Reference | Entry point |
| -------------- | -------------------------------- | --------------------------------- |
| Plan a run | references/run-planning.md | scripts/planning/plan_run.py |
| Plan the week | references/week-planning.md | scripts/planning/plan_week.py |
| Weekly digest | references/weekly-digest.md | scripts/digest/run_pipeline.py |
| Run playlist | references/run-playlist.md | scripts/playlist/build_playlist.py|
Work out which one is wanted, then read only that capability's reference.
Demandez une liste de lecture et l’agent ouvre run-playlist.md et rien d’autre. La spécification du flux de travail du résumé, ses pièges et ses notes de modèle n’entrent jamais en compte, puisque le contenu emballé non utilisé ne coûte rien. Le niveau 2 reste léger et le niveau 3 ne contribue qu’un seul fichier. La ligne « lit X, ne lit pas Y » dans l’évaluation vérifie précisément cela.
4. Les scripts plutôt que le texte : exécutez-les, ne les lisez pas
Ce pourrait être l’idée la plus précieuse dans la conception des compétences. Lorsque l’agent exécute un script via bash, seuls les résultats s’inscrivent dans le contexte, jamais le code source. Les scripts prédéfinis sont également plus fiables que le code généré sur le moment, et ils donnent toujours le même résultat.
Ainsi, la logique de running-coach se trouve en Python, et les instructions indiquent explicitement à l’agent de laisser le code source de côté :
**Run the scripts, don't read them.** They print everything needed; every one
takes --help and most take --json. Reading their source wastes context.
La projection du temps d’arrivée selon Riegel, le calcul de la bande d’effort et l’analyse synthétique s’exécutent tous en Python déterministe et renvoient une réponse concise. L’agent orchestre les opérations sans les recalculer. Comparez un agent qui évalue votre vitesse, qui est lente, variable et gourmande en ressources, à un autre qui utilise un calculateur de vitesse et affiche un chiffre, ce qui est rapide et identique à chaque fois.
5. Chaque script est une véritable CLI avec des conventions communes
Pour que le principe « exécutez-le, ne le lisez pas » fonctionne, l’agent doit faire fonctionner un script qu’il lui est interdit d’ouvrir. Cela dépend d’une interface uniforme et auto-descriptive, de sorte que chaque script respecte les mêmes conventions :
- un argument positionnel pour tout élément toujours requis (la distance, pour la planification et les listes de lecture) ;
--data DIRpartout, afin que n’importe quel script puisse faire référence à un cache différent, ce qui permet les tests hors ligne et des environnements isolés par session ;--jsonsur presque tous, de sorte que la sortie d’un script puisse alimenter le suivant ;--helpsur chacun d’eux, solution de secours de l’agent lorsqu’il hésite sur un paramètre. Il lit le texte d’aide, ce qui est peu coûteux, et non la source, qui est coûteuse et interdite d’accès.
Voici l’aperçu, capacité par capacité.
Planification d’une exécution utilise plan_run.py :
python3 scripts/planning/plan_run.py 8 # 8 km, defaults
python3 scripts/planning/plan_run.py 8 --band steady # force an effort band
python3 scripts/planning/plan_run.py 8 --start-in 3 # start 3h from now (weather-aware)
python3 scripts/planning/plan_run.py 8 --json # machine-readable plan
L’unique argument requis est distance_km. Les paramètres optionnels sont --exp, qui ajuste l’exposant de fatigue de Riegel, et --band, qui impose une plage d’effort spécifique. En l’absence de spécification, la plage est déterminée en fonction de la charge d’entraînement antérieure du coureur. La partie heuristique offre une certaine liberté, tandis que la projection exacte reste déterministe.
La playlist est gérée par build_playlist.py, qui imite délibérément la structure du planificateur :
python3 scripts/playlist/build_playlist.py 8 # size to the 8 km finish
python3 scripts/playlist/build_playlist.py 8 --genre "lo-fi"
python3 scripts/playlist/build_playlist.py 8 --minutes 55 # size by time instead
python3 scripts/playlist/build_playlist.py 8 --band hard
Puisque les deux prennent la même distance positionnelle, ces deux capacités se combinent : le temps d’achèvement projeté par plan_run correspond à la durée pour laquelle build_playlist définit la taille de la playlist. Le script génère une requête et non une playlist, que l’agent transmet ensuite au connecteur Spotify. La distinction entre les tâches liées aux identifiants apparaît à nouveau ici, avec le script effectuant des calculs et l’agent s’occupant des entrées/sorties.
Le résumé hebdomadaire est un petit pipeline et non un monolithe. run_pipeline.py valide le cache puis exécute deux étapes :
python3 scripts/digest/run_pipeline.py # validate → analyse → render
python3 scripts/digest/run_pipeline.py --plan-km 8 # lead the digest with a planned run
python3 scripts/digest/run_pipeline.py --seed 3 # pin the masthead title choice
python3 scripts/digest/run_pipeline.py --skip-validate # skip the pre-flight cache check
En dessous, analyze.py regroupe la semaine en mémoire dans un fichier digest_data.json, tandis que build_email.py transforme ce JSON en un e-mail HTML autonome. En séparant l’analyse de la mise en forme, il est possible de redéfinir le style de l’e-mail sans modifier l’analyse, et le paramètre --seed permet de reproduire le même choix aléatoire, à savoir le titre de la page de titre, ce qui est pratique pour prendre deux captures d’écran de la même semaine.
Planning the week est l’interface en ligne de commande la plus intéressante, car elle représente un processus de planification, de validation et d’exécution exprimé sous forme de sous-commandes que l’agent exécute dans l’ordre :
python3 scripts/planning/plan_week.py goal # show the weekly goal in force
python3 scripts/planning/plan_week.py propose # propose the split of runs (as JSON)
python3 scripts/planning/plan_week.py draft --split - # lay the split across the days
python3 scripts/planning/plan_week.py brief --draft - # hand the agent a per-run contract
python3 scripts/planning/plan_week.py events --draft - --content - # emit calendar blocks
Chaque étape affiche du JSON que l’étape suivante consomme via stdin. Lorsque cela est applicable, les étapes acceptent également des paramètres de remplacement courants (--km, --runs) ainsi que des options de planification telles que --start-date, --time, --weekend-time, --location et --tz. Les directives recommandent cette forme d’« sorties intermédiaires vérifiables » pour les tâches fragiles : l’agent n’écrit jamais de manière arbitraire une semaine entière. Il émet un plan structuré, le script le vérifie et le modifie, puis seulement ensuite il est transformé en événements calendaires.
À mi-parcours, il y a un transfert délibéré des responsabilités. Au stade du brief, le script détermine la distance, la fenêtre de vitesse et le jour, puis fournit à l’agent un contrat pour chaque course. L’agent rédige les mots qui donnent un aspect humain au texte, afin que les descriptions des courses ne soient jamais identiques. Le script s’occupe de ce qui doit être exact, tandis que le modèle s’occupe de ce qui doit avoir l’air écrit par un être humain. Une faible liberté pour les calculs mathématiques complexes et une grande liberté pour la prose constituent le principe des « degrés de liberté appropriés » dans une seule commande.
6. Résolvez les problèmes, ne les ignorez pas
Une compétence insuffisante charge l’agent de cas complexes avec des instructions vagues du type « si les données semblent obsolètes, trouvez une solution ». Selon Anthropic, il s’agit de résoudre les problèmes plutôt que de les ignorer : gérer les conditions d’erreur dans le script sans les déléguer. running-coach met cela en œuvre grâce à un modèle de fraîcheur. Pour chaque donnée mémorisée, il enregistre son âge, et status indique précisément quelle appel MCP permettrait de combler chaque lacune :
python3 scripts/cache.py status # what's cached, how stale, what to call
python3 scripts/cache.py put streams --file - # ingest an MCP response from stdin
python3 scripts/cache.py show week --json # a compact summary, not the raw blob
python3 scripts/cache.py validate --for digest # shape-check before a run
cache.py comprend quatre fonctions (status, put, show, validate), chacune représentant un échelon dans la procédure de mise à jour. status énumère chaque clé avec son budget de fraîcheur : ce budget est limité pour la semaine en cours, car il change à chaque exécution, et est hebdomadaire pour les semaines terminées ainsi que pour les données des athlètes ; en cas de données obsolètes ou manquantes, il indique l’outil MCP spécifique à utiliser. L’agent effectue cette appel et transmet directement la réponse à put. Ensuite, validate --for <capability> vérifie que le résultat est utilisable avant que le script suivant ne s’exécute, de sorte qu’une récupération défaillante est stoppée immédiatement avec une erreur, plutôt que de se manifester plus tard sous forme de résumé incorrect.
Mesures de sécurité supplémentaires : put ne prend en charge que des clés issues d’une liste autorisée prédéfinie, show affiche un résumé plutôt que du JSON brut, de sorte que l’examen du cache ne déverse pas un mégaoctet de données GPS dans le contexte, et l’agent est instruit de ne pas modifier manuellement ces fichiers JSON. En conséquence, l’agent n’a pas besoin de décider s’il doit récupérer de nouvelles données ou quelles données récupérer, car c’est ce que le script indique. Un jugement subjectif (“est-ce suffisamment à jour ?”) devient une instruction déterministe, ce qui correspond précisément à ce que cache.py status fait en étant exécuté avant la projection.
7. Les limites des identifiants
C’est le choix architectural central ainsi qu’une pratique de sécurité : auditer les appels réseau inattendus et les schémas d’accès aux fichiers. Cette solution rend cet audit trivial par construction, puisqu’aucun élément à l’intérieur ne contient d’identifiants ni n’ouvre de socket.
agent --(Strava / Spotify / AccuWeather MCP)--> cache.py put --> the cache
the cache --> plan_run.py / run_pipeline.py / build_playlist.py --> output
Seul l’agent communique avec les connecteurs MCP, et seul cache.py écrit dans le cache. Tous les autres scripts se contentent de transformer les données en cache hors ligne, sans client HTTP, sans clés API et sans tokens d’accès nulle part dans le package.
# cache.py — the only gateway to the running-coach data cache.
# The agent is the only thing that talks to MCP; this script is the only thing
# that reads or writes the cache. Nothing here needs an access token.
Voici trois avantages. En termes de sécurité, il n’y a rien à fuiter, et la surface réseau à auditer est vide. En ce qui concerne la portabilité, cette compétence ne se soucie pas du fait que les connecteurs soient locaux, distants ou absents, car elle ne consomme qu’un cache. Enfin, pour la testabilité, un cache rempli permet à chaque script downstream de fonctionner entièrement hors ligne, ce qui rend possible un test de base en CI.
8. Une exigence de temps de exécution : Python 3.10
Les compétences ne s’ synchronisent pas entre les différentes surfaces, et chacune d’elles possède ses propres limites de temps de exécution. L’API Claude ne propose ni accès réseau ni installation de paquets de temps de exécution, tandis que Claude Code dispose de tout cela. Pour faire fonctionner une compétence partout, il faut la concevoir pour l’environnement le plus restreint.
C’est pourquoi running-coach ne nécessite rien d’externe. Il n’y a ni requirements.txt, ni virtualenv, ni étape d’installation. Ses instructions indiquent que Python 3.10 ou une version plus récente est tout ce qui est requis, et le simple python3 fonctionne tel quel, même dans un environnement sandbox hébergé où l’installation de paquets est impossible.
Tout ce dont a besoin cette compétence se trouve à l’intérieur du répertoire skills/running-coach/. En déposant ce dossier dans ~/.claude/skills/, il est disponible pour Claude Code, et un téléchargement en format zip fonctionne également pour Cowork ou claude.ai, avec un comportement identique. Les chemins utilisent systématiquement des barres obliques, évitant ainsi un problème courant entre systèmes d’exploitation. Les fichiers de cache et de sortie sont stockés dans un emplacement en dehors du dossier de la compétence, ce qui maintient la copie installée en lecture seule, et ils ne sont jamais soumis à git, car le cache contient des polygones GPS réels qui se décodent en itinéraires partant de la porte d’entrée de quelqu’un.
9. Vérifications de l’emballage et CI qui étayent les affirmations
L’emballage est l’étape que les gens sautent. Une compétence installée incorrectement semble défectueuse de manières non évidentes : un archive contenant des fichiers non verrouillés à son niveau racine est silencieusement rejeté lors du téléchargement. C’est pourquoi le projet inclut package.py, un validateur sans dépendances qui répète les vérifications présentes dans le propre répertoire de compétences d’Anthropic. Il vérifie les clés de frontmatter autorisées, un name en format kebab-case correspondant au répertoire, une longueur de description conforme aux spécifications (avec un avertissement concernant la limite d’environ 200 caractères de claude.ai), ainsi qu’un archive contenant exactement un répertoire de niveau supérieur avec SKILL.md à son niveau racine.
GitHub Actions bloque ensuite chaque publication en fonction de ces vérifications. Lorsqu’une étiquette v* est poussée, le flux de travail :
- compile chaque script en bytecode (
python -m compileall) afin de détecter les erreurs de syntaxe ; - vérifie le frontmatter conformément aux spécifications ;
- exécute chaque point d’entrée avec
--help, ce qui montre au minimum que chaque script importe et analyse des éléments avec une version propre de Python 3.10 ; - vérifie que
running-coach/SKILL.mdse trouve au niveau racine de l’archive ; - et ce n’est qu’après tout cela que le zip est publié.
# 3.10 is the version the skill claims to support, so building on it is
# also how that claim stays honest.
- uses: actions/setup-python@v5
with:
python-version: "3.10"
Leçons généralisables
Quelques points se détachent particulièrement de cette approche :
Une description d’environ 100 tokens suffit pour accomplir 90 % du travail. Comme c’est la seule partie toujours chargée, si son déclenchement échoue, tout le reste devient irrelevant. Accordez-lui une attention particulière et ajoutez des tests qui vérifient le déclenchement sans mentionner le nom de la compétence.
Transférez la logique des prompts vers des scripts déterministes. Le texte lu par l’agent varie et coûte des tokens, tandis qu’un script qu’il exécute est peu coûteux et donne toujours la même réponse. Le principe à retenir est d’exécuter plutôt que de lire.
Faites confiance à des limites que l’on peut exprimer en une phrase. « Seul l’agent communique avec MCP, un seul script écrit le cache, tout le reste est hors ligne » vaut bien des piles de code de défense, car des catégories entières de bugs et de fuites deviennent structurellement impossibles.
Faites vérifier vos affirmations par le build. Une compétence qui promet Python 3.10 et zéro dépendances doit être construite exactement de cette manière dans l’environnement CI à chaque publication. Sans cela, cette promesse n’est qu’un espoir.
L’objectif de course lui-même reste ouvert, mais il existe désormais un coordinateur qui planifie la semaine, donne une évaluation honnête le lundi matin et recalcule l’objectif en fonction de la distance parcourue chaque semaine. La course est devenue un problème d’ingénierie résolvable dès lors qu’on l’a traitée comme tel.
Toute personne installant une telle compétence doit avoir l’exécution de code activée ainsi que son propre connecteur Strava, car la compétence ne contient aucune identifiante. Il en va de même pour les sources à consulter : l’aperçu des Agent Skills d’Anthropic et sa page sur les meilleures pratiques pour créer des compétences, toutes deux disponibles dans les documents de la plateforme Claude.
Lectures complémentaires
- Gemini Spark expliqué : tâches, compétences, plannings, sécurité et tarifs — Ce que fait l’agent Gemini Spark toujours actif de Google dans Gmail, Calendar et Docs, comment fonctionnent ses couches de sécurité, son coût, les endroits où il est bloqué et qui devrait l’adopter.
- RAG avancé en production : architecture, outils et compromis — Une présentation du RAG en environnement de production : ingestion, analyse, segmentation, génération d’embeddings, stockage vectoriel, récupération hybride, réclassement, évaluation et sécurité, ainsi que les compromis associés à chacun d’eux.