Ensayo de Activación
Qué es esto
Un agente que se activa a sí mismo mediante eventos es tan seguro como sus reglas de activación. El agente en sí mismo puede ser cuidadoso y aún así quemar el presupuesto de un día, porque el problema suele estar un nivel arriba:
- una regla reacciona a un evento que su propio trabajo produce, por lo que se dispara una y otra vez;
- dos reglas se alimentan mutuamente a través de un segundo sistema (el agente comenta, el comentario se sincroniza, la sincronización lanza un webhook);
- una fuente ruidosa envía diez ediciones en dos minutos y cada una inicia una ejecución nueva;
- una línea cron pensada como "revisa de vez en cuando" se dispara 288 veces al día;
- dos reglas manejan el mismo evento y hacen el trabajo dos veces;
- una regla que reembolsa, elimina o envía mensajes a personas se ejecuta sin preguntar a nadie.
Ninguno de estos problemas se muestra cuando pruebas el agente con un solo ejemplo. Aparecen cuando llega tráfico real. Trigger Rehearsal hace que el tráfico real llegue antes, en papel: escribes las reglas en un formato neutral, reproduces un día o una semana de eventos a través de ellas, y lees la línea de tiempo antes de que cualquier cosa esté armada.
Cuándo usarlo
- Antes de activar cualquier regla que permita a un agente comenzar a trabajar sin que una persona escriba un prompt.
- Al agregar una regla a un conjunto existente (una nueva regla puede cerrar un bucle que antes estaba abierto).
- Cuando un agente en segundo plano se ejecutó mucho más, o mucho menos, de lo esperado.
- Al elegir ventanas de debounce, límites de paralelismo y topes diarios, y quieres números en lugar de suposiciones.
- Cuando un revisor pregunta "¿qué pasa si este repositorio recibe 50 issues en una hora?"
No para: verificar lo que el agente hace dentro de una ejecución (eso es una pregunta de prompt o habilidad), ni hacer pruebas de carga de la fuente de eventos en sí misma.
La hoja de reglas
Todo se traduce a rules.json. Mantén la traducción honesta: una regla de hoja por regla real, y escribe las protecciones que realmente existen, no las que planeas agregar.
{
"tz": "Asia/Shanghai",
"run_s": 180,
"external_sources": ["github"],
"budget": {"runs_per_day": 40, "cost_per_run": 0.15, "cost_per_day": 6},
"rules": [
{
"name": "triage-new-issue",
"on": {"source": "github", "types": ["issues.opened", "issues.edited"]},
"where": {"repo": "me/app"},
"key": "subject",
"debounce_s": 300,
"parallel": 1,
"max_per_day": 20,
"unless_actor": ["my-bot"],
"unless_label": "triaged",
"emits": [{"source": "github", "types": ["issues.labeled"], "actor": "my-bot", "labels": ["triaged"]}],
"sensitive": [],
"asks": false
},
{"name": "morning-digest", "on": {"cron": "0 9 * * 1-5"}, "max_per_day": 1}
]
}
| Campo | Significado |
|---|---|
on.source + on.types | Qué eventos inician la regla. Omite types para coincidir con cada tipo de esa fuente. |
on.cron | Una línea cron de cinco campos en la zona horaria de la hoja. Una regla tiene source o cron. |
where | Campos del evento que deben ser iguales a un valor (o estar en una lista). |
key | Campo del evento que nombra el asunto (número de issue, ID de ticket, URL). Se usa para fusionar repeticiones. |
debounce_s | Espera este tiempo tras el último evento para un asunto, luego ejecuta una vez. |
parallel | Cuántas ejecuciones de esta regla pueden estar activas a la vez. Las ejecuciones extra esperan. |
max_per_day | Límite máximo por día local. Las ejecuciones extra se descartan. |
run_s | Duración típica de ejecución, usado para colas. Recurre al valor de la hoja, luego a 120. |
unless_actor, unless_label | Protecciones: saltar eventos hechos por estos actores, o que tengan esta etiqueta. |
emits | Eventos que la ejecución misma produce (comentarios, etiquetas, cambios de estado, eventos posteriores). Incluye el actor y las etiquetas que llevarán. |
sensitive + asks | Acciones que necesitan una persona (eliminar, gastar, reiniciar, enviar). asks: true significa que la ejecución se detiene y pregunta primero. |
external_sources | Fuentes alimentadas por el mundo exterior. Cualquier otra cosa debe ser emitida por una regla. |
budget | Opcional. Ejecuciones por día, costo por ejecución y costo por día usados para el veredicto. |
Paso a paso
- Recopila las reglas reales. Lee la configuración real, no la descripción de ella. Archivos de reglas del hub de agentes, rutinas guardadas, bloques
on:de flujos de trabajo CI, líneas crontab, pantallas de automatización sin código. Toma nota de cualquier cosa que escriba de vuelta a una fuente que otra regla observa. - Traduce a
rules.json. Comienza conpython3 scripts/rehearse.py new rehearsal/. Por cada regla, haz tres preguntas y completa los campos: qué la despierta, qué escribe una ejecución de vuelta, y qué la detiene de despertarse por sus propias escrituras. Si la respuesta a la tercera es "nada", omite la protección. El ensayo está diseñado para detectar eso. - Lintea.
python3 scripts/rehearse.py lint rehearsal/rules.json. Corrige cada HOLD antes de reproducir; los elementos REVIEW pueden esperar a los números. - Recopila eventos. Lo mejor: exporta un día real ocupado de la fuente (registro de entregas de webhook, línea de tiempo de issues, registro de auditoría de helpdesk) en
events.jsonl, un objeto JSON por línea con al menost,source,type, más los campos que usan tukeyywhere. Si no hay historial, escribe un breve estallido sintético a propósito: diez ediciones en tres minutos, dos asuntos a la vez, un evento hecho por tu propio bot. - Reproduce durante días completos.
python3 scripts/rehearse.py replay rehearsal/rules.json rehearsal/events.jsonl --from 2026-11-02T00:00:00Z --to 2026-11-03T00:00:00Z --md rehearsal/report.md. Pasa--from/--topara que los números por día no se inflen por una ventana corta. - Lee la línea de tiempo, no solo el veredicto. Busca ejecuciones en el mismo asunto con minutos de diferencia (debounce faltante o corto), cadenas con profundidad creciente (bucles), esperas largas (paralelismo demasiado bajo), descartes (tope demasiado bajo para trabajo real, o señal de que la regla se dispara por ruido).
- Ajusta y repite. Cambia una perilla a la vez y reproduce los mismos eventos, para que la diferencia se deba claramente a ese cambio.
- Decide. ARMA solo cuando la última reproducción sea ARM en un día realista. Guarda el
rules.jsonfinal, el archivo de eventos yreport.mdjuntos como el registro de por qué se activaron las reglas.
Veredictos
- ARM: sin hallazgos HOLD o REVIEW. Seguro de activar tal como está escrito.
- TRIM: algo desperdicia ejecuciones o puede saltar trabajo (sin clave, sin tope, superposición, el tope descartó eventos reales, cerca del presupuesto). Activar solo después de recortar, o con una razón por escrito.
- HOLD: no activar. Auto-bucle, ciclo sin protección, una cadena que superó la profundidad 5, una acción sensible sin preguntar, sobrepresupuesto, o una línea cron rota.
El código de salida sigue al veredicto (0, 1, 2; 3 significa entrada incorrecta), por lo que un ensayo puede controlar un cambio de configuración en CI.
Ejemplo
Un equipo de soporte quiere que un agente responda tickets de helpdesk. Su hoja (en examples/loopy/) tiene una regla de respuesta que publica una réplica, que actualiza el ticket, que coincide con la misma regla. Una reproducción de un día normal da:
HOLD self-loop answer-ticket: la salida propia de la regla coincide con su propio disparador sin protección unless_actor/unless_label
HOLD runaway answer-ticket: la cadena siguió disparándose más allá de la profundidad 5 (3 cortes)
HOLD sensitive-unasked refund-check: declara acciones sensibles ['refund'] pero 'asks' no es verdadero
HOLD over-runs-budget -: 100.0/día contra un presupuesto de 30/día
REVIEW cron-tight inbox-poll: hasta 720 disparos/día
VEREDICTO HOLD
Tres actualizaciones de ticket se convirtieron en 18 ejecuciones de respuesta. La solución es pequeña: agrega "unless_actor": ["support-bot"] a la regla de respuesta, establece "asks": true en la regla de reembolso, y reemplaza la encuesta de dos minutos con un script que emite inbox.changed solo cuando la bandeja de entrada realmente cambió (ver examples/loopy/rules.fixed.json). La siguiente reproducción del mismo día baja de 100 ejecuciones a 4 y el veredicto pasa a TRIM. El único hallazgo restante es que la respuesta y la verificación de reembolso ven un ticket de reembolso, lo cual es intencionado aquí, así que se acepta con una razón escrita de una línea al lado del informe.
Consejos
- La mejor protección contra bucles única es una condición que se vuelve falsa después de la ejecución: una etiqueta que la ejecución agrega y la regla excluye, o un estado que la regla ya no coincide.
- Los bucles entre sistemas son los más fáciles de pasar por alto. Si el agente escribe en el sistema A y el sistema A se sincroniza con B, lista el evento sincronizado bajo
emitscon el actor que mostrará. - El debounce agrega retraso por diseño. Elige la ventana según cuánto tiempo tarda una persona en terminar un estallido de ediciones, y mantenla corta donde alguien espera una respuesta.
- El debounce no es deduplicación de acciones. Para cualquier cosa irreversible, la ejecución en sí misma debería verificar "¿ya hecho?" antes de actuar.
- Un tope diario es un fusible, no un plan. Si la reproducción de un día normal lo alcanza, la regla es demasiado amplia.
- La encuesta cada pocos minutos suele ser más barata como un pequeño script que compara el estado y emite un evento al cambiar; el agente solo se despierta cuando hay algo nuevo.
- Vuelve a ejecutar el ensayo cada vez que se agregue una regla. La mayoría de los bucles son creados por la segunda regla, no por la primera.