Empêcher une fonction de trop dépenser
Un budget pour une fonction à modèle transforme les jetons en argent et refuse un appel qui dépasserait une limite. C’est utile parce que le coût croît avec l’usage. Un utilisateur très actif ou une boucle peut dépenser un mois de budget en un après-midi.
Un modèle est facturé au jeton, et personne ne voit la facture avant la fin du mois. Une limite dans votre propre code est le seul frein qui agit tout de suite.
À la fin de cette leçon
Remarque
Je peux empêcher une fonction de trop dépenser, avec une limite par utilisateur, une limite par jour et un message clair.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Jeton | l’unité que lit et écrit un modèle, et qui sert à facturer |
| Grille de prix | le coût d’un million de jetons, en entrée et en sortie |
| Limite par utilisateur | le maximum qu’un compte peut dépenser en un jour |
| Limite du jour | le maximum que toute la fonction peut dépenser en un jour |
| Registre | le relevé courant de ce qui a été dépensé |
| Refus | renvoyer un appel, avec une raison |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Transformer des jetons en argent
Un modèle a deux prix : l’un pour les jetons en entrée, l’autre pour ceux en sortie, en général par million. Un appel coûte les jetons d’entrée fois le premier prix, plus les jetons de sortie fois le second.
N’utilisez jamais de virgule flottante pour l’argent. Comptez dans une petite unité entière. Ici, des euros par million de jetons veulent dire que chaque jeton coûte autant de micro-euros, donc les calculs restent entiers. Les prix de cette leçon sont inventés.
Exemple détaillé : deux appels
| Modèle | Entrée | Sortie | Calcul | Coût |
|---|---|---|---|---|
| petit, prix 1 et 4 | 900 | 300 | 900 x 1 + 300 x 4 | 2100 micro-euros |
| grand, prix 10 et 40 | 200 | 100 | 200 x 10 + 100 x 40 | 6000 micro-euros |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Vérifier une limite avant de facturer
Tenez un registre : le total de chaque utilisateur et le total du jour. Avant de facturer un appel, vérifiez-le. Refusez-le s’il fait dépasser sa limite à l’utilisateur, ou sa limite au jour. Un total exactement égal à une limite est permis.
Examinez la limite par utilisateur en premier, car son message est le plus utile. Un appel refusé n’est pas facturé. Et le refus dit pourquoi, avec des mots qu’une personne peut suivre.
Une erreur courante
Attention
Beaucoup pensent qu’une limite ne sert qu’aux abus, alors ils la fixent après le lancement. Les coûts grimpent aussi à cause d’un défaut, comme une boucle d’essais ou un long prompt envoyé à tous. Fixez les deux limites avant l’arrivée du premier utilisateur.
É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 budget, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' '' '// Une grille de prix inventée : des euros par million de jetons. Jetons fois prix = micro-euros.' 'const PRIX = { petit: { entree: 1, sortie: 4 }, grand: { entree: 10, sortie: 40 } };' 'const LIMITE_UTILISATEUR = 5000; // micro-euros pour un utilisateur et un jour' 'const LIMITE_JOUR = 8000; // micro-euros pour tout le monde et un jour' 'const depense = { utilisateur: new Map(), jour: 0 };' 'function cout(modele, entree, sortie) {' ' return entree * PRIX[modele].entree + sortie * PRIX[modele].sortie;' '}' > budget3.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 budget, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function verifier(utilisateur, prix) {' ' const deja = depense.utilisateur.get(utilisateur) || 0;' ' if (deja + prix > LIMITE_UTILISATEUR) return \"utilisateur\";' ' if (depense.jour + prix > LIMITE_JOUR) return \"jour\";' ' return \"ok\";' '}' 'function facturer(utilisateur, prix) {' ' depense.utilisateur.set(utilisateur, (depense.utilisateur.get(utilisateur) || 0) + prix);' ' depense.jour = depense.jour + prix;' '}' >> budget3.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 budget, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function message(pourquoi, utilisateur) {' ' if (pourquoi === \"utilisateur\") return \"Bonjour \" + utilisateur + \", cet appel vous ferait dépasser votre quota du jour. Il se renouvelle à minuit.\";' ' return \"Cet appel ferait dépasser la limite du jour de l’assistant pour tout le monde. Merci de réessayer demain.\";' '}' 'async function essai() {' ' const consigne = \"Dis bonjour en cinq mots.\";' ' const r = await demander([{ role: \"user\", content: consigne }], \"Bonjour, que puis-je faire ?\", 20);' ' const e = r.entree === null ? Math.ceil(consigne.length / 4) : r.entree;' ' const s = r.sortie === null ? Math.ceil(r.texte.length / 4) : r.sortie;' ' const mode = r.entree === null ? \"estimés\" : \"comptés\";' ' console.log(\"Votre appel : \" + e + \" en entrée, \" + s + \" en sortie (\" + mode + \"), \" + cout(\"petit\", e, s) + \" micro-euros\");' '}' >> budget3.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 budget, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const appels = [' ' { utilisateur: \"lea\", modele: \"petit\", entree: 900, sortie: 300 },' ' { utilisateur: \"hugo\", modele: \"petit\", entree: 1000, sortie: 500 },' ' { utilisateur: \"lea\", modele: \"grand\", entree: 200, sortie: 100 },' ' { utilisateur: \"ines\", modele: \"grand\", entree: 300, sortie: 50 },' ' { utilisateur: \"ines\", modele: \"petit\", entree: 500, sortie: 100 },' ' { utilisateur: \"tom\", modele: \"grand\", entree: 200, sortie: 50 },' '];' >> budget3.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 budget, partie 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' await essai();' ' appels.forEach(function (a, rang) {' ' const prix = cout(a.modele, a.entree, a.sortie);' ' const pourquoi = verifier(a.utilisateur, prix);' ' const debut = \"#\" + (rang + 1) + \" \" + a.utilisateur + \" \" + a.modele + \" \" + prix + \" micro-euros : \";' ' if (pourquoi === \"ok\") { facturer(a.utilisateur, prix); console.log(debut + \"ok\"); }' ' else console.log(debut + message(pourquoi, a.utilisateur));' ' });' ' console.log(\"Dépensé aujourd’hui : \" + depense.jour + \" micro-euros = \" + (depense.jour / 1000000).toFixed(6).replace(\".\", \",\") + \" euros\");' '}' 'main();' >> budget3.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 budget
docker exec -w /home/node/project agent node budget3.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.
Une sortie possible
| Ligne | Ce qu’elle montre |
|---|---|
| Votre appel : 42 en entrée, 17 en sortie (comptés)... | Votre propre appel. Compté si la réponse donne l’usage, estimé sinon. Il change à chaque Run |
| #1 lea petit 2100 micro-euros : ok | Facturé |
| #3 lea grand 6000 micro-euros : Bonjour lea, cet appel vous ferait dépasser... | Refusé par la limite par utilisateur, avec un message |
| #4 ines grand 5000 micro-euros : Cet appel ferait dépasser la limite du jour... | Refusé par la limite du jour |
| Dépensé aujourd’hui : 6000 micro-euros = 0,006000 euros | Seuls les appels facturés comptent |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.