¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Instantánea y reversión de una carpeta

Después de esta lección: Puedo devolver una carpeta a como estaba antes de que actuara un agente.

Lección de 65 min7 comandosLaboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

Devolver una carpeta a como estaba

Una instantánea es una copia de todos los archivos de una carpeta, tomada antes de que actúe un agente. Una reversión devuelve la carpeta a esa copia.

Importa porque incluso un agente confinado puede estropear los archivos que sí puede tocar. Con una instantánea puedes decir: devuélvelo todo, exactamente.

Al final de esta lección

Nota

Puedo devolver una carpeta a como estaba antes de que actuara un agente.

Palabras clave

PalabraQué significa
Instantáneauna copia de cada archivo y su texto
Diferenciasla lista de lo que cambió entre dos estados
Restaurardevolver los archivos desde una instantánea
Reversiónuna restauración de toda la carpeta
Archivo añadidoun archivo que no estaba en la instantánea

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Una instantánea guarda cada archivo

Antes de que empiece el agente, lee cada archivo de la carpeta y guarda su ruta y su texto. Guárdalo donde el agente no pueda llegar.

Compara por ruta. Cuando el agente termina, un archivo está añadido si es nuevo, quitado si ya no está y cambiado si su texto es distinto.

Ejemplo resuelto: tres ediciones

Edición del agenteEn las diferenciasPara devolverlo
Escribió src/app.js con texto nuevoCambiadoEscribir el texto antiguo
Borró notas.txtQuitadoEscribir el archivo otra vez
Creó nuevo.txtAñadidoBorrarlo
Escribió un archivo con su texto antiguoNadaNada que hacer

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un error frecuente

Advertencia

Mucha gente cree que restaurar es solo reescribir los archivos cambiados. También hay que borrar los archivos que añadió el agente, o la carpeta no queda como estaba. Después comprueba: tras restaurar, las diferencias deben estar vacías.

Demuestra que la restauración funcionó

Una restauración que no compruebas es una esperanza. Tras restaurar, toma otra instantánea y compárala con la primera. Si no hay diferencias, la carpeta está exactamente como estaba.

Esto compara solo archivos. Una carpeta vacía que creó el agente no aparece en una lista de archivos, así que di qué no cubre tu instantánea.

Escribe el ayudante del modelo, parte 1

Comando
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

Comando
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 guion de reversión, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const fs = require(\"fs\");' 'const path = require(\"path\");' 'const root = path.join(process.cwd(), \"snap-lab\");' 'function put(rel, text) {' '  const f = path.join(root, rel);' '  fs.mkdirSync(path.dirname(f), { recursive: true });' '  fs.writeFileSync(f, text);' '}' 'function names(dir) {' '  let out = [];' '  for (const n of fs.readdirSync(dir).sort()) {' '    const f = path.join(dir, n);' '    out = fs.statSync(f).isDirectory() ? out.concat(names(f)) : out.concat([f]);' '  }' '  return out;' '}' > rollback.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 guion de reversión, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function take() {' '  const s = {};' '  names(root).forEach(function (f) { s[path.relative(root, f)] = fs.readFileSync(f, \"utf8\"); });' '  return s;' '}' 'function diff(a, b) {' '  const d = { added: [], changed: [], removed: [] };' '  Object.keys(b).forEach(function (k) { if (a[k] === undefined) d.added.push(k); else if (a[k] < b[k] || a[k] > b[k]) d.changed.push(k); });' '  Object.keys(a).forEach(function (k) { if (b[k] === undefined) d.removed.push(k); });' '  return d;' '}' 'function restore(snap) {' '  const now = take();' '  Object.keys(now).forEach(function (k) { if (snap[k] === undefined) fs.rmSync(path.join(root, k)); });' '  Object.keys(snap).forEach(function (k) { put(k, snap[k]); });' '}' >> rollback.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 guion de reversión, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'fs.rmSync(root, { recursive: true, force: true });' 'put(\"src/app.js\", \"let x = 1;\");' 'put(\"src/util.js\", \"export {};\");' 'put(\"notas.txt\", \"pendiente\");' >> rollback.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 guion de reversión, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const snap = take();' '  console.log(\"instantánea: \" + Object.keys(snap).join(\" \"));' '  put(\"src/app.js\", \"let x = 2;\");' '  fs.rmSync(path.join(root, \"notas.txt\"));' '  put(\"nuevo.txt\", \"hola\");' '  console.log(\"cambios: \" + JSON.stringify(diff(snap, take())));' '  restore(snap);' '  console.log(\"igual tras restaurar: \" + (JSON.stringify(take()) === JSON.stringify(snap)));' '  const q = \"En una frase corta: ¿por qué conviene tomar una instantánea antes de que un agente edite archivos?\";' '  const sample = \"Si las ediciones salen mal, puedes devolver cada archivo exactamente a como estaba.\";' '  const reply = await ask([{ role: \"user\", content: q }], sample, 60);' '  console.log(\"el modelo dijo: \" + reply.text);' '}' 'main();' >> rollback.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 reversión

Comando
docker exec -w /home/node/project agent node rollback.js

Se 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íneaQué muestra
instantánea: notas.txt src/app.js src/util.jsTres archivos reales en snap-lab
cambios: {"added":["nuevo.txt"],"changed":["src/app.js"],"removed":["notas.txt"]}Lo que hizo el agente de pega
igual tras restaurar: trueLa carpeta vuelve a ser igual a la instantánea
el modelo dijo: ...Una frase. Las palabras cambian en cada ejecución

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Sobre este laboratorio y su código: Laboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.