Cinquante personnes demandent en même temps
Un serveur de modèle est un programme qui garde un modèle en mémoire et répond à beaucoup de monde. Il met les demandes dans une file et les groupe en lots.
C’est important, car une puce qui répond à une personne à la fois fait attendre tous les autres. Le lot permet à un serveur de rester rapide quand la salle est pleine.
À la fin de cette leçon
Remarque
Je peux décrire comment un serveur transforme beaucoup de demandes en lots.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| File | des demandes qui attendent dans l’ordre d’arrivée |
| Lot | des demandes traitées ensemble |
| Attente | le temps entre l’arrivée et le départ |
| Temps avant le premier mot | l’attente plus le temps de faire le premier jeton |
| Débit | le nombre de demandes finies en un temps donné |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une file nourrit la puce
Les demandes entrent dans une file. Quand la puce est libre, le serveur prend les demandes arrivées et les lance ensemble en un lot.
Dans notre modèle, un lot coûte 2 tics pour démarrer, plus le travail de sa demande la plus longue. Ajouter une demande courte à un lot ne coûte presque rien.
Les mêmes cinq demandes, deux tailles de lot
| Taille du lot | Attentes (a, b, c, d, e) | Attente moyenne |
|---|---|---|
| 1 | 0 6 9 13 11 | 7,80 |
| 3 | 0 6 5 3 2 | 3,20 |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Deux vitesses : le premier mot et le débit
Le temps avant le premier mot est l’attente plus le temps de faire le premier jeton. C’est le retard que sent la personne. Le débit est le nombre de demandes finies en un temps donné. C’est ce que paie le propriétaire.
Un lot fait monter le débit. Mais une demande courte dans un lot ne finit qu’avec la plus longue : une demande très longue peut ralentir les autres.
Une erreur courante
Attention
Beaucoup pensent qu’un lot plus grand est toujours meilleur. Un lot plus grand fait monter le débit, mais chaque lot dure autant que sa demande la plus longue. Choisissez la taille en regardant les attentes et le débit.
Écrire l’assistant du modèle, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Les jetons d’une réponse, ou null si elle n’en donne pas.' 'function compter(donnees, nom) {' ' return donnees.usage && typeof donnees.usage[nom] === \"number\" ? donnees.usage[nom] : null;' '}' > 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’assistant du modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' '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 || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"statut \" + res.status);' ' const d = await res.json();' ' return { texte: d.choices[0].message.content, entree: compter(d, \"prompt_tokens\"), sortie: compter(d, \"completion_tokens\"), enregistre: false };' ' } catch (erreur) {' ' console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' ' return { texte: echantillon, entree: null, sortie: null, enregistre: true };' ' }' '}' '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 le simulateur de file, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' 'const demandes = [[\"a\", 0, 5], [\"b\", 1, 2], [\"c\", 2, 4], [\"d\", 4, 3], [\"e\", 11, 2]];' 'function simuler(liste, lotMax) {' ' const sortie = [];' ' let temps = 0;' ' let suivant = 0;' ' while (suivant < liste.length) {' ' if (liste[suivant][1] > temps) temps = liste[suivant][1];' ' const lot = [];' ' while (suivant < liste.length && lot.length < lotMax && liste[suivant][1] <= temps) {' ' lot.push(liste[suivant]);' ' suivant++;' ' }' > servir.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 le simulateur de file, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' const fin = temps + 2 + Math.max(...lot.map(function (r) { return r[2]; }));' ' lot.forEach(function (r) { sortie.push({ id: r[0], attente: temps - r[1], fin: fin }); });' ' temps = fin;' ' }' ' return sortie;' '}' >> servir.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 le simulateur de file, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function bilan(lotMax) {' ' const lignes = simuler(demandes, lotMax);' ' const attentes = lignes.map(function (r) { return r.attente; });' ' const moyenne = attentes.reduce(function (a, b) { return a + b; }, 0) / attentes.length;' ' console.log(\"lot \" + lotMax + \" : attentes \" + attentes.join(\" \") + \", moyenne \" + moyenne.toFixed(2) + \", fini au tic \" + lignes[lignes.length - 1].fin);' '}' >> servir.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 le simulateur de file, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' bilan(1);' ' bilan(3);' ' const q = \"En une phrase courte, pourquoi un serveur regroupe-t-il les demandes en lot ?\";' ' const echantillon = \"Un lot laisse la puce travailler sur plusieurs demandes à la fois : plus de gens sont servis dans le même temps.\";' ' const reponse = await demander([{ role: \"user\", content: q }], echantillon, 60);' ' console.log(\"le modèle dit : \" + reponse.texte);' '}' 'main();' >> servir.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.
Lancer le simulateur de file
docker exec -w /home/node/project agent node servir.jsAffiché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.
Ce que le Run affiche
| Ligne | Ce qu’elle montre |
|---|---|
| lot 1 : attentes 0 6 9 13 11, moyenne 7.80 ... | Une à la fois : de longues attentes |
| lot 3 : attentes 0 6 5 3 2, moyenne 3.20 ... | En groupe : des attentes plus courtes |
| fini au tic 26 et au tic 17 | La file entière se vide plus tôt |
| le modèle dit : ... | Une phrase. Les mots changent à chaque Run |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.