Créer un outil pour le copilote StormPress, pas à pas
Le copilote StormPress sait lire votre site, publier un article ou ranger un média. Pour le reste, c'est vous qui lui donnez des outils. Exemple complet : lui apprendre à planifier un article à une date donnée, sans lui ouvrir plus de droits qu'il n'en faut.
Un outil, pour le copilote, c'est quatre choses : un nom, une description qui dit au modèle quand s'en servir, le schéma de ses paramètres et une fonction PHP qui fait le travail. StormPress se charge du reste : présenter l'outil au modèle, recevoir ses appels, vérifier les droits, renvoyer le résultat.
Nous allons écrire un outil réel : planifier la publication d'un article. C'est une demande fréquente (« programme le guide pour lundi 9 h ») et c'est une action qui modifie le site. Elle mérite donc d'être écrite avec soin.
Où déclarer l'outil
Les outils se déclarent sur l'action stormpress_ai_init, déclenchée pendant l'initialisation de WordPress. Placez le code dans une petite extension à vous, ou dans le fichier functions.php de votre thème enfant — jamais dans les fichiers de StormPress, que la prochaine mise à jour écraserait.
add_action( 'stormpress_ai_init', function () {
if ( ! function_exists( 'stormpress_register_ai_tool' ) ) {
return; // StormPress désactivé : on ne fait rien
}
stormpress_register_ai_tool( [
'name' => 'monsite_schedule_post',
'description' => 'Planifie la publication d\'un article existant à une date et une heure données, dans le fuseau horaire du site.',
'parameters' => [
'type' => 'object',
'properties' => [
'post_id' => [ 'type' => 'integer', 'description' => 'ID WordPress de l\'article' ],
'date' => [ 'type' => 'string', 'description' => 'Date locale au format AAAA-MM-JJ HH:MM' ],
],
'required' => [ 'post_id', 'date' ],
],
'capability' => 'publish_posts',
'progress_message' => 'Planification de l\'article…',
'handler' => 'monsite_schedule_post',
] );
} );
Passons en revue chaque clé.
name et description
Le nom doit être unique sur le site : préfixez-le, comme vous le feriez pour une fonction PHP. La description est plus importante qu'elle n'en a l'air : c'est le principal texte que le modèle lit pour décider s'il doit utiliser l'outil, avec le nom de l'outil et la description de chaque paramètre. Dites ce que fait l'outil, sur quoi il agit, et les conventions qu'il attend (ici, le fuseau horaire).
parameters
Les paramètres se décrivent en JSON Schema, le format que comprennent les modèles d'Anthropic et d'OpenAI. Chaque propriété a son type et sa description. Un outil sans paramètre peut déclarer 'properties' => [] : PHP l'encoderait en tableau JSON vide, que les fournisseurs refusent, mais StormPress le convertit en objet avant l'envoi au modèle.
capability
C'est la capacité WordPress exigée pour utiliser l'outil. StormPress la vérifie deux fois : l'outil n'est même pas présenté au modèle si l'utilisateur connecté ne l'a pas, et elle est contrôlée à nouveau juste avant l'exécution. Sans cette clé, StormPress applique edit_posts.
progress_message et handler
Le message de progression est émis pendant l'exécution de l'outil, dans la réponse en streaming de l'API et en ligne de commande. Le handler est n'importe quel callable PHP : un nom de fonction, une méthode statique, une fermeture. Il reçoit un seul argument, le tableau des paramètres envoyés par le modèle.
La fonction qui fait le travail
function monsite_schedule_post( array $input ) {
$post_id = (int) ( $input['post_id'] ?? 0 );
$post = $post_id ? get_post( $post_id ) : null;
if ( ! $post ) {
return new WP_Error( 'not_found', 'Article introuvable.' );
}
// La capacité déclarée est globale. Le droit sur CET article se vérifie ici.
if ( ! current_user_can( 'edit_post', $post_id ) ) {
return new WP_Error( 'forbidden', 'Vous ne pouvez pas modifier cet article.' );
}
$tz = wp_timezone();
$date = DateTimeImmutable::createFromFormat( 'Y-m-d H:i', (string) ( $input['date'] ?? '' ), $tz );
if ( ! $date || $date <= new DateTimeImmutable( 'now', $tz ) ) {
return new WP_Error( 'invalid_date', 'La date doit suivre le format AAAA-MM-JJ HH:MM et se situer dans le futur.' );
}
$local = $date->format( 'Y-m-d H:i:s' );
$result = wp_update_post( [
'ID' => $post_id,
'post_status' => 'future',
'post_date' => $local,
'post_date_gmt' => get_gmt_from_date( $local ),
'edit_date' => true,
], true );
if ( is_wp_error( $result ) ) {
return $result;
}
return [
'post_id' => $post_id,
'title' => get_the_title( $post_id ),
'scheduled_for' => $date->format( DATE_ATOM ),
];
}
Trois règles que cet exemple applique
1. La capacité déclarée ne suffit pas
publish_posts dit « cette personne peut publier ». Elle ne dit pas « cette personne peut publier cet article ». Un auteur a publish_posts, mais il ne peut pas modifier les articles des autres. Le contrôle par objet, current_user_can( 'edit_post', $post_id ), appartient à votre fonction. C'est exactement l'erreur que nous avons corrigée dans StormPress cet été, et que nous racontons dans notre retour sur les correctifs de juillet.
2. Ne jamais faire confiance aux paramètres
Le modèle respecte généralement le schéma, mais « généralement » n'est pas une garantie. Traitez $input comme une saisie utilisateur : convertissez les types, vérifiez la présence, validez les formats. Ici, une date mal formée ou passée est refusée avant tout appel à la base.
3. Des erreurs que le modèle peut comprendre
Ce que renvoie votre fonction est transmis au modèle :
- un tableau est encodé en JSON ;
- une chaîne est transmise telle quelle ;
- un
WP_Errorest transmis par son message, que l'API d'Anthropic reçoit en outre marqué comme erreur.
Un message d'erreur clair permet au modèle de se corriger seul : s'il reçoit « la date doit suivre le format AAAA-MM-JJ HH:MM », il reformule et réessaie, dans la limite de dix tours par demande. Une exception non interceptée est aussi rattrapée et son message transmis au modèle : n'y mettez donc jamais d'information sensible, comme un chemin de fichier ou une requête SQL.
Tester l'outil
Une fois le code en place, ouvrez le copilote et demandez par exemple : « planifie l'article 42 pour le 3 novembre à 9 h ». Le copilote affiche son indicateur de réflexion, puis sa réponse. Vérifiez ensuite dans la liste des articles que le statut est bien « Planifié ».
Testez aussi les cas qui doivent échouer : avec un compte d'auteur sur l'article d'un autre auteur, avec une date passée. Un outil n'est fiable que si vous l'avez vu refuser.
Le copilote se teste aussi en ligne de commande, ce qui est pratique pendant le développement :
wp stormpress ai:chat "Planifie l'article 42 pour le 3 novembre à 9 h"
En ligne de commande, le message de progression, ici « Planification de l'article… », s'affiche pendant l'exécution de l'outil.
Pour aller plus loin
Les outils sont la manière d'étendre ce que le copilote fait. Pour étendre ce que vous faites au clavier, la palette ⌘K accepte aussi vos propres commandes : c'est l'objet de notre second tutoriel.