Chapitre 11.6 — Le sous-système IA (core_ai)
Partie 11 : Niveau expert — Chapitre 6/8 Public : dev voulant brancher un LLM (l’API Claude d’Anthropic) dans Moodle proprement, via le sous-système IA du core. Version de référence : Moodle 5.2 (sous-système
core_ai, apparu en 4.5 et enrichi jusqu’en 5.x).
⏱️ TL;DR
- Moodle a un sous-système IA de premier plan (
core_ai) structuré en trois briques : providers (qui parlent à un service d’IA), actions (ce qu’on demande : générer du texte, une image, résumer…), placements (où l’IA apparaît dans l’UI : éditeur, assistant de cours).- Un provider est un plugin
aiprovider_*(ex.aiprovider_openai,aiprovider_azureailivrés avec le core). Pour brancher Claude, on écritaiprovider_anthropic.- Un provider déclare les actions qu’il supporte et fournit un processeur par action (une classe qui traduit l’action Moodle en appel à l’API du fournisseur et remet en forme la réponse).
- L’appel HTTP se fait avec le client HTTP de Moodle (
\core\http_client/ Guzzle), vershttps://api.anthropic.com/v1/messages, headersx-api-key+anthropic-version, corps{model, max_tokens, messages}.- Modèles :
claude-opus-4-8(le plus capable, $5/$25 par M tokens),claude-haiku-4-5(rapide/économe, $1/$5) pour les usages simples — le modèle est un réglage admin.- Consentement et politiques :
core_aigère l’acceptation d’une politique d’usage par l’utilisateur et l’activation par contexte. À respecter.- Bonnes pratiques : maîtriser les coûts (modèle + max_tokens + cache), journaliser les appels, et protéger la confidentialité des données élèves (ne pas envoyer de données personnelles inutiles à un tiers — RGPD, ch. 11.5).
🎯 Objectifs
- Comprendre l’architecture providers / actions / placements de
core_ai. - Écrire un provider
aiprovider_anthropicbranchant l’API Claude. - Implémenter un processeur d’action (génération de texte) et l’appel HTTP sûr.
- Gérer clé API, modèle, consentement et journalisation.
- Appliquer les bonnes pratiques (coûts, confidentialité, RGPD).
1. Pourquoi passer par core_ai plutôt qu’un appel direct
Vous pourriez appeler l’API Claude depuis n’importe quel plugin avec un curl. Mais core_ai vous donne, gratuitement, ce qu’une intégration sérieuse exige :
- une abstraction fournisseur : le même code d’UI marche avec Claude, OpenAI, Azure… (on change de provider sans réécrire les placements) ;
- une gestion centralisée des clés, des réglages, du consentement utilisateur et des politiques d’usage ;
- une activation par contexte (site, catégorie, cours) et par capability ;
- une journalisation et des points d’extension cohérents.
Autrement dit, core_ai est à l’IA ce que le carnet de notes est aux notes (ch. 6.5) : un service central auquel votre plugin se branche, au lieu de réimplémenter tout le cadre.
⚠️ Piège :
core_aiest un sous-système jeune (né en 4.5, encore en évolution rapide en 5.x). Les noms de classes et signatures exacts peuvent varier entre versions. Le code de ce chapitre est fidèle à l’esprit de l’architecture 5.2 ; vérifiez les signatures précises sur moodledev.io et dans les providers du core (ai/provider/openai,ai/provider/azureai) avant de coder en production.
2. Les trois briques : providers, actions, placements
| Brique | Type de plugin | Rôle |
|---|---|---|
| Action | (classes du core, \core_ai\aiactions\*) | Ce qu’on demande : generate_text, generate_image, summarise_text, explain_text… |
| Provider | aiprovider_* | Qui exécute l’action en parlant à un service externe (Claude, OpenAI…) |
| Placement | aiplacement_* | Où l’IA apparaît dans l’UI (bouton dans l’éditeur TinyMCE, assistant de cours…) |
Le flux : un placement (ex. le bouton « Générer » dans l’éditeur) émet une action (generate_text) ; core_ai vérifie le consentement et l’activation, choisit le provider configuré, appelle son processeur pour cette action, et renvoie le résultat au placement.
Vous, dev branchant Claude, écrivez surtout un provider (aiprovider_anthropic). Les actions existent déjà (fournies par le core) ; les placements existent aussi (éditeur, assistant) et vous pouvez en écrire, mais le besoin premier est le provider.
💡 Pour un dev React : pensez à une couche d’abstraction LLM (à la Vercel AI SDK) : les actions sont les capacités typées (
generateText,generateImage), le provider est l’adaptateur d’un fournisseur (le « model provider »), et les placements sont les points d’UI qui consomment ces capacités. Vous écrivez l’adaptateur Anthropic ; le reste de l’app parle en actions abstraites.
3. Anatomie du provider aiprovider_anthropic
public/ai/provider/anthropic/
├── version.php
├── settings.php ← clé API, modèle, org…
├── lang/en/aiprovider_anthropic.php
├── classes/
│ ├── provider.php ← déclare les actions supportées
│ └── process_generate_text.php ← processeur de l'action generate_text
└── db/
└── ... ← si réglages/consentement stockésversion.php — component = 'aiprovider_anthropic', dossier ai/provider/anthropic.
4. Les réglages : clé API et modèle
<?php
// public/ai/provider/anthropic/settings.php
defined('MOODLE_INTERNAL') || die();
if ($ADMIN->fulltree) {
// Clé API Anthropic — secret, côté serveur uniquement.
$settings->add(new admin_setting_configpasswordunmask(
'aiprovider_anthropic/apikey',
get_string('apikey', 'aiprovider_anthropic'),
get_string('apikey_desc', 'aiprovider_anthropic'),
''
));
// Modèle par défaut.
$settings->add(new admin_setting_configselect(
'aiprovider_anthropic/model',
get_string('model', 'aiprovider_anthropic'),
get_string('model_desc', 'aiprovider_anthropic'),
'claude-opus-4-8',
[
'claude-opus-4-8' => 'Claude Opus 4.8 (le plus capable)',
'claude-sonnet-5' => 'Claude Sonnet 5 (équilibré)',
'claude-haiku-4-5' => 'Claude Haiku 4.5 (rapide / économe)',
]
));
// Plafond de tokens de sortie.
$settings->add(new admin_setting_configtext(
'aiprovider_anthropic/maxtokens',
get_string('maxtokens', 'aiprovider_anthropic'),
get_string('maxtokens_desc', 'aiprovider_anthropic'),
1024, PARAM_INT
));
}Le choix de modèle est un réglage : un admin économe met Haiku 4.5 ($1/$5 par million de tokens) pour un simple résumé, un admin exigeant met Opus 4.8 ($5/$25). max_tokens borne le coût de chaque réponse.
⚠️ Piège de sécurité : la clé API est un secret serveur. Elle vit dans les réglages Moodle (base), n’est jamais exposée au navigateur, et transite en HTTPS vers Anthropic. Traitez-la comme le token de la Partie 9 : côté serveur uniquement, révocable, à ne pas logger en clair.
5. La classe provider
Le provider déclare quelles actions il sait exécuter. (Signatures à vérifier sur moodledev.io ; forme représentative de la 5.2.)
<?php
// public/ai/provider/anthropic/classes/provider.php
namespace aiprovider_anthropic;
defined('MOODLE_INTERNAL') || die();
class provider extends \core_ai\provider {
/**
* Liste des actions supportées par ce provider.
*
* @return array de FQCN d'actions \core_ai\aiactions\*
*/
public function get_action_list(): array {
return [
\core_ai\aiactions\generate_text::class,
\core_ai\aiactions\summarise_text::class,
\core_ai\aiactions\explain_text::class,
];
}
/**
* Le provider est-il correctement configuré (clé API présente) ?
*/
public function is_provider_configured(): bool {
return !empty(get_config('aiprovider_anthropic', 'apikey'));
}
}Chaque action déclarée doit avoir son processeur (§6). Ici on supporte trois actions textuelles ; on n’expose pas generate_image (Claude est un modèle de texte — soyez honnête sur les capacités).
6. Le processeur d’action : appeler l’API Claude
Le cœur : traduire l’action generate_text en appel à l’API Messages d’Anthropic, avec le client HTTP de Moodle.
<?php
// public/ai/provider/anthropic/classes/process_generate_text.php
namespace aiprovider_anthropic;
defined('MOODLE_INTERNAL') || die();
class process_generate_text extends \core_ai\process_base {
/**
* Exécute l'action et renvoie une réponse d'action.
*
* @return \core_ai\aiactions\responses\response_base
*/
public function process(): \core_ai\aiactions\responses\response_base {
$action = $this->get_action();
$prompt = $action->get_configuration('prompttext'); // le texte demandé par l'utilisateur
$apikey = get_config('aiprovider_anthropic', 'apikey');
$model = get_config('aiprovider_anthropic', 'model') ?: 'claude-opus-4-8';
$maxtokens = (int) (get_config('aiprovider_anthropic', 'maxtokens') ?: 1024);
// Client HTTP de Moodle (Guzzle) — gère proxy, timeouts, logs.
$client = \core\di::get(\core\http_client::class);
try {
$response = $client->post('https://api.anthropic.com/v1/messages', [
'headers' => [
'x-api-key' => $apikey,
'anthropic-version' => '2023-06-01',
'content-type' => 'application/json',
],
'body' => json_encode([
'model' => $model,
'max_tokens' => $maxtokens,
'messages' => [
['role' => 'user', 'content' => $prompt],
],
]),
]);
$data = json_decode((string) $response->getBody(), true);
// La réponse Anthropic : content est une liste de blocs ; on prend le texte.
$text = '';
foreach (($data['content'] ?? []) as $block) {
if (($block['type'] ?? '') === 'text') {
$text .= $block['text'];
}
}
// Réponse d'action réussie (forme représentative — vérifier en 5.2).
return new \core_ai\aiactions\responses\response_generate_text(
success: true,
generatedcontent: $text,
// + métadonnées : modèle, tokens (data['usage']), finishreason…
);
} catch (\Throwable $e) {
// Ne jamais fuiter la clé ni le détail brut dans un message utilisateur.
\core\notification::add_all_to_output; // exemple ; en réel : journaliser proprement
debugging('Anthropic API error: ' . $e->getMessage(), DEBUG_DEVELOPER);
return new \core_ai\aiactions\responses\response_generate_text(
success: false,
errorcode: 500,
error: get_string('apierror', 'aiprovider_anthropic'),
);
}
}
}Points cruciaux :
- Client HTTP de Moodle (
\core\http_client, basé sur Guzzle) — pas decurlbrut : il respecte le proxy configuré, les timeouts, et s’intègre aux tests. C’est l’équivalent serveur du chapitre 9.5, côté sortant. - Endpoint et headers Anthropic :
POST https://api.anthropic.com/v1/messages,x-api-key(la clé),anthropic-version: 2023-06-01, corps{model, max_tokens, messages}. - La réponse a un champ
contentqui est une liste de blocs ; on concatène les blocs de typetext. (Comme côté JS au chapitre 9 : ne pas supposercontent[0].) - Gestion d’erreur : on journalise côté serveur (sans la clé) et on renvoie un message générique à l’utilisateur. Ne jamais exposer le détail de l’API ni la clé.
usagedans la réponse (input_tokens/output_tokens) permet de suivre les coûts — à journaliser (§8).
⚠️ Piège du modèle : utilisez les IDs de modèle exacts (
claude-opus-4-8,claude-haiku-4-5,claude-sonnet-5) — un ID inventé ou daté à tort renvoie une 404. Ne construisez pas d’ID vous-même ; prenez ceux des réglages (§4).
💡 Pour un dev React : c’est le même appel que vous feriez avec le SDK Anthropic, transposé côté PHP serveur. La logique (endpoint, headers,
messages, lecture decontent[]) est identique à ce que fait@anthropic-ai/sdksous le capot. Ici on reste en HTTP brut via le client de Moodle, car on est dans le sous-système IA du LMS, pas dans une app Next.js.
7. Consentement, politiques et activation
core_ai impose un cadre de gouvernance que votre provider doit respecter :
- Politique d’usage / consentement : avant qu’un utilisateur n’utilise l’IA, Moodle peut exiger l’acceptation d’une politique (l’IA envoie du texte à un tiers).
core_aigère cet écran d’acceptation. - Activation par contexte : l’admin active l’IA (et chaque action) au niveau site, et éventuellement restreint par catégorie/cours et par capability. Une action peut être activée pour la génération de texte mais pas pour autre chose.
- Capabilities : l’usage de l’IA est protégé par des capabilities (Partie 2) ; tout le monde n’a pas droit à tout.
Votre provider ne réimplémente pas ce cadre : il s’y branche. core_ai vérifie consentement + activation avant d’appeler votre process(). Vous vous concentrez sur l’appel API et la mise en forme.
⚠️ Piège de conformité : ne contournez jamais le cadre de consentement pour « simplifier ». Envoyer du contenu d’élève à un LLM externe sans consentement ni activation est un problème RGPD (ch. 11.5) et de confiance. Le cadre existe pour une raison légale.
8. Bonnes pratiques : coûts, journalisation, confidentialité
- Maîtriser les coûts : le modèle (Haiku vs Opus, facteur ~5×) et
max_tokenssont vos deux leviers. Journalisezusage.input_tokens/output_tokensde chaque appel pour suivre la dépense. Pour des prompts répétés (mêmes instructions système), le prompt caching d’Anthropic réduit fortement le coût — envisageable pour un usage à volume. - Journalisation : loggez les appels (qui, quand, quelle action, combien de tokens, succès/échec) — sans le contenu sensible ni la clé. Utile pour le monitoring (ch. 11.2) et l’audit.
- Confidentialité des données élèves : minimisez ce que vous envoyez à Anthropic. N’incluez pas de données personnelles inutiles (noms, e-mails, identifiants) dans le prompt. C’est un traitement de données par un tiers → déclaration RGPD (ch. 11.5), et le privacy provider de votre plugin doit le refléter.
- Honnêteté sur les capacités : ne déclarez que les actions réellement supportées. Claude fait du texte ; n’exposez pas
generate_image. - Gestion des erreurs et des refus : l’API peut renvoyer une erreur (429 rate limit, 529 surcharge) ou, sur certains modèles, un refus de sécurité. Gérez ces cas gracieusement (message clair, éventuel retry avec backoff).
📚 Aller plus loin : la référence du sous-système est sur moodledev.io/docs/5.2/apis/subsystems/ai . Lisez les providers du core (
ai/provider/openai,ai/provider/azureai) — ce sont vos modèles de référence, à jour avec les signatures exactes de la 5.2. Pour l’API Claude elle-même (modèles, prix, paramètres), la doc officielle Anthropic (platform.claude.com/docs ) fait foi.
9. Différences 4.5 → 5.2 (aperçu)
Le sous-système core_ai est apparu en 4.5 et s’est enrichi jusqu’en 5.2 : plus d’actions, plus de placements (éditeur, assistant de cours), une gestion du consentement et des politiques affinée, et des providers du core supplémentaires. C’est un domaine en mouvement rapide : chaque version majeure (deux par an) peut ajouter des actions ou changer des signatures. Conséquence pratique (Partie 6) : un provider aiprovider_* doit être testé et remis à jour à chaque version de Moodle, plus encore que les autres plugins — vérifiez les upgrade notes IA à chaque sortie.
10. Synthèse
core_ai = un cadre providers / actions / placements dans lequel votre plugin aiprovider_anthropic se branche pour exposer Claude. Vous écrivez un provider (liste des actions supportées) et un processeur par action qui appelle l’API Messages d’Anthropic via le client HTTP de Moodle, avec un modèle et un max_tokens réglables. Le core gère consentement, activation, capabilities ; vous respectez coûts, journalisation et confidentialité (RGPD, ch. 11.5). C’est le même réflexe que partout dans Moodle : se brancher sur le sous-système, ne pas le réinventer.
✏️ Exercices
Exercice 1 — Trois briques.
Associez : (a) « bouton Résumer dans l’éditeur TinyMCE » ; (b) summarise_text ; (c) aiprovider_anthropic. → provider / action / placement.
✅ Solution
(a) placement (aiplacement_editor — où l’IA apparaît dans l’UI) ; (b) action (\core_ai\aiactions\summarise_text — ce qu’on demande) ; (c) provider (qui exécute l’action en appelant Claude). Le placement émet l’action, core_ai la route vers le provider actif.
Exercice 2 — Pas de curl brut.
Pourquoi utiliser \core\http_client plutôt qu’un curl_exec direct dans le processeur ?
✅ Solution
Le client HTTP de Moodle (Guzzle) respecte le proxy configuré du site, applique les timeouts cohérents, s’intègre au débogage et aux tests (mockable), et centralise la gestion réseau. Un curl brut ignore la config proxy (échoue derrière un pare-feu d’entreprise), n’est pas testable proprement, et duplique une plomberie que le core fournit. Réflexe Moodle : utiliser l’API du core, pas réinventer le réseau.
Exercice 3 — Lire la réponse.
Un dev fait $text = $data['content'][0]['text']; et ça plante parfois. Pourquoi, et le correctif ?
✅ Solution
Le champ content de la réponse Anthropic est une liste de blocs de types variés (text, et potentiellement d’autres) ; content[0] n’est pas garanti d’être un bloc text (ni de exister si la requête échoue/refuse). Correctif : itérer sur content, filtrer les blocs type === 'text' et concaténer leur text — après avoir vérifié le succès de l’appel (code HTTP + absence d’erreur dans le corps). Ne jamais supposer content[0].
Exercice 4 — Choisir le modèle. Pour (a) un résumé automatique de forum à grand volume et (b) une aide à la rédaction de feedback nuancé, quel modèle règleriez-vous, et sur quel levier jouez-vous pour le coût ?
✅ Solution
(a) claude-haiku-4-5 (rapide, ~$1/$5 par M tokens) suffit pour des résumés à volume — le levier est le modèle (économe) + un max_tokens serré. (b) claude-opus-4-8 (le plus capable, ~$5/$25) pour un feedback nuancé où la qualité prime — levier : accepter un coût par appel plus élevé mais volume plus faible. Dans les deux cas, journaliser usage pour suivre la dépense réelle, et envisager le prompt caching si le prompt système est répété.
Exercice 5 — Confidentialité. Un dev inclut, dans le prompt envoyé à Claude, le nom complet et l’e-mail de l’étudiant « pour personnaliser ». Quels problèmes, et que faire ?
✅ Solution
Problèmes : (1) RGPD — envoyer des données personnelles à un tiers (Anthropic) est un traitement qui doit être déclaré (privacy provider, ch. 11.5), consenti et minimisé ; ici les données ne sont pas nécessaires à la tâche ; (2) surface de fuite et coût (tokens) inutiles. À faire : retirer les données personnelles du prompt (personnaliser côté Moodle avec le résultat, pas dans l’envoi), respecter le cadre de consentement de core_ai, et refléter le traitement IA dans le privacy provider du plugin. Principe : minimisation — n’envoyer que le strict nécessaire.
🧠 Quiz de révision
Q1. Quelles sont les trois briques de core_ai et leurs rôles ?
Réponse
aiprovider_*, exécutent en parlant à un service externe), actions (\core_ai\aiactions\*, ce qu’on demande : générer/résumer/expliquer du texte…), placements (aiplacement_*, où l’IA apparaît dans l’UI). Un placement émet une action, core_ai la route vers le provider actif.Q2. Que déclare la classe provider d’un plugin aiprovider_* ?
Réponse
get_action_list()) et son état de configuration (is_provider_configured()). Chaque action déclarée doit avoir un processeur qui l’exécute. On ne déclare que les actions réellement supportées (Claude = texte, pas d’image).Q3. Comment le processeur appelle-t-il l’API Claude, et quels headers ?
Réponse
\core\http_client/Guzzle), en POST sur https://api.anthropic.com/v1/messages, avec les headers x-api-key (la clé), anthropic-version: 2023-06-01 et content-type: application/json, et un corps {model, max_tokens, messages}. La réponse a un champ content (liste de blocs) dont on concatène les blocs text.Q4. Qui gère le consentement et l’activation, et qu’implique-t-on de ne pas les contourner ?
Réponse
core_ai gère l’acceptation d’une politique d’usage, l’activation par contexte/capability, et vérifie tout cela avant d’appeler le processeur du provider. Ne pas contourner : envoyer du contenu d’élève à un LLM externe sans consentement/activation est un problème RGPD et de confiance — le cadre est une obligation, pas une option.Q5. Citez trois bonnes pratiques d’un provider IA en production.
Réponse
max_tokens serré, journalisation de usage, prompt caching si volume. (2) Confidentialité : minimiser les données envoyées (pas de données personnelles inutiles), refléter le traitement dans le privacy provider (RGPD). (3) Journalisation et gestion d’erreurs : logguer les appels sans la clé/le contenu sensible, gérer 429/529/refus gracieusement. Bonus : n’exposer que les actions réellement supportées, et tester à chaque version (core_ai évolue vite).Chapitre suivant : 07 — Maintenance et mises à jour d’une instance — le cycle de release Moodle et la stratégie de mise à jour, le chemin d’upgrade vers 5.2, la procédure complète et scriptée (backup, mode maintenance, bascule git, upgrade CLI, plugins incompatibles), la gestion des données qui grossissent, et le monitoring d’une production dans la durée.