MÓDULO 5.5

⏰ Cron, schedules e watchers

Tempo (cron), evento (webhook), filesystem (watcher). Como dar vida automática ao Agentic OS.

6
Tópicos
35
Minutos
Médio
Nível
Prático
Tipo
1

⏰ Cron — gatilho de tempo

Cron é o mecanismo mais robusto de automação: executa um comando em horário predefinido, independente de qualquer evento externo. Marco recebe o brief financeiro às 6h de segunda porque o sistema lembra — não porque ele pediu.

💻 Exemplos de cron para agentes

# crontab -e

# Brief financeiro toda segunda 6h
0 6 * * 1 /usr/bin/claude --print "Gera brief financeiro semanal" \
  --no-interactive >> /tmp/brief-$(date +%Y%m%d).md

# Monitoramento de concorrentes toda sexta 18h
0 18 * * 5 /home/user/.local/bin/run-agent.sh competitive-monitor

# Silver Platter update todo dia 2h
0 2 * * * python3 /workspace/scripts/update-silver-platter.py

# Relatório mensal: 1o dia do mes 7h
0 7 1 * * claude --print "Gera relatorio mensal completo" \
  --context /workspace/context/ >> /workspace/reports/monthly-$(date +%Y%m).md

💡 systemd timer vs. crontab

Para 2026, prefira systemd user timers sobre crontab em Linux: logs estruturados via journalctl, retry automático em falha, melhor controle de dependências. crontab é mais simples para começar.

2

📡 Webhook — gatilho de evento

Quando o gatilho é externo, webhook é mais natural que polling: o sistema externo notifica o agente quando algo acontece. Stripe paga → CFO Bot atualiza o relatório. GitHub merge → Builder roda testes.

💻 Receptor de webhook simples

# webhook-receiver.py (Flask)
from flask import Flask, request
import subprocess, hmac, hashlib

app = Flask(__name__)

@app.route('/webhook/stripe', methods=['POST'])
def stripe_webhook():
    # Valida assinatura
    sig = request.headers.get('Stripe-Signature')
    if not validate_stripe_sig(request.data, sig):
        return 'Invalid', 401

    event = request.json
    if event['type'] == 'payment_intent.succeeded':
        amount = event['data']['object']['amount']
        # Dispara agente assincronamente
        subprocess.Popen([
            'claude', '--print',
            f'Pagamento de R${amount/100:.2f} recebido. Atualiza relatorio.',
            '--no-interactive'
        ])

    return 'OK', 200

Boas práticas de webhook

  • Validar assinatura — HMAC-SHA256. Sem isso, qualquer um dispara seu agente.
  • Responder 200 rápido — processar em background. Stripe timeout em 30s.
  • Idempotência — webhook pode chegar duplicado. Deduplique por event_id.
  • Fila — em produção, use fila (Redis, SQS) entre webhook e agente.
3

📁 File watcher — filesystem como evento

Drop folders são a interface mais natural para processar documentos: usuário solta arquivo em /intake/ e agente processa automaticamente. Sem UI, sem upload manual, sem lembrar de "enviar para o bot".

💻 File watcher com fswatch

#!/bin/bash
# watch-intake.sh — monitora /intake/ e processa novos PDFs

INTAKE_DIR="/workspace/intake"
PROCESSED_DIR="/workspace/processed"

# fswatch dispara script para cada novo arquivo
fswatch -0 --event Created "$INTAKE_DIR" | while IFS= read -r -d "" file; do
  # So processa PDFs
  if [[ "$file" == *.pdf ]]; then
    echo "Processando: $file"

    # Chama agente com o arquivo
    claude --print "Analisa este PDF e extrai dados chave: $(cat "$file")" \
      --no-interactive > "${PROCESSED_DIR}/$(basename "$file" .pdf).md"

    # Move para processed
    mv "$file" "$PROCESSED_DIR/"
    echo "Concluido: $file"
  fi
done

💡 Debounce — problema do grande arquivo

Sem debounce, watcher dispara durante a escrita do arquivo — processando arquivo incompleto. Adicione: sleep 2 após detectar criação, ou use inotifywait --event close_write que espera o arquivo ser fechado.

4

⚙️ systemd user services — daemon agêntico

Para agentes always-on, systemd user service é superior a script em background: reinicia em falha, logs estruturados no journalctl, controle fino de recursos, integração com o sistema.

💻 Unit file para agente watcher

# ~/.config/systemd/user/intake-watcher.service
[Unit]
Description=Agente de processamento de intake
After=network.target

[Service]
Type=simple
ExecStart=/home/user/workspace/scripts/watch-intake.sh
Restart=on-failure
RestartSec=10s
StandardOutput=journal
StandardError=journal

# Limites de recursos
MemoryMax=512M
CPUQuota=50%

[Install]
WantedBy=default.target

# Ativar:
# systemctl --user enable intake-watcher
# systemctl --user start intake-watcher
# journalctl --user -u intake-watcher -f

Vantagens do systemd sobre nohup/screen

  • Restart automático — sem monitoramento manual de processo morto.
  • Logs persistentes — journalctl com filtros, timestamps, níveis.
  • Controle de recursos — MemoryMax evita OOM killer.
  • Dependências — After=network.target garante ordem.
5

🧊 Idempotência — não duplicar trabalho

Cron pode disparar duas vezes (clock drift, restart). Webhook pode chegar duplicado (retry do cliente). Idempotência garante que rodar N vezes produz o mesmo resultado de 1 vez — sem duplicação de trabalho, sem efeitos colaterais extras.

💻 Padrões de idempotência

#!/bin/bash
# Padrao 1: marker file (cron diario)
MARKER="/tmp/brief-$(date +%Y%m%d).done"
if [ -f "$MARKER" ]; then
  echo "Brief ja gerado hoje, pulando."
  exit 0
fi
# ... gera brief ...
touch "$MARKER"

# Padrao 2: lock file (previne execucao paralela)
LOCKFILE="/tmp/intake-processor.lock"
exec 9>"$LOCKFILE"
if ! flock -n 9; then
  echo "Outra instancia rodando, saindo."
  exit 1
fi
# ... processa ...
# lock liberado automaticamente ao sair

# Padrao 3: deduplica por ID (webhook)
PROCESSED_IDS_FILE="/var/lib/agent/processed-ids"
EVENT_ID=$(echo "$PAYLOAD" | jq -r '.id')
if grep -q "^$EVENT_ID$" "$PROCESSED_IDS_FILE" 2>/dev/null; then
  exit 0  # Ja processado
fi
echo "$EVENT_ID" >> "$PROCESSED_IDS_FILE"

⚠️ Consequências de não implementar

Marco recebe o brief financeiro duplicado às 6h de segunda. Dois relatórios diferentes, com dados ligeiramente distintos (coletados em timestamps diferentes), causam confusão nas reuniões. Parece trivial até acontecer com dados críticos.

6

📲 Notificações — Telegram, Slack, e-mail

Sistema sem notificação é sistema invisível. O agente precisa de um canal para avisar o humano: quando terminou, quando falhou, quando encontrou algo que exige decisão. A escolha do canal define a urgência.

💻 Notificador multi-canal

#!/bin/bash
# notify.sh - envia para canal correto por urgencia
# Uso: notify.sh "mensagem" [urgencia: low|medium|high]

MESSAGE="$1"
URGENCY="${2:-low}"

case "$URGENCY" in
  high)
    # Telegram para urgente (chega no celular)
    curl -s -X POST "https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage" \
      -d "chat_id=$TELEGRAM_CHAT_ID&text=URGENTE: $MESSAGE"
    ;;
  medium)
    # Slack para medium
    curl -s -X POST "$SLACK_WEBHOOK" \
      -d "{\"text\": \"$MESSAGE\"}"
    ;;
  low)
    # E-mail para baixa urgencia
    echo "$MESSAGE" | mail -s "Agentic OS Update" "$NOTIFY_EMAIL"
    ;;
esac

💡 Rate limiting de notificações

Sistema que notifica demais treina o humano a ignorar. Defina: máximo N notificações por hora por canal. Use digest para agregar mensagens de baixa urgência. Reserve alta urgência para situações que realmente precisam de resposta imediata.

📋 Resumo do Módulo

Cron — gatilho de tempo mais robusto. crontab ou systemd timer. Brief às 6h.
Webhook — gatilho reativo. Valida assinatura, responde rápido, processa em background.
File watcher — drop folder. fswatch/inotifywait. Debounce com close_write.
systemd service — daemon agêntico com restart, logs, controle de recursos.
Idempotência — marker file, lock, deduplica por ID. Rodar 2x = resultado de 1x.
Notificações — canal por urgência: Telegram (alta), Slack (média), e-mail (baixa).

Próximo Módulo:

5.6 — Segurança aplicada pela arquitetura