Idea en 30 segundos
Write Access by Default es un anti-pattern donde el agente recibe write-tools por defecto, sin policy-gate ni verificación antes de ejecutar.
Como resultado, un error del modelo o una ruta incorrecta no se convierte solo en respuesta errónea, sino en cambio real del estado externo.
Regla simple: write no debe ser "por defecto". Debe pasar por una ruta separada y controlada con verificación explícita de permisos, contexto y condiciones de ejecución.
Ejemplo del anti-pattern
El equipo construye un agente de soporte que lee estado de pedido y, si hace falta, puede cerrar un ticket o enviar un correo.
El agente recibe write-tools de inmediato, sin etapa dedicada de control antes de actuar.
decision = agent.decide_next_action(user_message)
# la ruta es incorrecta, pero write sigue habilitado
result = run_tool(decision.tool, decision.args)
return result
En este esquema falta protección base:
# no hay deny-by-default para write-tools
# no hay approval_required para acciones riesgosas
# no hay idempotency_key para retries
# no hay tenant/env scope estricto
Para este caso se necesita un policy-gate antes de cualquier paso write:
if decision.tool in WRITE_TOOLS and not is_write_allowed(ctx, decision):
return stop("approval_required")
Si no se cumplen condiciones, el run no debe pasar a una acción write externa.
En este caso, Write Access by Default agrega:
- riesgo de cambios no deseados en sistemas externos
- operaciones write duplicadas durante retries
- mayor blast radius en entornos multi-tenant
Por qué aparece y qué sale mal
Este anti-pattern aparece cuando el equipo quiere que el agente sea "lo más autónomo posible" y abre acceso write antes de construir límites de control.
Causas típicas:
- enfoque demo-first: primero dar todas las herramientas, luego agregar restricciones
- ausencia de separación explícita entre rutas
readywrite - reglas de acceso descritas en prompt, no forzadas en gateway
- falta de
idempotency_keyobligatorio para write-operaciones
Como resultado aparecen problemas:
- side effects inseguros (cambios de estado) - el agente puede escribir donde debía solo leer
- acciones repetidas - retry o loop repite la misma write-operación
- blast radius alto - sin scope estricto, el error impacta tenant/env incorrecto
- análisis de incidente difícil - cuesta demostrar por qué write fue permitido
- pérdida de previsibilidad - el equipo no controla cuándo el sistema pasa a write
A diferencia de Blind Tool Trust, aquí el problema central no es validar payload, sino que write está abierto por defecto.
Señales típicas de producción de control write débil:
- write-tools se llaman incluso en escenarios que deberían ser read-only
- en logs hay muchos write-calls con el mismo
args_hasho sinidempotency_key approval_requiredcasi no aparece aunque el porcentaje de write sea alto- blocked write attempts casi no ocurren aunque el sistema proponga risky actions de forma regular
- audit-logs no muestran qué regla policy permitió write
- el equipo no puede explicar claramente por qué un write concreto fue permitido en ese run
- los errores se detectan después de la acción externa, no en policy-gate
Es importante: cada write-call cambia estado externo y muchas veces no tiene rollback simple. Sin deny-by-default y ruta write controlada, una inference fallida se vuelve incidente de producción.
Enfoque correcto
Empieza con modelo read-first: los read-tools quedan disponibles por ruta, y los pasos write pasan por verificación separada vía policy-gate.
Marco práctico:
- aplica deny-by-default para todos los write-tools
- separa rutas
read_onlyywrite_candidate - exige approval para write-acciones riesgosas
- toma
tenant_idyenvsolo desde contexto autenticado, no desde model output - agrega
idempotency_keyen cada write-operación - loggea
stop_reasony decisiones del policy-gate en cada write-step
WRITE_TOOLS = {"ticket.close", "refund.create", "email.send"}
def execute_action(user_message: str, ctx: dict):
decision = agent.next_action(user_message)
if decision.tool in WRITE_TOOLS:
if not is_write_allowed(ctx, decision): # policy gate: role, route allowlist, tenant/env scope
return stop("approval_required")
scoped_args = enforce_scope(
decision.args,
tenant_id=ctx["tenant_id"],
env=ctx["env"],
)
scoped_args["idempotency_key"] = make_idempotency_key(ctx["run_id"], decision)
return run_tool(decision.tool, scoped_args)
return run_tool(decision.tool, decision.args) # read-only tool from allowed set
En este esquema, el paso write queda controlado: el sistema ejecuta de forma segura o detiene el run de manera transparente.
Prueba rápida
Si respondes "sí" a estas preguntas, tienes riesgo del anti-pattern Write Access by Default:
- ¿Un write-tool puede llamarse sin verificación explícita de policy/approval?
- ¿Un retry a veces repite la misma write-acción sin
idempotency_key? - ¿El equipo no puede explicar rápidamente por qué un write concreto fue permitido?
Cómo se diferencia de otros anti-patterns
Blind Tool Trust vs Write Access by Default
| Blind Tool Trust | Write Access by Default |
|---|---|
| Problema principal: tool output se acepta sin validación. | Problema principal: acceso write abierto por defecto. |
| Cuándo aparece: cuando faltan checks parse/schema/invariant antes de decidir. | Cuándo aparece: cuando write no pasa por deny-by-default ni approval-gate. |
En resumen: Blind Tool Trust trata de calidad de datos antes de actuar, mientras Write Access by Default trata de permisos para la acción.
Agents Without Guardrails vs Write Access by Default
| Agents Without Guardrails | Write Access by Default |
|---|---|
| Problema principal: faltan límites runtime y control policy en general. | Problema principal: específicamente las write-operaciones no tienen contorno de acceso estricto. |
| Cuándo aparece: cuando el sistema no tiene safety-policy clara para execution. | Cuándo aparece: cuando write se permite como ruta estándar en vez de excepción vía policy-gate. |
En resumen: Agents Without Guardrails es un problema más amplio de límites de ejecución, y Write Access by Default es específicamente un modelo de acceso write inseguro.
Tool Calling for Everything vs Write Access by Default
| Tool Calling for Everything | Write Access by Default |
|---|---|
| Problema principal: se llaman tools de más, incluso cuando no hacen falta. | Problema principal: una vez hay tool-call, write puede pasar sin control suficiente. |
Cuándo aparece: cuando no hay ruta no_tool estable para casos simples. | Cuándo aparece: cuando el sistema no separa niveles de acceso read y write. |
En resumen: Tool Calling for Everything aumenta cantidad de llamadas, y Write Access by Default aumenta el costo del error de cada write-call.
Autoevaluación: ¿tienes este anti-pattern?
Chequeo rápido del anti-pattern Write Access by Default.
Marca los puntos para tu sistema y revisa el estado abajo.
Revisa tu sistema:
Progreso: 0/8
⚠ Hay señales de este anti-patrón
Intenta mover los pasos simples a un workflow y dejar el agente solo para decisiones complejas.
FAQ
Q: ¿Esto significa que el agente nunca debe ejecutar write-acciones?
A: No. Las write-acciones son posibles, pero solo por ruta controlada: policy-gate, approval (cuando aplique), scope enforcement e idempotency.
Q: ¿Cuál es la diferencia entre policy-gate y approval?
A: Policy-gate es verificación determinista de reglas en runtime. Approval es confirmación separada para una acción riesgosa específica. Son capas de control distintas.
Q: ¿Qué mínimo implementar primero?
A: Empieza con deny-by-default para write, idempotency_key obligatorio, tenant/env scope estricto y logging de stop_reason para intentos write bloqueados.
Qué sigue
Anti-patterns relacionados:
- Blind Tool Trust - cuando el sistema actúa sobre tool output no validado.
- Agents Without Guardrails - cuando execution corre sin límites runtime claros.
- Tool Calling for Everything - cuando tools se llaman sin necesidad explícita.
Qué construir en su lugar:
- Allowed Actions - cómo fijar acciones permitidas con reglas explícitas.
- Tool Execution Layer - dónde centralizar policy, scope e idempotency.
- Stop Conditions - cómo detener un run de forma segura cuando write no está permitido.