Envía cada petición al más barato que sirva
Enrutar modelos es elegir, para cada petición, el modelo más barato que sea suficiente. Importa porque el más caro cuesta muchas veces lo que el más pequeño, y la mayoría de las peticiones no lo necesitan.
Una herramienta de soporte clasifica tickets, responde preguntas cortas y redacta respuestas largas. Clasificar no necesita el modelo que redacta. Mandarlo todo a un modelo grande paga una potencia que casi nadie usa.
Al final de esta lección
Nota
Puedo enrutar cada petición al modelo más barato que sea suficiente y comprobar la elección con casos grabados.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Enrutador | la regla que elige un modelo para una petición |
| Nivel | un grupo de modelos por tamaño y precio |
| Suficiente | acierta con la frecuencia que esa tarea pide |
| Caso grabado | una petición con el nivel que eligió una persona |
| Desacuerdo | un caso donde el enrutador y la persona difieren |
| Envoltorio | el código alrededor de una llamada que añade las reglas |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un enrutador es una regla simple
Empieza con una regla que se pueda leer. Un texto corto o una tarea de clasificar va al nivel small. Un texto largo o una tarea de razonar va al nivel large. El resto va al nivel medium.
Una regla sobre tamaño y tarea es barata, instantánea y fácil de explicar. También puedes pedir a un modelo pequeño que elija, pero eso añade una llamada a cada petición, así que prueba antes la regla.
Ejemplo resuelto: la regla con cuatro peticiones
| Petición | Tarea | Tamaño | Nivel |
|---|---|---|---|
| Clasificar un ticket | classify | 150 caracteres | small |
| Resumir una página | summarise | 1500 caracteres | medium |
| Planear una migración | reason | 900 caracteres | large |
| Resumir un informe largo | summarise | 5000 caracteres | large |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Comprueba la regla con casos
Un enrutador ahorra dinero y puede costar calidad, así que pruébalo. Guarda casos grabados: una petición, su tamaño y su tarea, y el nivel que una persona dice que necesita. Pasa el enrutador por ellos. Lista los desacuerdos y suma el coste.
Un desacuerdo no siempre es un fallo de la regla. Lee cada uno. Luego cambia la regla, o cambia el caso.
Un error frecuente
Advertencia
Mucha gente cree que el modelo más barato es siempre el mejor ahorro. Un modelo barato que responde mal cuesta un reintento, una queja o un usuario perdido. Baja de nivel solo donde los casos grabados muestren que la respuesta sigue siendo suficiente.
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 la comprobación, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' '// Precios inventados: euros por millón de tokens, así que tokens por precio = microeuros.' 'const PRICE = { small: { in: 1, out: 4 }, medium: { in: 3, out: 12 }, large: { in: 10, out: 40 } };' 'function route(request) {' ' if (request.task === \"classify\" || request.chars <= 200) return \"small\";' ' if (request.task === \"reason\" || request.chars > 2000) return \"large\";' ' return \"medium\";' '}' 'function cost(tier, c) { return c.in * PRICE[tier].in + c.out * PRICE[tier].out; }' > route6.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 la comprobación, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Casos grabados: qué es cada petición y el nivel que un revisor dice que necesita.' 'const CASES = [' ' { id: 1, task: \"classify\", chars: 150, in: 60, out: 5, expected: \"small\" },' ' { id: 2, task: \"qa\", chars: 180, in: 70, out: 40, expected: \"small\" },' ' { id: 3, task: \"summarise\", chars: 1500, in: 500, out: 150, expected: \"medium\" },' ' { id: 4, task: \"reason\", chars: 900, in: 400, out: 600, expected: \"large\" },' '];' >> route6.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 la comprobación, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const MORE = [' ' { id: 5, task: \"summarise\", chars: 5000, in: 1400, out: 300, expected: \"large\" },' ' { id: 6, task: \"classify\", chars: 3000, in: 800, out: 5, expected: \"small\" },' ' { id: 7, task: \"qa\", chars: 700, in: 250, out: 120, expected: \"medium\" },' ' { id: 8, task: \"summarise\", chars: 190, in: 80, out: 30, expected: \"medium\" },' '];' >> route6.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 la comprobación, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const all = CASES.concat(MORE);' ' let total = 0, dear = 0, wrong = [];' ' for (const c of all) {' ' const tier = route(c);' ' total = total + cost(tier, c);' ' dear = dear + cost(\"large\", c);' ' if (tier === c.expected) console.log(\"#\" + c.id + \" \" + c.task + \": \" + tier + \" ok\");' ' else { wrong.push(c.id); console.log(\"#\" + c.id + \" \" + c.task + \": \" + tier + \", se esperaba \" + c.expected); }' ' }' ' console.log(\"Desacuerdos: \" + wrong.length + \" de \" + all.length + \", casos \" + wrong.join(\", \"));' ' console.log(\"Coste enrutado: \" + total + \". Todo en large: \" + dear + \". Ahorro: \" + (dear - total));' >> route6.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 la comprobación, parte 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' const reply = await ask([{ role: \"user\", content: \"Clasifica como facturación, error u otro: me cobraron dos veces.\" }], \"facturación\", 10);' ' console.log(\"Enrutado a \" + route(all[0]) + \", respuesta: \" + reply.text.slice(0, 40));' '}' 'main();' >> route6.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 la comprobación
docker exec -w /home/node/project agent node route6.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
El laboratorio tiene un solo modelo
En el laboratorio responde un solo modelo sea cual sea el nivel que elija la regla, y lo elige la plataforma. Así que el Run muestra la elección y el coste, y la única llamada real solo muestra que una petición enrutada recibe respuesta.
Los precios y los ocho casos son inventados y están grabados, así que todos los estudiantes imprimen las mismas líneas.
Una salida posible
| Línea | Qué muestra |
|---|---|
| #1 classify: small ok | El enrutador y la persona coinciden |
| #8 summarise: small, se esperaba medium | Un desacuerdo que hay que leer y decidir |
| Desacuerdos: 1 de 8, casos 8 | El recuento y los números de caso |
| Coste enrutado: 60820. Todo en large: 85600. Ahorro: 24780 | El total, en microeuros |
| Enrutado a small, respuesta: ... | Una llamada real. Las palabras cambian en cada Run |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.