Lección 1 — Le standard et le chargeur

Le format SKILL.md

7 min de lectura

Este contenido aún no está traducido. Se muestra la versión original.

Le format SKILL.md

Une équipe finance dépose dans le dossier partagé un skill dont le frontmatter contient description: Use when: the user sends receipts. Le parseur YAML du harness lève une exception sur le deux-points, le chargeur s'arrête, et les cinq autres skills disparaissent avec lui. Personne ne le voit avant que le support ne signale que l'assistant « ne trie plus les tickets ». Lire un SKILL.md paraît trivial ; le lire sans faire tomber les voisins demande des règles écrites.

Le mécanisme

Un skill est un dossier qui contient au minimum un fichier SKILL.md : un frontmatter YAML entre deux lignes ---, puis un corps Markdown libre. La spécification (agentskills.io/specification) recommande trois sous-dossiers facultatifs : scripts/ (code exécutable), references/ (documentation lue à la demande) et assets/ (gabarits, données). Le dépôt anthropics/skills publie des exemples et un dossier spec/ ; son README renvoie vers agentskills.io pour le standard.

flowchart TB
  D["expense-report-checker/"] --> S["SKILL.md : frontmatter + corps"]
  D --> SC["scripts/check_expenses.py"]
  D --> R["references/politique-frais.md"]
  D --> A["assets/bareme.json, assets/rapport.md"]
  S --> F["name, description (obligatoires)"]
  S --> O["license, compatibility, metadata, allowed-tools (facultatifs)"]

Les contraintes, relevées sur la spécification le 2026-10-15 :

ChampObligatoireContrainte
nameoui1 à 64 caractères, a-z, 0-9 et tirets ; pas de tiret en tête, en fin ni doublé ; égal au nom du dossier
descriptionoui1 à 1 024 caractères ; dit ce que fait le skill et quand l'utiliser
licensenonnom de licence ou fichier de licence joint
compatibilitynon1 à 500 caractères (produit visé, paquets système, accès réseau)
metadatanondictionnaire de chaînes vers chaînes (version: "1.2.0")
allowed-toolsnonliste d'outils pré-approuvés séparés par des espaces, expérimental

Pour le corps, la spécification recommande moins de 500 lignes et moins de 5 000 tokens, avec des références à un seul niveau de profondeur depuis SKILL.md.

La mise en œuvre

Le dépôt applique deux politiques différentes, comme le recommande le guide d'implémentation client : strict en CI, tolérant au chargement. Un nom qui ne correspond pas au dossier est une faute de l'auteur, à refuser en revue ; en production, on charge quand même et on journalise. Seules deux fautes écartent un skill : YAML illisible après réparation, ou description absente (sans elle, le modèle ne peut pas savoir quand l'utiliser).

files/skillkit/spec.py
def split_frontmatter(text: str) -> tuple[dict, str]:
    m = re.match(r"^---\s*\n(.*?)\n---\s*(?:\n|$)(.*)", text, re.S)
    ...
    try:
        fm = yaml.safe_load(raw) or {}
    except yaml.YAMLError:
        # « description: Use when: ... » non guillemeté : on guillemette et on réessaie.
        fixed = re.sub(r"^(\w[\w-]*):\s+(.*:.*)$", lambda x: f'{x[1]}: "{x[2]}"', raw, flags=re.M)
        fm = yaml.safe_load(fixed) or {}

La découverte parcourt les dossiers, ignore .git, node_modules, .venv, borne la profondeur, et applique une règle de collision déterministe : le premier répertoire passé gagne (projet avant utilisateur), avec un avertissement.

files/skillkit/spec.py
def discover(*roots: Path, max_depth: int = 4) -> dict[str, Skill]:
    for root in roots:
        for md in sorted(root.rglob("SKILL.md")):
            ...
            if s.name in found:
                log.warning("collision : %s (%s) masqué par %s", s.name, md.parent, found[s.name].path)
                continue

Les trois skills nouveaux suivent la même structure de description que ceux de la bibliothèque : ce que le skill produit, « Use whenever… », une ligne « Not for » qui renvoie vers le skill voisin, puis des phrases déclencheuses en anglais et en français. Cette ligne « Not for » pèsera lourd dans la leçon sur le déclenchement : hr-policy-qa parle aussi de frais et de congés.

Résultat obtenu

Sortie réelle de python run.py validate, 2026-10-15 :

OK  csv-data-profiler            v0.9.0   corps  25 lignes, 2 ressource(s), description 707 car.
OK  expense-report-checker       v1.2.0   corps  27 lignes, 4 ressource(s), description 689 car.
OK  hr-policy-qa                 v0.0.0   corps  81 lignes, 0 ressource(s), description 781 car.
OK  leave-balance-calculator     v1.0.3   corps  31 lignes, 2 ressource(s), description 696 car.
OK  meeting-notes-to-actions     v0.0.0   corps  85 lignes, 0 ressource(s), description 669 car.
OK  ticket-triage                v0.0.0   corps  91 lignes, 0 ressource(s), description 752 car.

Ce qui casse en production

SymptômeCauseParade
Tous les skills disparaissent d'un coupune exception YAML non rattrapée dans la boucle de découvertetry par skill, réparation des deux-points, journal d'erreur, on continue
Un skill « n'existe pas » alors que le fichier est làname différent du dossier ; ADK refuse de le charger (load_skill_from_dir lève une erreur)validation stricte en CI, avant la fusion
Deux comportements selon le postedeux skills de même nom, l'un dans le projet, l'autre dans ~/.agents/skills/règle de précédence fixe et avertissement de collision visible
version: 1.10 devient le nombre 1.1, le verrou ne voit plus la montée de versionYAML convertit en nombrela spécification impose des chaînes : guillemets, et le validateur le vérifie
Un dépôt cloné injecte des instructionsles skills de projet viennent d'un dépôt non fiablene charger les skills de projet que si le dossier est marqué de confiance

La règle de décision

Validez strictement en CI et chargez avec tolérance en production. N'ajoutez des champs hors spécification que dans metadata, jamais à la racine du frontmatter : ADK les accepte, d'autres clients les refusent. Gardez SKILL.md sous 300 lignes ; au-delà, déplacez le détail dans references/. Surveillez dans les journaux le nombre de skills chargés au démarrage : une baisse soudaine signale un skill rejeté.

Pratique

Un auteur livre un skill dont le frontmatter contient « version: 2.0 » à la racine, à côté de name et description. Le chargeur tolérant l'accepte. Que faites-vous ?

Au démarrage, le journal affiche « collision : ticket-triage (~/.agents/skills/ticket-triage) masqué par ./skills/ticket-triage ». Le support se plaint d'un comportement différent sur le poste d'un collègue. Première vérification ?

Exercice. Ajoutez au validateur une règle : un SKILL.md qui cite un fichier (scripts/x.py, references/y.md) absent du dossier est une erreur. Testez-la sur expense-report-checker en renommant assets/rapport.md.

Voir la solution
def missing_refs(skill: Skill) -> list[str]:
    cited = set(re.findall(r"`((?:scripts|references|assets)/[\w./-]+)`", skill.body))
    return sorted(c for c in cited if c not in skill.resources)

Appelez-la dans cmd_validate et ajoutez un test qui copie le skill dans tmp_path, renomme assets/rapport.md et vérifie que missing_refs renvoie ["assets/rapport.md"]. Le motif ne couvre que les chemins entre accents graves : c'est la convention des six skills, à écrire dans votre guide d'auteur.

  • Je sais lister les six champs du frontmatter et leurs contraintes
  • Je sais pourquoi valider strictement en CI et charger avec tolérance
  • Je sais réparer un frontmatter avec deux-points non guillemetés
  • Je sais définir une règle de précédence entre skills de projet et d'utilisateur
Siguiente