CH 11 · Herramientas y Sandbox
Objetivo del capítulo
En los capítulos anteriores has visto al Agent en acción: lee archivos por su cuenta, ejecuta comandos, busca en la web y abre un diálogo para preguntarte sobre operaciones sensibles. Este capítulo desmonta "herramienta" y "sandbox" — cómo se invoca una herramienta, qué comprobaciones atraviesa; cómo el sandbox acorrala al Agent y qué pasa cuando choca con el límite. Cuando entiendas estos dos mecanismos, sabrás por qué se "atreve" a actuar en tu ordenador y qué es lo que realmente no puede tocar.
Herramientas: las "manos" del Agent
En dsh, el Agent en sí no puede mover nada; todas sus "acciones" se realizan a través de herramientas — leer archivos, ejecutar comandos, buscar en la web: cada una es una herramienta. En CH 08 dijimos que las herramientas son "el armario + el portero"; vamos a desglosarlo un poco más.
Una herramienta en el sistema tiene este aspecto:
| Parte | Qué hace |
|---|---|
| schema | Las "instrucciones" que ve el modelo: nombre, descripción, parámetros (JSON Schema) |
| Función de ejecución | El código que realmente hace el trabajo |
| Declaración de salida | La estructura que debe devolverse al terminar |
| Metadatos de planificación | Si puede ejecutarse en paralelo, timeout, cómo se muestra en la UI |
La clave está en la primera: a los ojos del modelo solo existen el nombre, la descripción y los parámetros de una herramienta — la función de ejecución, la declaración de salida, el timeout y la marca de paralelismo no son visibles para el modelo. Esta es la primera capa de seguridad: el modelo sabe "esta herramienta existe, los parámetros se rellenan así", pero no conoce su implementación interna y no puede saltarse la lógica de ejecución.
Una llamada a herramienta: una pipeline extensible
Después de que el modelo dice de llamar a una herramienta, no se ejecuta directamente; en su lugar, atraviesa una pipeline extensible. El equipo oficial diseñó esta pipeline de modo que cada etapa pueda ser interceptada o mejorada por plugins — otro "todo es un plugin" (en el diagrama se omite una etapa opcional finalizeContent; es el callback de fin de tubería propio de la herramienta y no afecta a la línea principal):
Recorrido paso a paso:
- Petición del modelo: el modelo emite un tool/call (nombre de herramienta + parámetros). Los parámetros se validan primero; si son inválidos, se lanza directamente un error (
INVALID_ARGS) y nunca se ejecuta. - pre-execute: el primer punto de control. Aquí se decide si una llamada es allow / deny / ask — el diálogo de aprobación que ves en la UI ocurre en esta capa.
- guard: una guardia monótona: solo puede volverse más estricta, no más laxa, evitando que alguna etapa relaje silenciosamente el límite.
- execute: la ejecución real. El sandbox se monta en este paso — antes de que el comando se ejecute de verdad, se envuelve en una capa de archivos (ver más abajo).
- post-execute: inspecciona el resultado, puede reemplazarlo si hace falta.
- result: produce el resultado autoritativo, se devuelve al modelo y entra en la siguiente ronda.
Cada paso puede tener hooks enganchados por plugins — por eso más adelante, al escribir plugins, podrás hacer uno que "intercepte ciertas llamadas de herramientas" (CH 21 cubre los hooks). Lee esta pipeline y sabrás dónde se montan la aprobación, el sandbox y los "componentes de seguridad" del log.
Sandbox: una "capa de archivos" alrededor de los comandos
CH 04 cubrió los tres niveles de permiso (read-only / workspace-write / danger-full-access), desde la perspectiva de tu manejo de la UI. Aquí vemos el mecanismo: el sandbox solo gobierna los efectos sobre el sistema de archivos; la visibilidad de red y procesos no es de su competencia.
El equipo oficial diseñó el sandbox como dos capas separadas — "política" y "backend":
- Política (SandboxPolicy): se re-parsea en cada llamada — modo + raíz del workspace. La raíz del workspace se deriva del cwd de la sesión actual.
- Backend (SandboxProvider): envuelve el comando en un proceso restringido para la plataforma actual. Cada plataforma tiene su propia implementación — Linux usa bwrap / Landlock (control de acceso sin privilegios a nivel de kernel), macOS usa Seatbelt, Windows usa un runner con token restringido por ACL.
Algunos diseños que vale la pena recordar:
fail-closed: esta es la clave de su seguridad. Si no hay ningún backend de sandbox disponible en el entorno actual, el sistema reporta directamente un error SANDBOX_UNAVAILABLE, nunca baja silenciosamente a "ejecutar desnudo sin sandbox". Mejor negarse a ejecutar que arriesgarse a soltar las riendas.
danger-full-access no envuelve una capa: solo los modos restringidos (read-only / workspace-write) pasan por la envoltura del sandbox. El modo de permiso total lanza directamente el comando original sin aislamiento de archivos — por eso la UI confirma dos veces cuando cambias al tercer nivel.
Integridad obligatoria dividida en full / partial: la mayor parte del tiempo el backend puede gobernar todos los efectos prometidos sobre archivos (full); pero en ABIs antiguas del kernel de Linux o en algunos límites de Windows, solo puede gobernar parte de ellos (partial), y cualquier escenario que requiera garantías absolutas debe saber esto. Para el uso diario ordinario, el workspace-write por defecto es suficientemente estable.
Manos a la obra: compruébalo tú mismo
Paso 1: ver una llamada a herramienta en la Trayectoria
En la Web UI, ejecuta una tarea que haga alguna acción (p. ej., el resumen de repo de CH 05). Cuando termine, cambia a la pestaña Trayectoria y haz clic en cualquier fila TOOL. El panel derecho tiene cuatro pestañas clave, que se corresponden exactamente con la estructura de herramienta anterior:
- Schema: las "instrucciones" de la herramienta (nombre, descripción, parámetros)
- Payload: los parámetros reales enviados esta vez
- Result: el resultado devuelto
- Summary / Timing: resumen y tiempo transcurrido
La imagen de abajo es una llamada real: a la izquierda, una fila TOOL (web_search) seleccionada en la línea de tiempo; a la derecha, todas las pestañas expandidas en el panel; abajo puedes ver las estadísticas globales de la ronda — fíjate en que en mitad también hay dos comandos pwsh que fallaron por problemas de red y el Agent cambió inmediatamente a web_search. Este es exactamente un ejemplo real de "las herramientas pueden cambiar de camino cuando fallan":

Paso 2: ver una aprobación
Con el permiso por defecto workspace-write, haz que el Agent escriba un archivo fuera del workspace. Aquí le pido que cree un archivo de saludo bajo E:\software-workspace\doubaowork\doubao — ese directorio no está en el workspace actual:

Fíjate en que lo que pasa en realidad son dos pasos:
- El primer Write choca contra el muro directamente — la Trayectoria muestra
Write · Error: [sandbox: file access denied under workspace-write mode]. - El Agent se da cuenta de que el destino está fuera del workspace y solicita proactivamente una escalada; el diálogo ask de la capa pre-execute aparece solo en este punto: elevar el sandbox a danger-full-access, con un motivo. Los dos botones de abajo — Denegar y Permitir una vez:

Pulsa "Permitir una vez", solo permite esta única escritura; pulsa "Denegar" y tiene que buscar otra forma.
Paso 3: cambia a read-only y mira
En el cuadro de entrada escribe /permission, cambia a read-only (tras la entrada, la UI mostrará permission · preset read-only) y haz que el Agent escriba un archivo. El resultado es similar al de arriba, pero con una diferencia clave:

- El primer Write también choca contra el muro — pero el error es distinto:
Write · Error: [sandbox: file access denied under read-only mode]. - El Agent también solicita una escalada — pero esta vez el destino es
escalate sandbox to workspace-write, no danger-full-access (solo necesita un permiso de escritura normal, no hace falta subirlo del todo).
Comparando los tres casos de "bloqueo" queda claro: independientemente del nivel de permiso, cuando falla una escritura el Agent choca primero contra el muro y luego muestra el diálogo pidiéndote permiso. Las diferencias son el mensaje de error (workspace-write mode / read-only mode) y el siguiente nivel que solicita (danger-full-access / workspace-write). El botón "Denegar" siempre está ahí — este es el núcleo del diseño del sandbox: el Agent puede "pedir", pero "dar o no dar" siempre depende de ti.
Qué aprendiste en este capítulo
Apruebas si puedes completar los siguientes puntos:
- [ ] Enumerar de qué partes se compone una herramienta y cuáles son visibles para el modelo y cuáles no
- [ ] Dibujar la pipeline de ejecución de herramientas (model request → pre-execute → guard → execute → post-execute → result) y decir dónde se montan la aprobación y el sandbox
- [ ] Explicar qué significa que el sandbox "solo gobierna los efectos sobre el sistema de archivos" y por qué
- [ ] Explicar fail-closed: qué hace el sistema cuando no hay backend de sandbox disponible (reporta SANDBOX_UNAVAILABLE, no ejecuta desnudo)
- [ ] Abrir una llamada a herramienta en la Trayectoria y entender Schema / Payload / Result
- [ ] Indicar los errores cuando se deniegan escrituras en distintos niveles de permiso (workspace-write mode / read-only mode) y los destinos de escalada que pide el Agent (danger-full-access / workspace-write)
- [ ] Poder explicar: cuando una escritura falla, el Agent choca primero contra el muro y luego abre el diálogo, pero "Denegar" siempre está en tus manos
