¿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

Modos de permiso para agentes de código

Después de esta lección: Puedo ejecutar el mismo agente en modos ask, plan y auto, y decir qué deja pasar cada uno y qué pregunta siempre.

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

Un agente, tres niveles de confianza

Un modo de permiso decide lo que un agente puede hacer sin preguntar. En modo plan solo lee. En modo ask pregunta antes de cambiar nada. En modo auto trabaja solo dentro de unos límites.

El mismo agente es seguro de dejar ejecutándose en un modo y peligroso en otro. Por eso el modo se elige en cada ejecución.

Al final de esta lección

Nota

Puedo ejecutar el mismo agente en modos ask, plan y auto, y decir qué deja pasar cada uno y qué pregunta siempre.

Palabras clave

PalabraQué significa
Modoun ajuste que decide qué se ejecuta sin preguntar
Modo planun modo que solo lee
Riesgocuánto daño puede hacer una acción
Lista permitidacomandos aprobados de antemano
Ruta protegidaun archivo que siempre pide aprobación
Aprobaciónel sí o el no de la persona a una acción

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

Un modo es un ajuste, no un prompt

Podrías escribir «nunca cambies archivos» en el prompt. Un modelo puede hacerlo igualmente, porque un prompt es una petición.

Un modo es código en el arnés. Cada acción pasa por una función antes de ejecutarse. En modo plan esa función rechaza una escritura, diga lo que diga el modelo. El modelo no ve ninguna forma de esquivarla.

Las reglas se comprueban en orden

Da a cada acción un riesgo: read, write, run o destructive. Luego decide por modo, con las reglas en un orden fijo. El modo plan va primero, porque rechaza más que ningún otro.

Después, una ruta protegida o una acción destructiva pregunta siempre. Solo entonces un modo relaja algo. Una lista permitida deja que el modo auto ejecute comandos con nombre sin preguntar.

Qué hace cada modo con cada riesgo

RiesgoPlanAskAuto
LecturaPermitirPermitirPermitir
EscrituraDenegarPreguntarPermitir
EjecuciónDenegarPreguntarPermitir si está en la lista
DestructivaDenegarPreguntarPreguntar
Ruta protegidaDenegarPreguntarPreguntar

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

Un error frecuente

Advertencia

Mucha gente pide aprobación para cada acción. Entonces la gente pulsa sí sin leer. Pregunta solo donde importa: rutas protegidas, acciones destructivas y comandos fuera de la lista.

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 revisor de modos, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const ALLOWED = [\"pytest\"];' 'const actions = [' '  { tool: \"read_file\", risk: \"read\", target: \"src/app.py\" },' '  { tool: \"write_file\", risk: \"write\", target: \"src/app.py\" },' '  { tool: \"run\", risk: \"run\", target: \"pytest\" },' '  { tool: \"run\", risk: \"run\", target: \"curl sitio\" },' '  { tool: \"delete_file\", risk: \"destructive\", target: \"src/viejo.py\" },' '  { tool: \"read_file\", risk: \"read\", target: \".env\" },' '];' > modes3.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 revisor de modos, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function protectedPath(t) {' '  return t === \".env\" || t.startsWith(\".env.\") || t.startsWith(\".git/\") || t.toLowerCase().includes(\"secret\");' '}' 'function decide(mode, a) {' '  if (mode === \"plan\") return a.risk === \"read\" && protectedPath(a.target) === false ? \"allow\" : \"deny\";' '  if (protectedPath(a.target) || a.risk === \"destructive\") return \"ask\";' '  if (mode === \"ask\") return a.risk === \"read\" ? \"allow\" : \"ask\";' '  if (a.risk === \"run\") return ALLOWED.includes(a.target) ? \"allow\" : \"ask\";' '  return \"allow\";' '}' >> modes3.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 revisor de modos, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  for (const a of actions) {' '    const row = [\"ask\", \"plan\", \"auto\"].map(function (m) { return m + \":\" + decide(m, a); });' '    console.log(a.tool + \" \" + a.target + \" -> \" + row.join(\" \"));' '  }' '  const q = \"Un agente quiere borrar src/viejo.py. Escribe una frase corta que pida a la persona que lo apruebe.\";' '  const reply = await ask([{ role: \"user\", content: q }], \"El agente quiere borrar src/viejo.py. ¿Lo permites?\", 40);' '  console.log(\"pregunta mostrada: \" + reply.text);' '}' 'main();' >> modes3.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 las acciones en tres modos

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

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Las decisiones no necesitan modelo

Las seis líneas salen solo de la función de decisión, así que son iguales para cada estudiante. Luego el Run hace una llamada corta, para redactar la pregunta que vería una persona antes de un borrado.

Sin modelo imprime una muestra grabada para esa sola línea.

Una salida posible

LíneaQué muestra
read_file src/app.py -> ask:allow plan:allow auto:allowUna lectura simple pasa en todas partes
write_file src/app.py -> ask:ask plan:deny auto:allowSolo auto deja pasar una escritura
run pytest -> ask:ask plan:deny auto:allowEstá en la lista permitida
run curl sitio -> ask:ask plan:deny auto:askFuera de la lista, auto pregunta
delete_file src/viejo.py -> ask:ask plan:deny auto:askUn borrado siempre pregunta
read_file .env -> ask:ask plan:deny auto:askUna ruta protegida siempre pregunta

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.