Skip to main content

Heartbeats

Heartbeats — agentes proativos com agenda, status e custo Heartbeats são agentes proativos. Um heartbeat é uma configuração que acorda um agente em uma agenda para que ele faça triagem de trabalho sem ser pedido. Em vez de você lembrar de perguntar ao Atlas “tem PR parado?” toda manhã, o Atlas acorda a cada 4 horas, verifica e age só se houver algo a fazer.

O Modelo Mental

Uma rotina é sistemática — o mesmo script roda todo dia às 7h. Um heartbeat é proativo — o agente acorda, observa e decide se deve agir. Cada execução de heartbeat responde a uma única pergunta:
Dado o estado atual do meu domínio, devo trabalhar agora ou pular?
O agente retorna um veredicto em JSON. Se action: work, o dispatcher deixa o agente executar seus turnos. Se action: skip, a execução termina imediatamente com custo próximo de zero.

Anatomia de um Heartbeat

Heartbeats vivem em config/heartbeats.yaml (fonte da verdade) e são espelhados no banco do dashboard. Cada entrada tem:

Início Rápido

1. Crie um heartbeat

No Claude Code, use a skill create-heartbeat:
“Crie um heartbeat de 4 horas para atlas-project que triagem PRs e issues parados.”
A skill conduz pelos campos, deixa enabled: false por padrão e chama POST /api/heartbeats. A entrada aparece na página Heartbeats do dashboard como desabilitada. Alternativamente, edite config/heartbeats.yaml diretamente e chame POST /api/heartbeats/reindex para espelhar no banco.

2. Dry-run antes de habilitar

No dashboard, clique em Run now no card (ou chame POST /api/heartbeats/{id}/run). Isso dispara uma execução manual única, independente do relógio do interval. Inspecione o resultado:
  • O agente retornou JSON válido?
  • A decisão foi razoável?
  • Ficou dentro de max_turns e timeout_seconds?
Se sim, ative o toggle. O dispatcher pega no próximo tick do interval.

3. Monitore as execuções

Cada execução registra started_at, finished_at, status, tokens_input, tokens_output, cost_usd e o decision_json parseado. A página Heartbeats no dashboard mostra as últimas 10 execuções por card e o custo agregado de 7 dias.

Wake Triggers

Um heartbeat acorda em qualquer um dos triggers listados em wake_triggers:
  • interval — o scheduler dispara a cada interval_seconds. Quase sempre incluído.
  • manual — alguém clica em “Run now” ou chama a API. Permite a operadores debugar.
  • mention — um @agent-slug num comentário de ticket dispara um mention trigger (se o agente tiver um heartbeat habilitado com mention nos wake_triggers). Máximo 3 mentions por comentário.
  • new_task — uma task de goal criada com este agente como assignee_agent acorda o heartbeat.
  • approval_decision — uma solicitação de aprovação foi decidida (feature futura).
Mais triggers = mais responsividade. Menos triggers = custo mais previsível.

Controle de Custo

Como heartbeats queimam tokens em cada execução, coloque guardrails:
  1. Comece com enabled: false. Sempre. Revise a primeira execução manual antes de habilitar.
  2. Use um decision_prompt afiado. Se o agente escreve um parágrafo quando devia escrever JSON, você desperdiça tokens.
  3. Tenda ao skip. Treine o prompt para pular a menos que haja urgência genuína. Um heartbeat de 4 horas que pula 4 das 6 execuções diárias está fazendo seu trabalho.
  4. Mantenha max_turns baixo para heartbeats puramente de triagem (5–10). Só aumente para agentes que podem precisar agir em múltiplos itens.
  5. Acompanhe cost_7d no card do dashboard. Se um heartbeat sobe consistentemente, o prompt provavelmente está muito leniente — aperte os critérios de skip.

Debugando

Um heartbeat não está disparando

  1. Cheque o enabled do heartbeat — está individualmente ligado?
  2. Cheque o log do dispatcher (journalctl -u evo-nexus -f ou os logs do serviço do dashboard) para registro do interval.
  3. Chame POST /api/heartbeats/{id}/run — uma execução manual funciona? Se sim, é problema de scheduler; se não, é problema do agente ou prompt.

Uma execução falhou

Olhe o stderr_tail e stdout_tail da execução. Causas comuns:
  • required_secrets faltando no .env
  • decision_prompt não forçou JSON — o parser não conseguiu extrair o veredicto
  • O agente bateu em max_turns antes de retornar (aumente o limite ou aperte o prompt)
  • O agente bateu em timeout_seconds (chamada de integração foi lenta — aumente o timeout ou adicione um retry dentro do agente)

Drift entre YAML e banco

Se você editou config/heartbeats.yaml à mão e o dashboard ainda mostra o estado antigo:
Reconstrói as linhas do banco a partir do YAML. Seguro de rodar a qualquer momento.

Skills do CLI

Relacionados

  • docs/goals.md — ligar um heartbeat a um goal para injeção de contexto
  • docs/tickets.md@mentions em tickets acordam heartbeats
  • Código: dashboard/backend/heartbeat_dispatcher.py, dashboard/backend/routes/heartbeats.py, dashboard/backend/heartbeat_schema.py