Core · Herramientas tipadas con guardrails
Tool use
Las herramientas (tools) son cómo los agentes ejecutan acciones en el mundo real: leer una BD, llamar un API, crear un ticket, enviar un email. OpenClaw las define con schemas tipados y añade varias capas de seguridad antes y después de cada invocación.
Definición básica
import { z } from 'zod';
claw.tool({
name: 'create_ticket',
description: 'Crea un ticket de soporte en Zendesk',
input: z.object({
subject: z.string().min(5).max(200),
description: z.string().min(20),
priority: z.enum(['low', 'normal', 'high']).default('normal'),
customerId: z.string().uuid(),
}),
output: z.object({
ticketId: z.string(),
url: z.string().url(),
}),
handler: async ({ subject, description, priority, customerId }) => {
const res = await zendesk.tickets.create({ subject, description, priority, customerId });
return { ticketId: res.id, url: res.url };
},
});Guardrails
Cada tool puede tener validaciones adicionales antes y después del handler. Fallan la ejecución con un error estructurado que el LLM puede entender y reaccionar.
claw.tool({
name: 'refund_order',
input: z.object({
orderId: z.string(),
amount: z.number().positive(),
reason: z.string().min(10),
}),
guardrails: {
// Pre-invocación
pre: async ({ input, ctx }) => {
const order = await db.orders.findById(input.orderId);
if (!order) throw new ToolError('order_not_found');
if (input.amount > order.total) throw new ToolError('refund_exceeds_total');
if (order.refunded) throw new ToolError('already_refunded');
},
// Post-invocación
post: async ({ output, ctx }) => {
ctx.audit.log('refund', { orderId: input.orderId, amount: input.amount });
},
},
handler: async ({ orderId, amount, reason }) => {
return stripe.refunds.create({ charge: orderId, amount, metadata: { reason } });
},
});Aprobación humana
Para tools críticas, la ejecución se pausa y se envía una solicitud a un canal configurado (Slack, email, dashboard). El humano aprueba o rechaza y el agente continúa.
claw.tool({
name: 'transfer_funds',
input: z.object({
from: z.string(),
to: z.string(),
amount: z.number().positive(),
}),
humanApproval: {
when: (input) => input.amount > 1000, // condicional
channel: 'slack',
channelTarget: '#finance-approvals',
timeoutSeconds: 300,
onTimeout: 'reject',
},
handler: async (input) => { /* ... */ },
});Sandboxing
Tools que ejecutan código no confiable (shell, SQL dinámico) pueden correr en sandbox con aislamiento de red y filesystem.
claw.tool({
name: 'run_python',
input: z.object({ code: z.string() }),
sandbox: {
runtime: 'python3.12',
network: 'none', // sin acceso a internet
filesystem: 'tmpfs', // solo escritura temporal
cpuLimitMs: 5000,
memoryLimitMb: 256,
},
handler: async ({ code }) => {
return sandbox.run(code);
},
});Buenas prácticas
- Usa inputs estrictos (
z.string().uuid()>z.string()). - Define outputs también — el LLM razona mejor con contrato claro.
- Para acciones irreversibles (borrar, cobrar, enviar), activa
humanApproval. - Añade un
idempotencyKeypara tools que pueden reintentarse.
Anterior
Canales
Siguiente
MCP