ulrich.dev

Fallbacks, timeouts et l'art de se dégrader gracieusement

Livrer l'IA · · 7 min de lecture

Par , Software Engineer

En novembre dernier, le fournisseur LLM que j’utilisais pour une fonctionnalité de résumé automatique est tombé pendant quarante minutes. Quarante minutes, c’est rien, sauf quand votre fonctionnalité n’a pas de mode dégradé. Pendant ces quarante minutes, les utilisateurs voyaient un spinner infini suivi d’une erreur générique. Le support a reçu vingt-trois tickets. Le NPS de la fonctionnalité a chuté de quinze points en une semaine.

La panne du modèle n’était pas le vrai problème. Le vrai problème : personne n’avait décidé à l’avance ce que la fonctionnalité devait faire quand le modèle ne répondait pas.

La dégradation est une décision de conception

La dégradation gracieuse n’est pas un pattern de résilience qu’on ajoute après coup. C’est une décision de conception qu’on prend avant la première ligne de code, en répondant à une question simple : « Que fait cette fonctionnalité quand le modèle est absent ? »

Les réponses possibles forment un spectre :

  1. Disparaître. La fonctionnalité ne s’affiche pas. L’utilisateur ne sait pas qu’elle existe.
  2. Informer. Un message court explique que la fonctionnalité est temporairement indisponible.
  3. Dégrader. Une version moins bonne mais utile prend le relais (résumé tronqué, template statique, cache périmé).
  4. Reporter. L’action est mise en file d’attente et sera traitée quand le service reviendra.

La plupart des équipes choisissent implicitement l’option 0 : planter. Pas par choix, par omission.

Timeout : le curseur qu’on ne règle jamais

Le timeout par défaut de la plupart des SDK HTTP est 30 secondes. Pour un appel LLM qui prend typiquement 2-4 secondes, c’est 10× trop long. Votre utilisateur a déjà quitté la page au bout de 8 secondes.

Ma règle : le timeout d’un appel LLM devrait être 3× le p95 en temps normal. Si votre p95 est 3 secondes, timeout à 9 secondes. Au-delà, le service est probablement en difficulté et vous ne faites que consumer des ressources pour rien.

const TIMEOUT_MS = 9_000; // 3× p95 normal
const FALLBACK_RESPONSE = {
  summary: null,
  source: "cache", // ou "template", ou "unavailable"
};

async function summarize(text: string) {
  try {
    return await withTimeout(llm.complete(prompt(text)), TIMEOUT_MS);
  } catch (e) {
    metrics.increment("summarize.fallback", { reason: e.code });
    return FALLBACK_RESPONSE;
  }
}

Les trois couches de fallback

Couche 1 : le cache chaud

Si vous avez déjà généré un résumé pour ce contenu, servez-le depuis le cache, même s’il date de deux jours. Un résumé légèrement périmé est infiniment meilleur qu’une erreur. Le cache a un coût de stockage négligeable et une valeur de résilience immense.

Couche 2 : le modèle de secours

Si votre modèle principal (disons Claude Sonnet) est indisponible, basculez sur un modèle plus petit ou un autre fournisseur. La qualité sera moindre, mais la fonctionnalité reste debout. La bascule doit être automatique, pas un changement de config qui nécessite un redéploiement.

Couche 3 : la réponse statique

Quand tout le reste échoue, affichez quelque chose d’utile. Un template pré-rempli. Les trois premières phrases du contenu. Un message honnête (« Le résumé automatique est temporairement indisponible, voici le début du document »). L’utilisateur comprend, et vous n’avez pas consommé son budget de patience.

Le kill switch

Toute fonctionnalité dépendant d’un LLM devrait avoir un interrupteur d’arrêt, un feature flag qui la désactive instantanément, sans redéploiement. Pas « au prochain déploiement ». Maintenant.

Les raisons de tirer le kill switch ne sont pas toujours des pannes :

  • Le modèle hallucine sur un sujet sensible.
  • Le coût a explosé à cause d’un bug de boucle.
  • Un concurrent utilise votre fonctionnalité pour extraire vos données.
  • Le modèle a commencé à générer du contenu inapproprié.

Dans tous ces cas, vous voulez que la fonctionnalité disparaisse en secondes, pas en minutes.

Concevoir le mode dégradé dès le début

La question à poser en revue de conception, avant même de discuter du prompt : « Dessine-moi l’écran quand le modèle ne répond pas. » Si la réponse est « on n’y a pas pensé », c’est le premier ticket à ouvrir, pas le dernier.

Comme le note Kleppmann dans Designing Data-Intensive Applications, les systèmes fiables ne sont pas ceux qui ne tombent jamais, ce sont ceux dont les pannes partielles restent partielles au lieu de se propager.

Un modèle tombera. Un modèle sera lent. Un modèle aura tort. Concevez pour ces trois cas, et votre fonctionnalité IA passe de fragile à robuste, sans que le cas nominal ne change d’un pixel.


Si cela vous a parlé, vous aimerez Votre logique de retry vous ment et Ce que coûte réellement une fonctionnalité LLM sur un an. Abonnez-vous ci-dessous pour recevoir le billet de vendredi prochain.

Abonnez-vous pour recevoir l'article de vendredi prochain ci-dessous.

Un e-mail · le vendredi · désabonnement à tout moment