API d'IA intégrées : bonnes pratiques et pratiques à éviter

Publié le : 30 avril 2026

Grâce à l'IA intégrée, votre site Web ou votre application Web peuvent effectuer des tâches optimisées par l'IA, sans avoir à déployer, gérer ni auto-héberger des modèles. Il peut être difficile de passer d'une démo à une fonctionnalité prête pour la production. Ce document aborde les considérations techniques et liées à l'expérience utilisateur pour vous aider à éviter les pièges courants.

Préparer le modèle à l'avance

S'applique à toutes les API, par exemple Summarizer, Translator et Writer.

À faire : Initialisez la session dès que vous identifiez l'intention de l'utilisateur. Étant donné que l'activation de l'utilisateur est requise pour initialiser une session, vous pouvez utiliser n'importe quelle interaction, par exemple un clic n'importe où sur la page proposant une fonctionnalité optimisée par l'IA. Cela prépare le modèle et le runtime pendant que l'utilisateur interagit avec l'UI. Lorsque cela est pertinent, lancez la tâche d'IA suivante la plus probable dès que vous commencez à afficher le résultat.

Ne faites pas : attendez que l'utilisateur clique sur "Générer" pour initialiser la session. Cela entraîne un délai de démarrage à froid de plusieurs secondes, car le modèle doit d'abord se charger en mémoire et préparer son pipeline d'exécution.

Définir des invites initiales lors de la création

S'applique à l'API Prompt.

À faire : fournissez des instructions système lors de l'initialisation de la session pour améliorer la vitesse du premier prompt.

Ne faites pas : commencez par une session vide et envoyez les instructions système dans le premier appel prompt(). Cela augmente la latence, car cela oblige le modèle à traiter ces instructions au dernier moment.

// ✅ DO: Create the session as early as possible (tip on warming up the model early) and use initialPrompts for system instructions in the create call
const session = await LanguageModel.create({
  initialPrompts: [
    { role: 'system', content: 'You are a helpful assistant specialized in code reviews.' }
  ]
});

// A few moments later, when the user triggers the AI feature
const review = await session.prompt(`Review the following code:\n\n${code}`);

// ❌ DON'T: Send instructions using prompt() after creation
// const slowerSession = await LanguageModel.create();
// await slowerSession.prompt(`You are a helpful assistant specialized in code reviews.\n\nReview the following code:\n\n${code}`); // Higher latency

Cloner des sessions pour les tâches répétitives

S'applique à l'API Prompt.

Pour l'API Prompt, chaque session suit le contexte de la conversation en tenant compte de toutes les interactions précédentes. Étant donné qu'un clone hérite de tout de sa session parente, y compris des requêtes initiales et de l'historique des interactions jusqu'au point de clonage, structurez votre utilisation pour n'hériter que de ce dont vous avez besoin.

À faire :

  • Créez une session de base : pour gérer efficacement les tâches sans rapport, créez une session de base qui ne contient que vos instructions système et aucun contexte conversationnel précédent.
  • Cloner la référence : utilisez clone() sur cette session de base pour les nouvelles tâches afin d'éviter la surcharge liée à l'analyse de nouvelles instructions système lourdes. Cela vous permet de créer des conversations parallèles ou de réinitialiser une tâche à sa base de référence.

À éviter :

  • N'utilisez pas la même session pour des tâches sans rapport et évitez de cloner une session qui contient déjà un historique des interactions inutile. Ces deux schémas peuvent entraîner une interférence du contexte précédent sans rapport avec votre tâche actuelle.
  • N'appelez pas create() de manière répétée avec des instructions système identiques et lourdes. Utilisez plutôt le modèle de clonage pour optimiser les performances.
// ✅ DO: Create a baseline session and clone it for each new task
const baseSession = await LanguageModel.create({
  initialPrompts: [{
    role: 'system',
    content: 'You are a technical editor...',
  }],
});

// Clone the base session once for the first task
const task1 = await baseSession.clone();
const response1 = await task1.prompt("Review this first draft...");
// ... Repeat the cloning pattern for subsequent independent tasks
// Each task starts fresh from the baseline system instructions

// ❌ DON'T:
// Bad performance pattern: repeated create() calls for identical tasks.
// This forces the model to re-parse instructions every time, increasing latency.
// const sessionA = await LanguageModel.create({ initialPrompts: [...] });
// await sessionA.prompt("Task 1...");
// const sessionB = await LanguageModel.create({ initialPrompts: [...] });
// await sessionB.prompt("Task 2...");
// Bad quality pattern: reusing the same session for unrelated tasks.
// const session = await LanguageModel.create();
// await session.prompt("Analyze this financial report...");
// Unrelated task in the same session:
// await session.prompt("Now write a children's story...");

Détruire les sessions inutilisées

S'applique à : toutes les API.

À faire : appelez explicitement destroy() sur les sessions dont vous n'avez plus besoin, pour libérer de la mémoire lorsqu'une fonctionnalité n'est plus utilisée. Si vous utilisez un modèle de clonage, conservez la session de base et détruisez les clones dont vous n'avez plus besoin.

À éviter : laisser plusieurs sessions volumineuses actives. Chaque session consomme de la mémoire, ce qui entraîne une utilisation inutile des ressources et peut devenir problématique. Les sessions seront naturellement nettoyées par le garbage collector, mais l'appel de destroy() libère de la mémoire plus rapidement.

// ✅ DO: Use the clone and destroy it immediately after
const clone = await baseSession.clone();
const response = await clone.prompt("Quick task...");
// Free memory right away: destry the clone, keep the baseSession
clone.destroy();

Afficher les réponses en streaming de manière sûre et efficace

S'applique à toutes les API compatibles avec le streaming (Prompt, Summarizer, Writer, Rewriter et Translator).

À faire : considérez tous les résultats des LLM comme du contenu non fiable. Assainissez la sortie combinée complète, et pas seulement les blocs, car du code malveillant peut être réparti sur plusieurs mises à jour. Avant le rendu, utilisez l'API Sanitizer lorsque cela est possible. Pour éviter une baisse des performances, utilisez un analyseur Markdown de streaming tel que streaming-markdown.

À ne pas faire : ne définissez pas directement innerHTML à chaque mise à jour de bloc. Cette méthode est lente, surtout avec une mise en forme complexe comme la coloration syntaxique, et vulnérable aux injections.

import * as smd from "streaming-markdown";
// Set up virtual buffer and Sanitizer API
const sanitizer = new Sanitizer({
  allowElements: ['figure', 'figcaption', 'p', 'br', 'strong', 'em', 'img', 'a'],
  allowAttributes: {
    'loading': ['img'], 'decoding': ['img'], 'src': ['img'], 'href': ['a']
  }
});

// Create an off-screen fragment so the parser doesn't cause flicker
// or trigger XSS in the live DOM during the building process.
const buffer = new DocumentFragment();
const parser = smd.parser_new(buffer);

// Use sanitizer as a gatekeeper / cleaner function so we can combine it with the streaming Markdown parser
function syncSanitized(target, sourceFragment) {
  // .sanitize() returns a fresh, clean DocumentFragment
  const cleanFragment = sanitizer.sanitize(sourceFragment);
  // replaceChildren is the modern high-performance way to swap DOM content
  target.replaceChildren(cleanFragment);
}

// Streaming Logic
// `chunks` keeps track of the raw string (useful for logs/debug)
chunks += chunk;
// Let the parser build the DOM incrementally in the buffer.
// This is high-performance because the buffer is not live
smd.parser_write(parser, chunk);
// Use the Sanitizer API to port the content safely to the container.
syncSanitized(container, buffer);

Optimiser la saisie pour la vitesse

S'applique à : toutes les API.

À faire : ne transmettez au modèle que ce qui est strictement nécessaire. Supprime tout ce qui n'est pas pertinent pour la tâche à accomplir. Pour les grands ensembles de données, fournissez un bref aperçu et une petite sélection d'éléments pertinents.

Ne faites pas : n'envoyez pas de texte brut non traité, de métadonnées inutiles, de balises HTML ni de grandes listes non filtrées aux API. La latence augmente considérablement avec la taille de l'entrée, ce qui peut donner l'impression que la fonctionnalité d'IA ne fonctionne pas sur de nombreux appareils.

// ✅ DO: Send only relevant text
const cleanText = document.querySelector('#article').innerText;
const summary = await Summarizer.summarize(cleanText);

// ❌ DON'T: Send the entire DOM structure
// const dirtyText = document.querySelector('#article').innerHTML;

Utiliser une sortie structurée pour obtenir des résultats prévisibles

S'applique à l'API Prompt.

À faire : lorsque vous avez besoin que le modèle renvoie des données dans un format spécifique, utilisez la sortie structurée en fournissant un champ responseConstraint pour fournir un schéma JSON. Cela garantit que le résultat est prévisible et vous évite d'avoir besoin d'un post-traitement complexe ou d'une analyse manuelle.

À éviter : ne vous fiez pas uniquement aux instructions en langage naturel (comme "générer uniquement du JSON"). Les modèles peuvent inclure des mots de remplissage qui cassent votre analyseur.

// ✅ DO: Use a JSON Schema for predictable results
const schema = {
  type: "object",
  properties: {
    isTopicCats: { type: "boolean" }
  }
};

const result = await session.prompt(`Is this post about cats?\n\n${post}`, {
  responseConstraint: schema,
});
console.log(JSON.parse(result).isTopicCats);

Dissocier la génération des contraintes de longueur

S'applique à l'API Prompt, car il s'agit de la seule API compatible avec les schémas de sortie structurée.

À faire : laissez le modèle générer sa réponse naturellement, puis utilisez la logique côté client pour tronquer le texte afin qu'il s'adapte à votre UI.

À ne pas faire : appliquer des limites de caractères strictes comme maxLength: 125 à l'aide de schémas de sortie structurés. Lorsque la réponse d'un modèle dépasse la limite que vous avez définie, le modèle peut passer à des jetons à haute densité tels que des langues étrangères ou des emoji pour compresser le sens, ce qui peut entraîner une sortie absurde.

/*  DO: Handle overflow using CSS */
.result {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis; /* Displays '…' */
}
// ❌ DON'T: Force length in the prompt
const result = await session.prompt("Write a bio in exactly 50 characters.");

Gérer la patience de l'utilisateur

S'applique à : toutes les API.

À faire : utilisez des animations et des techniques d'UI pour gérer la patience de l'utilisateur. L'approche optimale dépend de votre cas d'utilisation et de la longueur attendue de la sortie de l'API. Voici quelques idées :

  • Streaming pour les contenus longs : pour les résumés ou le chat, le streaming crée par défaut un effet de machine à écrire par jeton. Cela peut sembler naturel et fournir un retour immédiat.
  • Non-streaming pour les tâches courtes (ou les tâches asynchrones longues) : pour les sorties courtes, par exemple le texte alternatif, le non-streaming peut créer une interface utilisateur plus soignée. Il permet également de préparer de manière spéculative la prochaine tâche d'IA pendant que la tâche actuelle est en cours de rendu. Cette approche fonctionne également pour les tâches asynchrones ou en arrière-plan plus longues. Si l'utilisateur n'est pas bloqué dans la sortie pour poursuivre son parcours, il n'est pas urgent de produire la sortie au fur et à mesure. Indiquez que le processus est en cours dans l'UI.
  • Transitions visuelles pour les mises à jour : lorsque vous traduisez ou réécrivez du texte, utilisez des animations, par exemple la morphose de mots.

À éviter : mettre à jour l'UI sans repères visuels.

S'aligner sur le modèle mental de l'utilisateur concernant le temps et le travail

S'applique à : toutes les API.

À faire : envisagez un délai artificiel d'une ou deux secondes si une réponse est presque instantanée. Paradoxalement, les utilisateurs peuvent trouver les résultats plus fiables lorsqu'ils perçoivent un processus de génération qui correspond à la difficulté qu'ils perçoivent pour la tâche. Utilisez des animations pour signaler qu'un processus d'IA a eu lieu.

À éviter : surprendre les utilisateurs avec des remplacements instantanés de l'UI.

Permettre aux utilisateurs de naviguer rapidement et d'annuler les modifications apportées par l'IA

S'applique à : toutes les API.

À faire : équipez votre UI d'un sélecteur ou d'un historique de navigation qui permet aux utilisateurs d'explorer différents résultats en toute confiance et de revenir rapidement en arrière après des modifications apportées par l'IA. Cela garantit que les différentes versions restent facilement disponibles.

Ne faites pas : écraser le brouillon précédent de l'utilisateur ou un résultat d'IA qu'il a pu apprécier sans lui donner la possibilité de revenir en arrière, d'annuler ou de comparer les versions.

Élément d'interface utilisateur Stepper affichant l'historique de navigation.
UI pattern: Reject / Accept suggestion (Google docs)
Bouton "Annuler toutes les modifications de l'agent" dans une interface utilisateur Google Antigravity.
UI pattern: Undo all agent edits (Google Antigravity)
Boutons "Refuser" ou "Accepter" dans Google Docs
UI pattern: Stepper (Alt text demo)

Donner aux utilisateurs le contrôle et la possibilité de remplacer les paramètres

S'applique à : toutes les API.

À faire : laissez toujours le dernier mot à l'utilisateur. Fournissez un moyen de remplacer manuellement les suggestions. Les API peuvent produire des résultats incorrects.

À éviter : forcer un résultat généré par IA comme seule option.

Mettre en cache les résultats des tâches répétées

S'applique à : toutes les API.

À faire : implémentez un cache de résultats locaux (par exemple, à l'aide de sessionStorage ou IndexedDB) pour les entrées ou requêtes répétées. Normalisez l'entrée en supprimant les espaces et en mettant en minuscules pour augmenter les accès au cache. Pour les entrées volumineuses, par exemple les images, générez un hachage à utiliser comme clé de cache. Définissez une valeur TTL (Time To Live) prudente pour votre cache (ou diffusez les résultats mis en cache tout en les mettant à jour en arrière-plan). Permettez à l'utilisateur de déclencher une nouvelle inférence si le résultat n'est pas satisfaisant.

Ne faites pas : réexécuter la même inférence pour une requête de recherche répétée ou une entrée de données identique, par exemple lorsqu'un utilisateur navigue d'avant en arrière entre les résultats de recherche. Bien que l'inférence sur l'appareil soit sans frais en termes de coûts cloud, elle est coûteuse en termes de temps utilisateur et d'autonomie de la batterie.

// ✅ DO: Check a local cache before running inference
async function getAiResponse(userInput, forceRefresh = false) {
  // Normalize the query to increase cache hits
  const query = userInput.trim().toLowerCase();
  const cacheKey = `ai_results_${query}`;
  const TTL_MS = 3600000; // 1 hour conservative TTL

  if (!forceRefresh) {
    const itemStr = localStorage.getItem(cacheKey);
    if (itemStr) {
      const item = JSON.parse(itemStr);
      const now = Date.now();

      // Check if the item has expired
      if (now < item.expiry) {
        // Lightweight safety check before rendering
        if (isValid(item.value)) return item.value;
      } else {
        // Delete the stale entry if the TTL has passed
        localStorage.removeItem(cacheKey);
      }
    }
  }

  // Fallback: Run inference if no valid cache exists
  const session = await LanguageModel.create();
  const response = await session.prompt(userInput);

  // Store the result for future use (with an expiration)
  const cacheData = {
    value: response,
    expiry: Date.now() + TTL_MS
  };
  localStorage.setItem(cacheKey, JSON.stringify(cacheData));

  return response;
}