Hacer fallar un control por una mala instrucción
Un equipo cambia una instrucción el viernes. El lunes, los clientes ven respuestas peores. Nadie miró la nota, porque nada les obligó a mirarla.
Una prueba de regresión dentro del control automático lo evita. El módulo sobre evaluación y pruebas compara dos ejecuciones a mano. Esta lección hace que la comparación detenga un cambio sola.
Al final de esta lección
Nota
Puedo hacer que una instrucción peor falle un control automático.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Control automático | las comprobaciones que se ejecutan solas con cada cambio | |
| Conjunto congelado | un conjunto de evaluación que no cambia entre ejecuciones | |
| Umbral | la tasa de aciertos más baja que puede tener un cambio | 80 |
| Caso crítico | un caso que no puede pasar nunca de aprobar a fallar | |
| Código de salida | el número con que acaba un programa; cero significa que va bien |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un conjunto congelado y un umbral
El control ejecuta tu función sobre el mismo conjunto congelado cada vez. Si el conjunto cambiara con la instrucción, no podrías comparar dos ejecuciones.
Después eliges un umbral, por ejemplo el 80 por ciento. Un cambio que saca menos que el umbral hace fallar el control.
Los casos que cambiaron importan más
Una tasa esconde el movimiento. Un caso puede romperse mientras otro se arregla, y la tasa queda igual.
Por eso el control también lista los casos que cambiaron. Un caso roto aprobaba antes y falla ahora. Un caso arreglado hace lo contrario.
Ejemplo resuelto: doce casos, dos ejecuciones
| Medida | Ejecución vieja | Ejecución nueva |
|---|---|---|
| Casos aprobados | 10 de 12 | 9 de 12 |
| Tasa de aciertos, porcentaje entero | 83 | 75 |
| Casos rotos | - | 4 y 7 |
| Casos arreglados | - | 6 |
| Caso crítico roto | - | Sí, el caso 4 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente cree que una tasa por encima del umbral significa que el cambio es seguro. Un caso crítico puede romperse mientras la tasa sigue alta. Mira primero los casos críticos y después la tasa.
Un código de salida dirige el control
Un programa acaba con un número. Cero significa que va bien y cualquier otro número significa un problema. El control se detiene cuando ve un número que no es cero.
Usamos 2 para un caso crítico roto, 1 para una tasa bajo el umbral y 0 para un aprobado. Lo crítico se mira primero.
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 puerta del control, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const mark = 80;' 'const rows = [' ' \"1 c 1 1\", \"2 n 1 1\", \"3 n 1 1\", \"4 c 1 0\",' ' \"5 n 1 1\", \"6 n 0 1\", \"7 n 1 0\", \"8 n 1 1\",' ' \"9 c 1 1\", \"10 n 0 0\", \"11 n 1 1\", \"12 n 1 1\",' '].map(function (r) {' ' const p = r.split(\" \");' ' return { id: Number(p[0]), critical: p[1] === \"c\", old: p[2] === \"1\", now: p[3] === \"1\" };' '});' > gate.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 puerta del control, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function rate(key) {' ' const n = rows.filter(function (r) { return r[key]; }).length;' ' return Math.floor(n * 100 / rows.length);' '}' 'const broken = rows.filter(function (r) { return r.old && r.now === false; });' 'const fixed = rows.filter(function (r) { return r.old === false && r.now; });' 'function ids(list) {' ' return list.length === 0 ? \"ninguno\" : list.map(function (r) { return r.id; }).join(\", \");' '}' >> gate.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 puerta del control, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function exitCode() {' ' if (broken.some(function (r) { return r.critical; })) return 2;' ' if (rate(\"now\") < mark) return 1;' ' return 0;' '}' 'async function main() {' ' console.log(\"tasa de aciertos: \" + rate(\"old\") + \" -> \" + rate(\"now\") + \" (umbral \" + mark + \")\");' ' console.log(\"rotos: \" + ids(broken) + \" arreglados: \" + ids(fixed));' ' console.log(\"código de salida: \" + exitCode());' ' const q = \"Escribe una frase tranquila para una revisión de cambios: el caso 4 aprobaba antes y ahora falla.\";' ' const sample = \"El caso 4 aprobaba con la instrucción anterior y falla con esta, así que se detiene el control.\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 60);' ' console.log(\"comentario: \" + reply.text);' '}' 'main();' >> gate.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 puerta del control
docker exec -w /home/node/project agent node gate.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 la puerta
| Línea | Qué muestra |
|---|---|
| tasa de aciertos: 83 -> 75 (umbral 80) | La tasa vieja y la nueva, y el umbral |
| rotos: 4, 7 arreglados: 6 | Los casos que cambiaron |
| código de salida: 2 | El caso 4 es crítico, así que el control se detiene |
| comentario: ... | Una frase del laboratorio. Una muestra grabada si ningún modelo responde |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.