reactSim = {
// Dépendance réactive par rapport au choix de la mission
mission_select;
const isMission1 = mission_select.includes("Mathématiques");
const steps = isMission1 ? [
{ type: "thought", activeNode: "T", activeLink: null, pastNodes: [], pastLinks: [], text: "Je dois d'abord calculer la racine carrée de 841.", delay: 1800 },
{ type: "action", activeNode: "A", activeLink: "T-A", pastNodes: ["T"], pastLinks: [], tool: "Calculatrice", input: "sqrt(841)", delay: 1500 },
{ type: "obs", activeNode: "O", activeLink: "A-O", pastNodes: ["T", "A"], pastLinks: ["T-A"], text: "29", delay: 1500 },
{ type: "thought", activeNode: "T", activeLink: "O-T", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O"], text: "Maintenant, je dois multiplier le résultat (29) par 12.", delay: 1800 },
{ type: "action", activeNode: "A", activeLink: "T-A", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T"], tool: "Calculatrice", input: "29 * 12", delay: 1500 },
{ type: "obs", activeNode: "O", activeLink: "A-O", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A"], text: "348", delay: 1500 },
{ type: "thought", activeNode: "T", activeLink: "O-T", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A", "A-O"], text: "J'ai la réponse finale.", delay: 1200 },
{ type: "final", activeNode: "F", activeLink: "T-F", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A", "A-O", "O-T"], text: "La racine carrée de 841 multipliée par 12 est 348.", delay: 1800 }
] : [
{ type: "thought", activeNode: "T", activeLink: null, pastNodes: [], pastLinks: [], text: "Je dois trouver l'année de naissance d'Alan Turing.", delay: 1800 },
{ type: "action", activeNode: "A", activeLink: "T-A", pastNodes: ["T"], pastLinks: [], tool: "Recherche_Web", input: "Alan Turing année naissance", delay: 1800 },
{ type: "obs", activeNode: "O", activeLink: "A-O", pastNodes: ["T", "A"], pastLinks: ["T-A"], text: "Alan Mathison Turing est né le 23 juin 1912 à Londres.", delay: 1500 },
{ type: "thought", activeNode: "T", activeLink: "O-T", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O"], text: "Turing est né en 1912. Je dois maintenant trouver l'année de naissance de Yann LeCun.", delay: 2000 },
{ type: "action", activeNode: "A", activeLink: "T-A", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T"], tool: "Recherche_Web", input: "Yann LeCun année naissance", delay: 1800 },
{ type: "obs", activeNode: "O", activeLink: "A-O", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A"], text: "Yann LeCun est un chercheur né le 8 juillet 1960 en France.", delay: 1500 },
{ type: "thought", activeNode: "T", activeLink: "O-T", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A", "A-O"], text: "LeCun est né en 1960. Je dois additionner 1912 et 1960.", delay: 1800 },
{ type: "action", activeNode: "A", activeLink: "T-A", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A", "A-O", "O-T"], tool: "Calculatrice", input: "1912 + 1960", delay: 1500 },
{ type: "obs", activeNode: "O", activeLink: "A-O", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A", "A-O", "O-T", "T-A"], text: "3872", delay: 1500 },
{ type: "thought", activeNode: "T", activeLink: "O-T", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A", "A-O", "O-T", "T-A", "A-O"], text: "Le calcul est terminé, j'ai la réponse finale.", delay: 1200 },
{ type: "final", activeNode: "F", activeLink: "T-F", pastNodes: ["T", "A", "O"], pastLinks: ["T-A", "A-O", "O-T", "T-A", "A-O", "O-T", "T-A", "A-O", "O-T"], text: "Le résultat de l'addition de leurs années de naissance (1912 + 1960) est 3872.", delay: 1800 }
];
const graphNodes = [
{ id: "T", label: "🧠 Pensée", shape: "rounded rect", fx: 250, fy: 60 },
{ id: "A", label: "🛠️ Action", shape: "rounded rect", fx: 380, fy: 160 },
{ id: "O", label: "👁️ Obs", shape: "rounded rect", fx: 250, fy: 260 },
{ id: "F", label: "✅ Réponse", shape: "pill", fx: 120, fy: 160 }
];
const graphLinks = [
{ source: "T", target: "A", label: "Outil" },
{ source: "A", target: "O", label: "Résultat" },
{ source: "O", target: "T", label: "Raisonner" },
{ source: "T", target: "F", label: "Synthèse" }
];
const graph = aptitek.renderStateMachineGraph("#react-loop", { nodes: graphNodes, links: graphLinks }, {
nodeRadius: 26,
fontSize: 10,
enableZoom: false,
enablePan: false,
zoomToFit: true,
zoomToFitPadding: 45,
height: 320
});
const terminal = aptitek.createTerminal("#react-terminal", { defaultPrompt: false, showCursor: false });
terminal.writeLine("En attente de démarrage...", { type: "muted" });
let currentIndex = 0;
let timeoutId = null;
let simCtrl = null;
function updateVisuals(index) {
const step = steps[index];
// Met à jour l'état des nœuds
graphNodes.forEach(n => {
if (n.id === step.activeNode) n.status = "current";
else if (step.pastNodes.includes(n.id)) n.status = "past";
else n.status = "default";
});
// Met à jour l'état des liens
graphLinks.forEach(l => {
const srcId = typeof l.source === "object" ? l.source.id : l.source;
const tgtId = typeof l.target === "object" ? l.target.id : l.target;
const lid = `${srcId}-${tgtId}`;
if (lid === step.activeLink) l.status = "current";
else if (step.pastLinks.includes(lid)) l.status = "past";
else l.status = "default";
});
graph.graphData({ nodes: graphNodes, links: graphLinks });
try {
graph.zoomToFit(200, 45);
} catch (e) {}
// Print to terminal
if (step.type === "thought") {
terminal.writeLine(`> PENSÉE : ${step.text}`, { type: "info" });
} else if (step.type === "action") {
terminal.writeLine(`> ACTION : Appel de l'outil "${step.tool}" avec arguments :`, { type: "warning" });
terminal.writeLine(` └─ input: "${step.input}"`, { type: "muted" });
} else if (step.type === "obs") {
terminal.writeLine(`> OBSERVATION : ${step.text}`, { type: "success" });
} else if (step.type === "final") {
terminal.writeLine("");
terminal.writeLine(`[ RÉPONSE FINALE ]`, { type: "success", class: "fw-bold" });
terminal.writeLine(step.text, { type: "success" });
}
}
function playNext() {
if (currentIndex >= steps.length) {
if (simCtrl) {
simCtrl.setState("idle");
}
return;
}
updateVisuals(currentIndex);
const step = steps[currentIndex];
currentIndex++;
timeoutId = setTimeout(playNext, step.delay);
}
simCtrl = aptitek.createSimulationControl("#react-ctrl", {
onStart: () => {
if (currentIndex === 0) {
terminal.clear();
terminal.writeLine(`[SYSTEM] Initialisation de l'Agent avec 2 outils.`, { type: "muted" });
terminal.writeLine(`[SYSTEM] Mission : "${mission_select}"`, { type: "muted" });
terminal.writeLine("");
}
playNext();
},
onPause: () => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
terminal.writeLine("[PAUSE]", { type: "warning" });
},
onStop: () => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
currentIndex = 0;
terminal.clear();
terminal.writeLine("Simulation arrêtée. En attente de démarrage...", { type: "muted" });
// Réinitialiser les états du graphe
graphNodes.forEach(n => n.status = "default");
graphLinks.forEach(l => l.status = "default");
graph.graphData({ nodes: graphNodes, links: graphLinks });
try {
graph.zoomToFit(200, 45);
} catch (e) {}
},
onRestart: () => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
currentIndex = 0;
terminal.clear();
terminal.writeLine(`[SYSTEM] Redémarrage...`, { type: "muted" });
terminal.writeLine(`[SYSTEM] Mission : "${mission_select}"`, { type: "muted" });
terminal.writeLine("");
playNext();
}
}, invalidation);
invalidation.then(() => {
if (timeoutId) clearTimeout(timeoutId);
graph?.destroy?.();
terminal?.destroy?.();
});
return { graph, simCtrl };
}Model Context Protocol (MCP)
LLM & IA
Deep Learning
Connecter les agents LLM aux outils externes, bases de données et API via le protocole universel MCP.
Intégrer un LLM dans une application d’entreprise, c’est comme changer de pays avec ses appareils électroniques : chaque connecteur est différent. Le Model Context Protocol (MCP) est la réponse de l’industrie — le port USB-C universel de l’IA.
🧩 Le Problème N×M
Sans standard, connecter N fournisseurs de LLM (OpenAI, Anthropic, Google…) à M applications d’entreprise (Slack, GitHub, bases de données…) exige N × M intégrations sur mesure. Chaque connexion parle un dialecte différent, se brise à chaque mise à jour d’API, et génère une dette technique incontrôlable.
Sans MCP : L’Explosion Combinatoire
3 LLM × 5 outils = 15 connecteurs custom à maintenir. Multiplié par les versions d’API et les fournisseurs… c’est une avalanche de code spaghetti.
Avec MCP : Un Langage Commun
Un seul protocole universel. Chaque outil expose un Serveur MCP ; chaque LLM consomme un Client MCP. Quel que soit le fournisseur, la “prise” est désormais identique.
🏗️ L’Architecture
MCP définit trois acteurs qui communiquent via JSON-RPC 2.0 — un protocole léger et standardisé pour appeler des fonctions à distance.
Le Serveur MCP
Programme léger qui expose données ou outils de manière standardisée.
Exemples : lecture de PostgreSQL, appels GitHub, consultation de calendrier.
Le Client MCP
Le connecteur intégré dans votre framework IA (LangChain, LlamaIndex…). Il interroge le serveur, filtre les données sensibles, et relaie le résultat au LLM.
L’Hôte
L’application finale avec laquelle l’utilisateur interagit : Claude Desktop, VS Code, un chatbot d’entreprise. C’est lui qui pilote le Client.
Tip
La bibliothèque communautaire Glama MCP Servers répertorie des centaines de serveurs prêts à l’emploi — bases de données, GitHub, Slack, outils de développement.
Qu’est-ce que JSON-RPC 2.0 ?
JSON-RPC est un protocole d’appel de procédure à distance (Remote Procedure Call). Léger, lisible, indépendant du transport (HTTP, WebSocket, stdio) :
{ "method": "tools/call", "params": { "name": "get_weather", "arguments": { "city": "Paris" } }, "id": 1 }🧠 La Boucle ReAct
Pour qu’un LLM utilise des outils sans se perdre, on lui impose un cadre de raisonnement : ReAct (Reasoning + Acting). Contrairement à la simple “Chain-of-Thought”, ReAct force l’agent à alterner entre réflexion et réalité — après chaque action, il observe le résultat avant de décider de la suite.
Le Cerveau de l’Agent : Simulateur ReAct
Cycle de Raisonnement
Console de l’Agent
✂️ Divulgation Progressive
Un agent avec accès à 1 000 outils ne peut pas charger tous leurs schémas en contexte : il saturerait la fenêtre du LLM avant même de répondre.
La Divulgation Progressive (Progressive Disclosure) résout ce problème : l’agent explore l’arborescence des outils à la demande, comme un explorateur de fichiers. Il ne charge que le schéma de l’outil dont il a besoin maintenant.
Schémas d’outils chargés en contexte
Tip
En pratique, un agent avec 1 000 outils disponibles n’en charge en moyenne que ~13 par tâche — soit une réduction de 98,7 % des tokens consommés par les définitions d’outils.
🐝 Multi-Agents
Pour les tâches trop complexes pour un seul agent, on recourt à une architecture Leader-Worker : un agent “chef d’orchestre” décompose la requête et délègue à des agents spécialisés, qui communiquent entre eux via le protocole A2A (Agent-to-Agent).
Architecture Leader-Worker
Pourquoi plusieurs agents plutôt qu’un seul ?
Un LLM unique sur une tâche très longue accumule les erreurs : plus le contexte grandit, plus les hallucinations se multiplient. Découper en agents spécialisés avec des contextes courts et ciblés réduit drastiquement ce phénomène. Chaque agent ne fait qu’une chose, mais la fait bien.