Cómo revisar los cambios de un agente
Un conjunto de cambios son todos los archivos que un agente de código añadió, cambió o borró en una tarea. Un agente puede cambiar veinte archivos en un minuto, y leerlos todos con el mismo cuidado no escala.
Una revisión por riesgo encuentra los archivos peligrosos y los lee primero. Los archivos seguros también se leen, pero después de los que podrían esconder un problema.
Al final de esta lección
Nota
Puedo ordenar por riesgo el conjunto de cambios de un agente de código antes de leerlo.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Conjunto de cambios | todos los archivos cambiados en una tarea |
| Desbordar el alcance | cambiar más de lo que pedía la tarea |
| Dependencia | un paquete que el código necesita para funcionar |
| Marca de riesgo | una señal de que un cambio pide cuidado |
| Peso | cuántos puntos vale una marca |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Cinco señales de que un cambio pide cuidado
Un archivo que la tarea no nombraba es desbordar el alcance. Un test borrado, o un test con menos líneas, puede esconder una función rota. Un archivo de configuración cambiado puede cambiar cómo se construye todo.
Un archivo nuevo muy grande cuesta leerlo. Una dependencia nueva trae código que nadie del equipo escribió. Cada una de estas cosas es una marca de riesgo.
Ejemplo resuelto: marcas y sus pesos
| Marca | Cuándo aparece | Peso |
|---|---|---|
| test borrado | Se borra un archivo de tests | 5 |
| test reducido | Un archivo de tests pierde más líneas de las que gana | 3 |
| fuera de alcance | La tarea no nombraba el archivo | 3 |
| configuración | package.json, package-lock.json o ci.yml | 2 |
| grande | Más de 200 líneas añadidas y quitadas | 1 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Puntúa, ordena y luego lee
Suma los pesos de las marcas de cada archivo para darle una puntuación. Ordena los archivos con la puntuación más alta primero, y deja los empates en el orden de llegada.
Después lee enteros los primeros archivos, línea a línea. Los de abajo también se leen, pero ya no esconden los peligrosos. Un test borrado devuelve todo el conjunto.
Un error frecuente
Advertencia
Mucha gente cree que revisar es leer cada línea con el mismo cuidado. Con 400 líneas, las diez peligrosas se esconden entre las seguras. Ordena primero por riesgo y luego lee enteros los archivos peligrosos.
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 revisión por riesgo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '// La tarea nombraba dos archivos. El agente devolvió siete.' 'const allowed = [\"shop.js\", \"shop.test.js\"];' 'const CONFIG = [\"package.json\", \"package-lock.json\", \"ci.yml\"];' 'const WEIGHT = { \"test borrado\": 5, \"test reducido\": 3, \"fuera de alcance\": 3, \"configuración\": 2, \"grande\": 1 };' > risk.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 revisión por riesgo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const changes = [' ' { path: \"shop.js\", added: 12, removed: 2, kind: \"modified\" },' ' { path: \"shop.test.js\", added: 1, removed: 9, kind: \"modified\" },' ' { path: \"prices.json\", added: 1, removed: 1, kind: \"modified\" },' ' { path: \"old.test.js\", added: 0, removed: 40, kind: \"deleted\" },' ' { path: \"package.json\", added: 1, removed: 0, kind: \"modified\" },' ' { path: \"README.md\", added: 3, removed: 0, kind: \"modified\" },' ' { path: \"utils/format.js\", added: 230, removed: 0, kind: \"added\" },' '];' 'const before = [\"date-tools\"];' 'const after = [\"date-tools\", \"money-format\"];' >> risk.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 revisión por riesgo, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function flags(c) {' ' const out = [];' ' const isTest = c.path.indexOf(\".test.\") >= 0;' ' if (isTest && c.kind === \"deleted\") out.push(\"test borrado\");' ' if (isTest && c.kind === \"modified\" && c.removed > c.added) out.push(\"test reducido\");' ' if (allowed.indexOf(c.path) < 0) out.push(\"fuera de alcance\");' ' if (CONFIG.indexOf(c.path) >= 0) out.push(\"configuración\");' ' if (c.added + c.removed > 200) out.push(\"grande\");' ' return out;' '}' 'function score(c) {' ' return flags(c).reduce(function (sum, f) { return sum + WEIGHT[f]; }, 0);' '}' >> risk.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 revisión por riesgo, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const order = changes.slice().sort(function (a, b) { return score(b) - score(a); });' ' order.forEach(function (c) {' ' console.log(score(c) + \" \" + c.path + (flags(c).length ? \": \" + flags(c).join(\", \") : \"\"));' ' });' ' after.filter(function (d) { return before.indexOf(d) < 0; })' ' .forEach(function (d) { console.log(\"dependencia nueva: \" + d); });' >> risk.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 revisión por riesgo, parte 5
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' const gone = changes.some(function (c) { return flags(c).indexOf(\"test borrado\") >= 0; });' ' console.log(gone ? \"devolver: se borró un test\" : \"revisar en este orden\");' ' const q = \"En una frase corta: ¿por qué quien revisa debe mirar primero un archivo de tests borrado?\";' ' const sample = \"Un test borrado puede esconder una función rota, porque ya nada la comprueba.\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 60);' ' console.log(\"el modelo dijo: \" + reply.text);' '}' 'main();' >> risk.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 revisión por riesgo
docker exec -w /home/node/project agent node risk.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 | Lo que muestra |
|---|---|
| 8 old.test.js: test borrado, fuera de alcance | El archivo más peligroso, se lee primero |
| 5 package.json: fuera de alcance, configuración | Un archivo de configuración que la tarea no nombraba |
| 4 utils/format.js: fuera de alcance, grande | Un archivo nuevo y grande que nadie pidió |
| 3 shop.test.js: test reducido | El test nombrado perdió líneas |
| 3 prices.json: fuera de alcance | Un archivo que la tarea no nombraba |
| 3 README.md: fuera de alcance | Otro archivo que la tarea no nombraba |
| 0 shop.js | El único archivo del que trataba la tarea |
| dependencia nueva: money-format | Código nuevo de fuera del equipo |
| devolver: se borró un test | La decisión para todo el conjunto |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.