Evita que una función gaste de más
Un presupuesto para una función con modelo convierte los tokens en dinero y rechaza la llamada que pasaría un límite. Importa porque el coste crece con el uso. Un usuario muy activo o un bucle pueden gastar un mes de presupuesto en una tarde.
Un modelo se factura por token y nadie ve la factura hasta fin de mes. Un límite en tu propio código es el único freno que actúa al instante.
Al final de esta lección
Nota
Puedo impedir que una función gaste de más, con un límite por usuario, un límite diario y un mensaje claro.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Token | la unidad que un modelo lee y escribe, y en la que se factura |
| Lista de precios | el coste de un millón de tokens, de entrada y de salida |
| Límite por usuario | lo máximo que una cuenta puede gastar en un día |
| Límite diario | lo máximo que toda la función puede gastar en un día |
| Libro de cuentas | el registro corriente de lo gastado |
| Rechazo | devolver una llamada, con un motivo |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Convierte tokens en dinero
Un modelo tiene dos precios: uno para los tokens de entrada y otro para los de salida, normalmente por millón. Una llamada cuesta los tokens de entrada por el primer precio más los de salida por el segundo.
No uses decimales para el dinero. Cuenta en una unidad pequeña y entera. Aquí, euros por millón de tokens significa que cada token cuesta esos microeuros, así que las cuentas son enteras. Los precios de esta lección son inventados.
Ejemplo resuelto: dos llamadas
| Modelo | Entrada | Salida | Cuenta | Coste |
|---|---|---|---|---|
| small, precios 1 y 4 | 900 | 300 | 900 x 1 + 300 x 4 | 2100 microeuros |
| large, precios 10 y 40 | 200 | 100 | 200 x 10 + 100 x 40 | 6000 microeuros |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Comprueba un límite antes de cobrar
Lleva un libro de cuentas: el total de cada usuario y el total del día. Antes de cobrar una llamada, compruébala. Recházala si lleva al usuario por encima de su límite, o al día por encima del suyo. Un total justo en el límite se permite.
Pregunta primero por el límite del usuario, porque su mensaje es más útil. Una llamada rechazada no se cobra. Y el rechazo dice por qué, con palabras que una persona pueda seguir.
Un error frecuente
Advertencia
Mucha gente cree que un límite solo sirve contra el abuso, y lo pone después del lanzamiento. Los costes también suben por un fallo, como un bucle de reintentos o un prompt largo enviado a todos. Fija los dos límites antes de que llegue el primer usuario.
Escribe el ayudante del modelo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// Los tokens de una respuesta, o null si no trae ninguno.' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el ayudante del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, 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(\"estado \" + res.status);' ' const d = await res.json();' ' return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el presupuesto, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' '// Una lista de precios inventada: euros por millón de tokens. Tokens por precio = microeuros.' 'const PRICE = { small: { in: 1, out: 4 }, large: { in: 10, out: 40 } };' 'const USER_LIMIT = 5000; // microeuros para un usuario en un día' 'const DAY_LIMIT = 8000; // microeuros para todos en un día' 'const spent = { user: new Map(), day: 0 };' 'function cost(model, inTokens, outTokens) {' ' return inTokens * PRICE[model].in + outTokens * PRICE[model].out;' '}' > budget3.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el presupuesto, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function check(user, price) {' ' const mine = spent.user.get(user) || 0;' ' if (mine + price > USER_LIMIT) return \"user\";' ' if (spent.day + price > DAY_LIMIT) return \"day\";' ' return \"ok\";' '}' 'function charge(user, price) {' ' spent.user.set(user, (spent.user.get(user) || 0) + price);' ' spent.day = spent.day + price;' '}' >> budget3.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el presupuesto, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function message(why, user) {' ' if (why === \"user\") return \"Hola \" + user + \", esta llamada te haría pasar tu cupo de hoy. Se renueva a medianoche.\";' ' return \"El asistente ha llegado a su límite diario para todos. Inténtalo mañana.\";' '}' 'async function demo() {' ' const prompt = \"Saluda en cinco palabras.\";' ' const reply = await ask([{ role: \"user\", content: prompt }], \"Hola, ¿en qué puedo ayudarte?\", 20);' ' const inT = reply.inTokens === null ? Math.ceil(prompt.length / 4) : reply.inTokens;' ' const outT = reply.outTokens === null ? Math.ceil(reply.text.length / 4) : reply.outTokens;' ' const how = reply.inTokens === null ? \"estimados\" : \"contados\";' ' console.log(\"Tu llamada: \" + inT + \" de entrada, \" + outT + \" de salida (\" + how + \"), \" + cost(\"small\", inT, outT) + \" microeuros\");' '}' >> budget3.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el presupuesto, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const calls = [' ' { user: \"lucia\", model: \"small\", in: 900, out: 300 },' ' { user: \"mateo\", model: \"small\", in: 1000, out: 500 },' ' { user: \"lucia\", model: \"large\", in: 200, out: 100 },' ' { user: \"carmen\", model: \"large\", in: 300, out: 50 },' ' { user: \"carmen\", model: \"small\", in: 500, out: 100 },' ' { user: \"pablo\", model: \"large\", in: 200, out: 50 },' '];' >> budget3.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el presupuesto, parte 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' await demo();' ' calls.forEach(function (c, at) {' ' const price = cost(c.model, c.in, c.out);' ' const why = check(c.user, price);' ' const label = \"#\" + (at + 1) + \" \" + c.user + \" \" + c.model + \" \" + price + \" microeuros: \";' ' if (why === \"ok\") { charge(c.user, price); console.log(label + \"ok\"); }' ' else console.log(label + message(why, c.user));' ' });' ' console.log(\"Gastado hoy: \" + spent.day + \" microeuros = \" + (spent.day / 1000000).toFixed(6) + \" euros\");' '}' 'main();' >> budget3.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta el presupuesto
docker exec -w /home/node/project agent node budget3.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una salida posible
| Línea | Qué muestra |
|---|---|
| Tu llamada: 42 de entrada, 17 de salida (contados)... | Tu propia llamada. Contada si la respuesta trae uso, estimada si no. Cambia en cada Run |
| #1 lucia small 2100 microeuros: ok | Cobrada |
| #3 lucia large 6000 microeuros: Hola lucia... | Rechazada por el límite del usuario, con un mensaje |
| #4 carmen large 5000 microeuros: El asistente... | Rechazada por el límite diario |
| Gastado hoy: 6000 microeuros = 0.006000 euros | Solo cuentan las llamadas cobradas |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.