¿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

Cómo limitar una herramienta LLM

Después de esta lección: Puedo comprobar cada petición de herramienta con una política de lista permitida antes de que se ejecute nada.

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

El modelo pide un archivo

Limitar las herramientas de un modelo significa que tu código, no el modelo, decide lo que puede hacer cada una. Importa porque la petición de un modelo es texto, y el texto puede ser erróneo, engañado o inventado.

Un asistente de notas tiene una herramienta que lee una nota por su nombre. Una respuesta pide la nota «../privado». La herramienta lo intentaría sin más. Una política la detiene antes de empezar.

Al final de esta lección

Nota

Puedo comprobar cada petición de herramienta con una política de lista permitida antes de que se ejecute nada.

Palabras clave

PalabraQué significa
Lista permitidalas únicas herramientas y valores que se permiten
Políticadatos que dicen lo que puede recibir cada herramienta
Comprobación de argumentosuna prueba de las entradas de una herramienta antes de ejecutarla
Mínimo privilegiodar a una herramienta solo el poder que necesita
Tiempo límiteun tope a lo que puede tardar una llamada

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

Una lista permitida dice sí, nunca no

Una lista de prohibidos nombra lo malo y deja pasar el resto. Siempre se le escapa algo. Una lista permitida nombra lo bueno y rechaza el resto.

Escribe una política como datos: cada herramienta, cada argumento, su tipo y sus límites. Una función lee la política y juzga cada petición. El modelo nunca ejecuta nada por sí mismo.

Ejemplo: una política y cuatro peticiones

PeticiónLa política diceDecisión
read_note, name menuname debe ser compras, menu u horarioPermitida
read_note, name ../privadoname debe ser compras, menu u horarioRechazada: no permitido
run_shell, cualquier textoNo existe esa herramientaRechazada: herramienta desconocida
add_numbers, b 99999b es como mucho 1000Rechazada: demasiado grande

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

Nunca construyas un comando con texto del modelo

Algunas herramientas tientan: ejecutar un comando, evaluar una expresión, escribir un archivo. No construyas ninguna con texto que escribió un modelo. Un rechazo es más seguro que un filtro ingenioso.

Si una función necesita ejecutar código real, hazlo en un entorno aislado aparte. Sin secretos y con un tiempo límite. Este laboratorio no tiene uno, así que la lección solo rechaza.

Escribir 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\";' '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 || 200 }),' '      signal: AbortSignal.timeout(30000),' '    });' '    if (res.ok === false) throw new Error(\"estado \" + res.status);' '    return (await res.json()).choices[0].message.content;' > 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.

Escribir el ayudante del modelo, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  } catch (error) {' '    console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' '    const list = [].concat(sample);' '    const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' '    return list[Math.min(turn, list.length - 1)];' '  }' '}' '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.

Escribir el script de política, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' '// Notas inventadas y una política: cada herramienta, sus argumentos y sus límites.' 'const notes = { compras: \"comprar harina\", menu: \"pan, tarta\", horario: \"abierto a las 8\" };' 'const policy = {' '  read_note: { name: { type: \"string\", allowed: Object.keys(notes) } },' '  add_numbers: { a: { type: \"number\", max: 1000 }, b: { type: \"number\", max: 1000 } },' '};' > policy.js"

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

Escribir el script de política, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function check(request) {' '  const rule = policy[request.tool];' '  if (rule === undefined) return \"rechazada: herramienta desconocida\";' '  const args = request.args || {};' '  for (const key of Object.keys(args)) {' '    if (rule[key] === undefined) return \"rechazada: argumento inesperado \" + key;' '  }' '  for (const key of Object.keys(rule)) {' '    const spec = rule[key];' '    const value = args[key];' '    if (typeof value === spec.type === false) return \"rechazada: \" + key + \" incorrecto\";' '    if (spec.allowed && spec.allowed.includes(value) === false) return \"rechazada: \" + key + \" no permitido\";' '    if (typeof spec.max === \"number\" && value > spec.max) return \"rechazada: \" + key + \" demasiado grande\";' '  }' '  return \"ok\";' '}' >> policy.js"

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

Escribir el script de política, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function run(request) {' '  if (check(request) === \"ok\") {' '    return request.tool === \"read_note\" ? notes[request.args.name] : request.args.a + request.args.b;' '  }' '  return check(request);' '}' 'const scripted = [' '  { tool: \"read_note\", args: { name: \"menu\" } },' '  { tool: \"read_note\", args: { name: \"../privado\" } },' '  { tool: \"run_shell\", args: { text: \"listar la carpeta\" } },' '  { tool: \"add_numbers\", args: { a: 5, b: 99999 } },' '  { tool: \"add_numbers\", args: { a: 2, b: 3, c: 4 } },' '];' 'for (const request of scripted) console.log(request.tool + \" -> \" + run(request));' >> policy.js"

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

Escribir el script de política, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function pullJson(text) {' '  const start = text.indexOf(\"{\");' '  const end = text.lastIndexOf(\"}\");' '  try { return JSON.parse(text.slice(start, end + 1)); } catch (error) { return null; }' '}' 'async function main() {' '  const question = [{ role: \"user\", content: \"Responde solo con un objeto JSON con las claves tool y args, para leer la nota llamada horario. Herramientas: read_note(name), add_numbers(a, b).\" }];' '  const sample = JSON.stringify({ tool: \"read_note\", args: { name: \"horario\" } });' '  const request = pullJson(await ask(question, sample, 80));' '  console.log(\"Petición del modelo: \" + JSON.stringify(request));' '  console.log(\"Resultado: \" + (request === null ? \"rechazada: no es JSON\" : run(request)));' '}' 'main();' >> policy.js"

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

Ejecutar el script de política

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

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

Una salida posible

LíneaQué muestra
read_note -> pan, tartaLa primera petición pasó todas las comprobaciones
read_note -> rechazada: name no permitidoEl truco de ruta falló ante la lista permitida
run_shell -> rechazada: herramienta desconocidaLa herramienta no está en la política
add_numbers -> rechazada: b demasiado grandeEl número supera el máximo de la política. La última línea rechaza el argumento c, que la política no nombra
Petición del modelo, y ResultadoCambia en cada ejecución. Una petición del modelo pasa por la misma comprobación

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

Limita el tiempo y el poder

Un tiempo límite evita que una herramienta lenta retenga una petición para siempre. El ayudante que escribiste ya abandona al modelo a los 30 segundos.

Aplica también el mínimo privilegio. Una herramienta que lee notas no debe poder escribirlas, y una que escribe solo debe llegar a su propia carpeta.

Un error frecuente

Advertencia

Mucha gente cree que, si se dijeron las reglas al modelo, la herramienta no necesita comprobar. El modelo puede equivocarse, ser engañado o inventar una herramienta. Comprueba siempre en el código que ejecuta la herramienta, porque es la única parte que hace lo mismo cada vez.

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.