Plus facile à lire avec un fond crème, une teinte, plus de contraste ou un texte plus grand ? Choisissez vos couleurs et votre texte, une fois pour toutes les pages.

Leçon gratuite

Afficher la réponse d’un chatbot en flux

Après cette leçon: Je peux afficher dans une page web une réponse envoyée en flux, au fur et à mesure.

Leçon de 60 min9 commandesLab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Montrer la réponse au fur et à mesure

Une page de chat en flux affiche la réponse d’un chatbot quelques mots à la fois. Le serveur envoie chaque mot au fur et à mesure. C’est utile, car une longue réponse peut prendre plusieurs secondes, et une page vide fait croire que le site est en panne.

Le serveur envoie de petits morceaux. La page ajoute chaque morceau à l’écran. Le terminal du labo n’a pas de navigateur : ici vous construisez la page et le flux sous forme de texte. Sur votre propre machine, vous l’ouvririez dans un navigateur.

À la fin de cette leçon

Remarque

Je peux afficher dans une page web une réponse envoyée en flux, au fur et à mesure.

Mots-clés

MotCe que ça veut dire
Fluxune réponse envoyée en petits morceaux
Événements serveurun moyen pour un serveur de pousser des lignes vers une page
Fonction de traitementla fonction qui répond à une requête
Échapperchanger des caractères pour qu’ils s’affichent comme du texte
Morceauune petite partie d’une réponse en flux

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Un serveur peut envoyer une réponse en morceaux

Avec des événements serveur, le serveur garde la connexion ouverte et écrit des lignes. Chaque événement commence par data: , contient un morceau en JSON et se termine par une ligne vide. Le type de la réponse est text/event-stream.

Un dernier événement, nommé fin (il a une ligne event: fin et une ligne data: fin), dit à la page que la réponse est terminée.

Exemple travaillé : les lignes d’un flux

Ligne envoyéeCe que fait la page
data: {"morceau": "Bonjour"} et une ligne videAjoute Bonjour à l’écran
data: {"morceau": " et bienvenue"} et une ligne videAjoute les mots suivants après
event: fin, data: fin et une ligne videFerme la connexion

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

La page ajoute et peut s’arrêter

La page contient un petit script. Il ouvre le flux et, pour chaque événement, ajoute le morceau à un paragraphe. Un bouton d’arrêt ferme le flux : plus aucun morceau n’arrive.

Ajouter un morceau avec append et une chaîne ajoute du texte simple. Cela n’ajoute jamais de balisage.

Échappez le texte du modèle

Un modèle peut répéter ce qu’on lui a donné, y compris <script>. Si ce texte entre dans une page comme du HTML, le navigateur peut l’exécuter.

Échapper transforme < en &lt; et les autres caractères spéciaux en codes sûrs. Échappez tout texte de modèle qui va dans du HTML. Faites & en premier, sinon vous échappez vos propres codes.

Une erreur fréquente

Attention

Beaucoup pensent que le texte du modèle est sûr parce qu’ils ont écrit le message. Ce n’est pas vrai. Le modèle peut répéter les mots d’un utilisateur ou d’une source. Traitez chaque réponse comme non fiable : ajoutez-la comme texte, ou échappez-la d’abord.

Écrire l’aide du modèle, partie 1

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function demander(messages, echantillon, max) {' '  try {' '    const res = await fetch(base + \"/v1/chat/completions\", {' '      method: \"POST\",' '      headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' '      body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"statut \" + res.status);' '    return (await res.json()).choices[0].message.content;' > model.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire l’aide du modèle, partie 2

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  } catch (erreur) {' '    console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' '    const liste = [].concat(echantillon);' '    const tour = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' '    return liste[Math.min(tour, liste.length - 1)];' '  }' '}' 'module.exports = { demander };' >> model.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la page et le flux, partie 1

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' '' 'function citer(texte) { return JSON.stringify(texte); }' 'function echapperHtml(texte) {' '  return texte.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\")' '    .replace(/\"/g, \"&quot;\").replace(/\x27/g, \"&#39;\");' '}' > interface4.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la page et le flux, partie 2

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function pageHtml() {' '  const script = [' '    \"const sortie = document.getElementById(\" + citer(\"reponse\") + \");\",' '    \"const flux = new EventSource(\" + citer(\"/flux\") + \");\",' '    \"flux.onmessage = function (e) { sortie.append(JSON.parse(e.data).morceau); };\",' '    \"flux.addEventListener(\" + citer(\"fin\") + \", function () { flux.close(); });\",' '    \"document.getElementById(\" + citer(\"arret\") + \").onclick = function () { flux.close(); };\",' '  ].join(\"\n\");' '  return \"<html><body><p id=reponse></p><button id=arret>Arrêter</button><script>\" + script + \"</script></body></html>\";' '}' >> interface4.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la page et le flux, partie 3

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function ligneSse(morceau) { return \"data: \" + JSON.stringify({ morceau: morceau }) + \"\n\n\"; }' 'function corpsFlux(morceaux, arretApres) {' '  let corps = \"\";' '  morceaux.slice(0, arretApres).forEach(function (morceau) { corps = corps + ligneSse(morceau); });' '  return arretApres >= morceaux.length ? corps + \"event: fin\ndata: fin\n\n\" : corps;' '}' >> interface4.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la page et le flux, partie 4

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function traiter(requete, morceaux) {' '  if (requete.url === \"/\") return { statut: 200, type: \"text/html\", corps: pageHtml() };' '  if (requete.url === \"/flux\") {' '    return { statut: 200, type: \"text/event-stream\", corps: corpsFlux(morceaux, requete.arretApres || 99) };' '  }' '  return { statut: 404, type: \"text/plain\", corps: \"Introuvable\" };' '}' >> interface4.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la page et le flux, partie 5

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function montrer(morceaux) {' '  const accueil = traiter({ url: \"/\" }, morceaux);' '  console.log(\"GET / ->\", accueil.statut, accueil.type, accueil.corps.length + \" caractères\");' '  const direct = traiter({ url: \"/flux\" }, morceaux);' '  console.log(\"GET /flux ->\", direct.statut, direct.type);' '  console.log(direct.corps.split(\"\n\n\").join(\" | \"));' '  const arrete = traiter({ url: \"/flux\", arretApres: 2 }, morceaux);' '  console.log(\"Arrêt après 2 morceaux :\", arrete.corps.split(\"\n\n\").length - 1, \"morceaux envoyés, pas de ligne fin\");' '  console.log(\"Échappé :\", echapperHtml(\"<b>salut</b> & merci\"));' '}' >> interface4.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Écrire la page et le flux, partie 6

Commande
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  // Échantillon enregistré, utilisé seulement si aucun modèle ne répond.' '  const echantillon = \"Bonjour et bienvenue à bord, où souhaitez-vous aller ?\";' '  const reponse = await demander([{ role: \"user\", content: \"Saluez un voyageur de bus en une phrase courte.\" }], echantillon, 40);' '  montrer(reponse.split(\" \").map(function (mot, rang) { return rang === 0 ? mot : \" \" + mot; }));' '}' 'main();' >> interface4.js"

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Appeler la fonction de traitement

Commande
docker exec -w /home/node/project agent node interface4.js

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Une sortie possible

LigneCe qu’elle peut montrer
GET /200 text/html et la taille de la page
GET /flux200 text/event-stream, puis les morceaux d’une réponse séparés par |
Arrêt2 morceaux envoyés, pas de ligne fin
Échappé&lt;b&gt;salut&lt;/b&gt; &amp; merci, toujours pareil

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

À propos de ce labo et de sa source: Lab agent de code IA : utiliser opencode pour expliquer et corriger du code, et vérifier son travail

Vous enseignez à une classe ? Proposez ce module à vos étudiants avec un guide de l'enseignant, des devoirs corrigés et un laboratoire pour chaque étudiant.