Haz reversible cada cambio
Deshacer, para un agente, es registrar lo que sustituyó cada escritura, para devolver los archivos a cualquier paso anterior. Los agentes se equivocan en el paso siete y el fallo solo aparece en el nueve.
Con un registro de cambios rebobinas al paso seis. Sin él, lees un diff y adivinas.
Al final de esta lección
Nota
Puedo registrar cada cambio de una ejecución, para rebobinar los archivos a cualquier paso.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Instantánea | una copia de un archivo antes de que cambie |
| Registro de cambios | la lista de todos los cambios, en orden |
| Paso | una acción numerada de una ejecución |
| Deshacer | devolver un archivo a como estaba |
| Rebobinar | deshacer todos los cambios posteriores a un paso elegido |
| Texto previo | lo que tenía un archivo antes de una escritura |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Deshacer no es reanudar
Un punto de control, como el que se ve al construir agentes, guarda una ejecución para reanudarla tras una parada. Eso avanza. Deshacer lleva los archivos hacia atrás, a como estaban.
Guardan registros distintos. Un punto de control guarda el estado del agente. Un registro de cambios guarda lo que sustituyó cada escritura.
Anota el texto previo
Antes de cada escritura o borrado, anota el número de paso, la ruta y lo que contenía el archivo, su instantánea. Si no existía, anótalo también, como nada.
Para deshacer hasta el paso n, recorre el registro desde el final hasta el paso n + 1. Devuelve cada texto previo. Si el previo era nada, quita el archivo. La última primero, o dos ediciones de un archivo se deshacen en el orden equivocado.
Ejemplo resuelto: cuatro pasos
| Paso | Acción | Texto previo |
|---|---|---|
| 1 | Escribir app.py | x = 1 |
| 2 | Escribir nuevo.py | nada: no existía |
| 3 | Borrar notas.txt | pendiente |
| 4 | Escribir app.py | x = 2 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente deshace solo con git. Git solo vuelve a un commit ya hecho. Si el agente cambió un archivo dos veces desde el último commit, git no guarda el estado de en medio. Un archivo nuevo sin seguir ni lo ve. Lleva tu propio registro de cada escritura.
Escribe el registro de cambios, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const files = { \"app.py\": \"x = 1\", \"notas.txt\": \"pendiente\" };' 'const ops = [' ' { op: \"write\", path: \"app.py\", text: \"x = 2\" },' ' { op: \"write\", path: \"nuevo.py\", text: \"y = 1\" },' ' { op: \"delete\", path: \"notas.txt\" },' ' { op: \"write\", path: \"app.py\", text: \"x = 3\" },' '];' 'function copy(f) { return Object.assign({}, f); }' > undo5.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 registro de cambios, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Antes de cada op, anota lo que tenía el archivo: su texto, o null si no existía.' 'function applyRun(start, list) {' ' const now = copy(start);' ' const log = [];' ' list.forEach(function (o, i) {' ' log.push({ step: i + 1, path: o.path, before: o.path in now ? now[o.path] : null });' ' if (o.op === \"write\") now[o.path] = o.text;' ' else delete now[o.path];' ' });' ' return { files: now, log: log };' '}' >> undo5.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 registro de cambios, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Deshace las entradas posteriores a un paso, la última primero.' 'function undoTo(now, log, step) {' ' const back = copy(now);' ' for (const e of log.slice().reverse()) {' ' if (e.step <= step) continue;' ' if (e.before === null) delete back[e.path];' ' else back[e.path] = e.before;' ' }' ' return back;' '}' >> undo5.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 registro de cambios, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function show(label, f) { console.log(label + \" \" + JSON.stringify(f)); }' 'const run = applyRun(files, ops);' 'show(\"inicio\", files);' 'show(\"tras 4 pasos\", run.files);' 'show(\"deshacer hasta 3\", undoTo(run.files, run.log, 3));' 'show(\"deshacer hasta 1\", undoTo(run.files, run.log, 1));' 'show(\"deshacer hasta 0\", undoTo(run.files, run.log, 0));' 'console.log(\"registro: \" + run.log.map(function (e) { return e.step + \":\" + e.path; }).join(\" \"));' >> undo5.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 y rebobina
docker exec -w /home/node/project agent node undo5.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Aquí los archivos son un diccionario
Los archivos viven en un diccionario del script, así que no cambia nada en el disco. Un arnés real escribe archivos reales y guarda el registro en un archivo o un almacén propio.
El Run no llama a ningún modelo, así que no necesita ayudante y no tiene nada que sustituir.
Salida del Run
| Línea | Qué muestra |
|---|---|
| inicio {"app.py":"x = 1","notas.txt":"pendiente"} | Los archivos antes de la ejecución |
| tras 4 pasos {"app.py":"x = 3","nuevo.py":"y = 1"} | La ejecución escribió, añadió y borró |
| deshacer hasta 3 {"app.py":"x = 2","nuevo.py":"y = 1"} | Solo se deshizo el paso 4 |
| deshacer hasta 1 {"app.py":"x = 2","notas.txt":"pendiente"} | Se deshicieron los pasos 2 a 4 |
| deshacer hasta 0 ... | Los archivos como al empezar |
| registro: 1:app.py 2:nuevo.py 3:notas.txt 4:app.py | Una entrada por paso |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.