Le streaming des réponses est une décision d'UX, pas de transport
Par Ulrich Dohou, Software Engineer
La première fois qu’un product manager m’a demandé « pourquoi on ne streame pas ? », j’ai répondu par la technique : Server-Sent Events, chunked transfer encoding, gestion des connexions longues. Il a hoché la tête poliment et reformulé : « Non, je veux dire, est-ce que les utilisateurs doivent voir le texte apparaître mot par mot ? »
C’était la bonne question. Le streaming n’est pas un choix d’infrastructure, c’est un choix de produit. Et comme tous les choix de produit, la réponse dépend du contexte d’usage.
Les trois modes d’affichage
Mode 1 : Streaming progressif (typewriter)
Le texte apparaît mot par mot ou phrase par phrase. L’utilisateur voit la réponse se construire en temps réel.
Quand l’utiliser :
- Chat conversationnel, l’utilisateur lit pendant que le modèle écrit.
- Réponses longues (>200 mots) où l’attente serait perçue comme un blocage.
- Cas où le temps perçu compte plus que le temps réel. Les travaux de Jakob Nielsen montrent qu’au-delà d’une seconde de silence, l’utilisateur perd le sentiment de fluidité.
Ce que ça complique :
- Pas de validation globale de la réponse avant affichage. Si le modèle hallucine en milieu de réponse, l’utilisateur l’a déjà lu.
- La gestion d’erreur est visible, une connexion coupée à mi-phrase est pire qu’une erreur propre.
- Le formatage (markdown, tables) ne peut être rendu qu’une fois le bloc complet reçu.
Mode 2 : Attente puis affichage complet
Rien ne s’affiche pendant le traitement. Un indicateur de chargement, puis la réponse complète apparaît d’un coup.
Quand l’utiliser :
- Résultats structurés (tableaux, JSON, formulaires pré-remplis) où la forme partielle n’a pas de sens.
- Actions avec conséquences, l’utilisateur doit voir le résultat final avant d’agir dessus.
- Réponses courtes (<50 mots) où le streaming est visuellement inutile, la différence entre 800 ms d’attente et 800 ms de streaming est imperceptible.
Ce que ça permet :
- Validation complète avant affichage. Vous pouvez parser, filtrer, reformater.
- UX d’erreur propre, soit la réponse arrive correcte, soit elle n’arrive pas.
- Pas de « recul », l’utilisateur ne voit jamais un texte se corriger en direct.
Mode 3 : Hybride (skeleton + remplissage)
Un squelette de réponse apparaît immédiatement (structure, titres, cadre), puis le contenu se remplit progressivement.
Quand l’utiliser :
- Dashboards et rapports, la structure est prévisible, le contenu ne l’est pas.
- Cas multi-step, montrez chaque étape complétée pendant que la suivante tourne.
- Quand vous voulez le feedback de progression du streaming sans l’instabilité visuelle du typewriter.
Le vrai compromis : confiance vs fluidité
Le streaming crée un effet psychologique puissant : l’utilisateur perçoit le système comme rapide et réactif. Mais il crée aussi un problème de confiance. Quand le texte apparaît mot par mot, l’utilisateur lit au fur et à mesure et forme son jugement avant que la réponse soit complète. Si les premiers mots sont bons mais la conclusion hallucine, le dégât est fait.
En mode d’attente, vous pouvez :
- Vérifier la cohérence de la réponse.
- Comparer contre une allow-list.
- Filtrer les contenus problématiques.
- Décider de ne pas afficher du tout.
C’est pourquoi les fonctionnalités à enjeux élevés (rédaction de contrats, réponses médicales, actions financières) ne devraient jamais streamer la réponse brute. L’attente de 3 secondes est le prix de la vérification.
Implémentation : ce qui change vraiment
Techniquement, le streaming est simple, les API modernes le supportent nativement via SSE. Ce qui est difficile, c’est la gestion des états intermédiaires côté frontend :
- Que montrer pendant les pauses du modèle (qui peuvent durer 2-3 secondes) ?
- Comment gérer un markdown partiel (un
**ouvert sans fermeture) ? - Comment afficher une erreur proprement après 200 mots déjà affichés ?
- Comment permettre à l’utilisateur d’annuler mi-parcours ?
Chacun de ces cas nécessite des décisions UX, pas des décisions d’infra.
Ma règle de décision
Avant d’implémenter le streaming pour une fonctionnalité, je pose trois questions :
- L’utilisateur va-t-il lire pendant la génération ? Si oui → streaming. Si la réponse est un résultat à utiliser d’un coup → attente.
- Pouvez-vous vous permettre d’afficher une réponse non validée ? Si oui → streaming. Si non → attente + validation.
- La réponse dépasse-t-elle 100 mots ? Si non → attente (la différence est imperceptible). Si oui → streaming probable.
Le streaming n’est pas mieux ou pire. C’est une décision de produit, comme le choix entre un formulaire en une page et un formulaire multi-étapes. La technique suit, elle ne décide pas.
Si cela vous a parlé, vous aimerez Fallbacks, timeouts et l’art de se dégrader gracieusement et La mise à jour de modèle qui a discrètement cassé la production. 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