Qué es un presupuesto de latencia
La latencia es el tiempo que espera un usuario una respuesta. Un presupuesto de latencia es lo máximo que permites, repartido entre los pasos de una petición.
El módulo sobre coste, trazas y fiabilidad muestra percentiles y un tiempo máximo. Esta lección va más allá: un presupuesto para la petición entera, y qué cambia el flujo y qué no.
Al final de esta lección
Nota
Puedo fijar un presupuesto de latencia y decir qué cambia el envío en flujo.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Latencia | el tiempo que espera un usuario | |
| Tiempo hasta el primer token | la espera antes de que salgan las primeras palabras | 600 ms |
| Tiempo total | la espera hasta la última palabra | |
| Flujo | enviar las palabras según se hacen, no todas al final | |
| Presupuesto | lo máximo que permites, repartido entre pasos | |
| Milisegundo | una milésima de segundo |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Dos tiempos: primeras palabras y última
Una respuesta tarda un tiempo antes de la primera palabra y un poco más por cada palabra siguiente. El primer tiempo es el tiempo hasta el primer token. El último es el tiempo total.
El tiempo total es el tiempo hasta el primer token más el tiempo de cada token por el número de tokens.
Ejemplo resuelto: dos respuestas
| Respuesta | Hasta el primer token | Por token | Tokens | Total |
|---|---|---|---|---|
| Corta | 600 | 20 | 30 | 1200 |
| Larga | 600 | 20 | 300 | 6600 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente cree que el flujo hace la respuesta más rápida. No es así. La última palabra llega exactamente al mismo tiempo. Lo que cambia es cuándo ve el usuario las primeras palabras, así que la espera parece más corta.
Dónde se nota la espera
Sin flujo, el usuario no ve nada hasta el tiempo total. Con flujo, ve palabras tras el tiempo hasta el primer token y puede empezar a leer.
En una respuesta larga la diferencia es grande. En el ejemplo, la espera por las primeras palabras baja de 6600 a 600 milisegundos, un ahorro de 6000.
Lo que te cuesta el flujo
Importante
Las palabras se muestran antes de revisar la respuesta entera. Una revisión que necesita el texto completo, como un control de seguridad, no puede ir primero. Decide cómo lo vas a resolver antes de activar el flujo.
Repartir el presupuesto entre pasos
Una petición tiene más pasos que el modelo. Espera en una cola, busca documentos y revisa la respuesta. Suma los tiempos en orden.
El primer paso donde el total acumulado se pasa del presupuesto es el que hay que arreglar primero, o quitar.
Ejemplo resuelto: un presupuesto de 1000
| Paso | Tiempo | Total acumulado | ¿Pasa de 1000? |
|---|---|---|---|
| Cola | 150 | 150 | No |
| Búsqueda | 400 | 550 | No |
| Primer token del modelo | 600 | 1150 | Sí, el primero que se pasa |
| Control de seguridad | 200 | 1350 | Sí, 350 de más |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
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 script de latencia, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'function total(r) { return r.ttft + r.perToken * r.tokens; }' 'function first(r) { return r.streamed ? r.ttft : total(r); }' 'const replies = [' ' { name: \"corta\", ttft: 600, perToken: 20, tokens: 30, streamed: false },' ' { name: \"larga\", ttft: 600, perToken: 20, tokens: 300, streamed: false },' ' { name: \"larga-en-flujo\", ttft: 600, perToken: 20, tokens: 300, streamed: true },' '];' > latency.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 script de latencia, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const parts = [[\"cola\", 150], [\"búsqueda\", 400], [\"primer token\", 600], [\"control de seguridad\", 200]];' 'function budget(limit) {' ' let sum = 0;' ' let over = \"ninguna\";' ' for (const p of parts) {' ' sum = sum + p[1];' ' if (sum > limit && over === \"ninguna\") over = p[0];' ' }' ' return \"total \" + sum + \", quedan \" + (limit - sum) + \", primera que se pasa: \" + over;' '}' >> latency.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 script de latencia, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' for (const r of replies) console.log(r.name + \": primero \" + first(r) + \", total \" + total(r));' ' console.log(\"presupuesto 1000: \" + budget(1000));' ' console.log(\"presupuesto 2000: \" + budget(2000));' ' const started = Date.now();' ' const reply = await ask([{ role: \"user\", content: \"En una frase corta, ¿por qué a los usuarios les gusta ver las palabras cuanto antes?\" }], \"Pueden empezar a leer enseguida, así que la espera parece más corta.\", 60);' ' console.log(\"el modelo dijo: \" + reply.text);' ' console.log(\"espera medida de la respuesta entera: \" + (reply.recorded ? \"no medida\" : Date.now() - started + \" ms\"));' '}' 'main();' >> latency.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 las cuentas de latencia
docker exec -w /home/node/project agent node latency.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Lo que imprime el Run
| Línea | Qué muestra |
|---|---|
| corta: primero 1200, total 1200 | Sin flujo: el primer texto es el total |
| larga-en-flujo: primero 600, total 6600 | Con flujo: primeras palabras pronto, mismo total |
| presupuesto 1000: total 1350, quedan -350, primera que se pasa: primer token | El paso que hay que arreglar |
| el modelo dijo: ... | Una frase del laboratorio. Una muestra grabada si ningún modelo responde |
| espera medida de la respuesta entera: ... | Un tiempo real en ms, o no medida para una muestra grabada |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.