Human-in-the-loop approvals (frenar writes) + Código

Las approvals son aburridas — y más baratas que deshacer 200 writes. Un gate de aprobación para tool calls de riesgo.
En esta página
  1. El problema (en producción)
  2. Por qué esto se rompe en producción
  3. 1) “Write tool” no es una categoría única
  4. 2) Sin approval path, te vuelves autónomo por accidente
  5. 3) Approval sin contexto no sirve
  6. Ejemplo de implementación (código real)
  7. Incidente real (con números)
  8. Trade-offs
  9. Cuándo NO usarlo
  10. Checklist (copiar/pegar)
  11. Config segura por defecto (JSON/YAML)
  12. FAQ (3–5)
  13. Páginas relacionadas (3–6 links)
Flujo interactivo
Escenario:
Paso 1/3: Execution

Action is proposed as structured data (tool + args).

El problema (en producción)

Quieres writes. Tu agente quiere writes. Tus clientes quieren que los writes sean correctos.

En demo: “el agente puede cerrar el ticket”. En prod: “puede” es la parte donde terminas explicando por qué 200 cosas quedaron mal.

Approvals no es “enterprise feature”. Es una válvula de seguridad cuando el que decide es probabilístico.

Por qué esto se rompe en producción

1) “Write tool” no es una categoría única

No todos los writes son iguales:

  • reversible vs irreversible
  • bajo impacto vs alto impacto
  • idempotente vs “oops, duplicado”

Si tratas todo igual, terminas con:

  • demasiadas approvals (UX muere)
  • o muy pocas (prod muere)

2) Sin approval path, te vuelves autónomo por accidente

Si un write tool está en la allowlist y nadie lo frena, se va a usar. No “quizás”. Cuando sea el camino más corto.

3) Approval sin contexto no sirve

“Approve este tool call?” no basta. Necesitas evidencia:

  • acción propuesta
  • args (o diff)
  • por qué el agente cree que es correcto
  • razón de policy / riesgo

Ejemplo de implementación (código real)

Un gate mínimo:

  • write tools → approval request
  • approval firmado (id/key)
  • tool gateway ejecuta solo si hay approval válida
PYTHON
from dataclasses import dataclass
from typing import Any


WRITE_TOOLS = {"ticket.close", "db.write", "email.send"}


@dataclass(frozen=True)
class Approval:
  approval_id: str
  approved: bool
  approved_by: str


class ApprovalRequired(RuntimeError):
  pass


def requires_approval(tool: str) -> bool:
  return tool in WRITE_TOOLS


def tool_gateway_call(tool: str, args: dict[str, Any], *, approval: Approval | None) -> Any:
  if requires_approval(tool):
      if not approval or not approval.approved:
          raise ApprovalRequired(f"approval_required:{tool}")
  return call_tool(tool, args)  # (pseudo)
JAVASCRIPT
const WRITE_TOOLS = new Set(["ticket.close", "db.write", "email.send"]);

export class ApprovalRequired extends Error {}

export function requiresApproval(tool) {
return WRITE_TOOLS.has(tool);
}

export function toolGatewayCall(tool, args, { approval } = {}) {
if (requiresApproval(tool)) {
  if (!approval || approval.approved !== true) throw new ApprovalRequired("approval_required:" + tool);
}
return callTool(tool, args); // (pseudo)
}

Incidente real (con números)

Teníamos un agente para “limpiar” tickets de soporte. Un cambio de prompt interpretó “cleanup” como “close”.

Impacto:

  • 63 tickets cerrados cuando no debían
  • ~2 horas para reabrir + pedir disculpas
  • pusimos el agente en read-only una semana hasta recuperar confianza

Fix:

  1. approvals para writes high-impact
  2. UI de approval con diff/evidence (no solo “approve?”)
  3. idempotency keys para evitar doble ejecución

Trade-offs

  • Approvals agregan latencia. Es el costo de seguridad.
  • Demasiadas approvals matan la UX → necesitas risk tiers.
  • Sin audit logs, en incidente no tienes pruebas.

Cuándo NO usarlo

  • No necesitas approvals para tools read-only.
  • Para writes low-risk y totalmente reversibles, auto-approve bajo budgets estrictos puede ser aceptable.
  • Si nadie revisa en serio: no es approval, es teatro.

Checklist (copiar/pegar)

  • [ ] Identificar write tools (capability-based)
  • [ ] Approvals para acciones irreversibles / high-impact
  • [ ] Evidence en la UI (diff, why, provenance)
  • [ ] Enforced en tool gateway (no solo frontend)
  • [ ] Idempotency keys por acción aprobada
  • [ ] Audit logs (who approved what)

Config segura por defecto (JSON/YAML)

YAML
approvals:
  required_for:
    - "db.write"
    - "email.send"
    - "ticket.close"
  evidence:
    include_args: true
    include_diff: true
  enforce_in: "tool_gateway"

FAQ (3–5)

¿Cuándo necesito approvals de verdad?
Cuando la acción es costosa/irreversible o modifica datos del cliente. Default: writes → approval.
¿Cómo evito spam de approvals?
Risk tiers + escalar solo high-risk. Y mantén write tools pequeños (no RPC genérico).
¿Puedo automatizar approvals?
Parcialmente: auto-approve low-risk bajo budgets estrictos. Pero loggea como ‘auto-approved’.
¿Dónde enforceo approvals?
En el tool gateway. Si es solo UI, se va a bypass.

P: ¿Cuándo necesito approvals de verdad?
R: Cuando la acción es costosa/irreversible o modifica datos del cliente. Default: writes → approval.

P: ¿Cómo evito spam de approvals?
R: Risk tiers + escalar solo high-risk. Y mantén write tools pequeños (no RPC genérico).

P: ¿Puedo automatizar approvals?
R: Parcialmente: auto-approve low-risk bajo budgets estrictos. Pero loggea como “auto-approved”.

P: ¿Dónde enforceo approvals?
R: En el tool gateway. Si es solo UI, se va a bypass.

No sabes si este es tu caso?

Disena tu agente ->
⏱️ 4 min de lecturaActualizado Mar, 2026Dificultad: ★★★
Implementar en OnceOnly
Budgets + permissions you can enforce at the boundary.
Usar en OnceOnly
# onceonly guardrails (concept)
version: 1
budgets:
  max_steps: 25
  max_tool_calls: 12
  max_seconds: 60
  max_usd: 1.00
policy:
  tool_allowlist:
    - search.read
    - http.get
writes:
  require_approval: true
  idempotency: true
controls:
  kill_switch: { enabled: true }
Integrado: control en producciónOnceOnly
Guardrails para agentes con tool-calling
Lleva este patrón a producción con gobernanza:
  • Presupuestos (pasos / topes de gasto)
  • Permisos de herramientas (allowlist / blocklist)
  • Kill switch y parada por incidente
  • Idempotencia y dedupe
  • Audit logs y trazabilidad
Mención integrada: OnceOnly es una capa de control para sistemas de agentes en producción.
Autor

Esta documentación está curada y mantenida por ingenieros que despliegan agentes de IA en producción.

El contenido es asistido por IA, con responsabilidad editorial humana sobre la exactitud, la claridad y la relevancia en producción.

Los patrones y las recomendaciones se basan en post-mortems, modos de fallo e incidentes operativos en sistemas desplegados, incluido durante el desarrollo y la operación de infraestructura de gobernanza para agentes en OnceOnly.