Como funciona um agente em loop

Nesta página

No post anterior, contei como meu MacBook velho virou o servidor que guarda o contexto dos meus agentes. Com essa metade funcionando, quero experimentar a outra: o que faz um agente continuar trabalhando por conta própria ali, depois que eu saio? A aula How always-on agents work, de Lee Robinson (@leerob), apresentada em Stanford CS146S em 8 de outubro de 2026, dá uma base para esse próximo passo.

Meu outro Mac já dormiu no meio de uma tarefa. Do celular, pedi ao agente no servidor para descobrir onde ela tinha parado. Ele encontrou o histórico na memória compartilhada e conferiu no GitHub o que já tinha sido enviado.

No vídeo: 2:35 (como chegamos aqui); 6:13 (modelos); 10:06 (arquitetura); 18:07 (harness); 27:26 (contexto); 34:26 (próximos passos).

A arquitetura vem da aula. Os relatos do MacBook vêm das experiências que contei no post anterior; os outros exemplos são hipotéticos. O plano final é uma proposta minha.

Por que o agente não roda na máquina que você fecha

Se a tarefa depende do processo aberto no terminal, suspender o computador interrompe o trabalho. Meu MacBook resolve essa parte porque funciona como servidor em vez de laptop: fica de tampa fechada, na tomada, ligado o dia inteiro, com o modo de suspensão desativado. A aula começa pelo loop da semana 1:

messages = [system_prompt, user_message]
while True:
    reply = llm(messages, tools=TOOLS)
    messages.append(reply)
    if not reply.tool_calls:
        print(reply.text); break
    for call in reply.tool_calls:
        messages.append(run_tool(call))

O modelo pede ferramentas, recebe os resultados e decide o próximo passo. Para continuar depois que você sai, esse loop roda no servidor, que salva cada passo. O aplicativo recebe mensagens e mostra o progresso. A VM, o computador do agente na nuvem, executa as ferramentas que precisam de uma máquina. Lee explica essa divisão no vídeo (a partir de 10:06).

Arquitetura do agente sempre ligadoOs apps mandam eventos ao servidor, onde a entrada, a fila de cada conversa e o loop do agente salvam cada passo no estado salvo; o loop responde por SendToUser, o estado salvo manda atualizações ao vivo para os apps e as chamadas de ferramentas vão para a VM do agente, com executor de comandos, servidor de arquivos e um desktop com Chrome por agente.SERVIDORCOMPUTADOR DO AGENTE · VMSendToUserATUALIZAÇÕESCHAMADAS DE FERRAMENTASApps e canaisdesktop · web · Slack · emailEntradavalida, roteiaFilauma por conversaLoop do agentechama o modeloEstado salvobanco + arquivosExecutor de comandosshell · arquivos · screenshots · cliquesServidor de arquivosarquivos · conexões · visão ao vivoDesktop + Chromeuma tela virtual por agente
Três partes: os apps, o servidor que roda o agente e o computador do agente. O estado fica no servidor, então a VM sempre pode ser reconstruída. fonte: Lee Robinson, CS146S

Sempre ligado também permite parar quando não há trabalho. O processo encerra após 2 minutos ocioso; um novo evento carrega o estado e inicia outro turno. Os números usados neste texto vêm dos slides da aula.

Imagine pedir uma pesquisa antes de dormir. Você fecha o laptop, mas o servidor continua a tarefa. De manhã, abre a conversa no celular e encontra o progresso salvo, sem precisar manter o terminal aberto durante a noite.

A máquina precisa poder ser reconstruída

Um patch de segurança pode exigir reconstruir a VM. Se a conversa e o progresso existirem só nela, a manutenção vira perda de trabalho. A orientação da aula é direta: “Salve tudo no servidor. A VM sempre pode ser reconstruída.”

O servidor guarda conversas, memória, rotinas e skills. Cada usuário tem uma microVM Firecracker com Linux e navegador. Seus agentes dividem a máquina, cada um com sua tela virtual e seu Chrome.

Quando fica ociosa, a VM sincroniza os arquivos com o armazenamento na nuvem, salva um snapshot da memória e do disco e para. Nunca dorme no meio de um turno. Uma chamada de ferramenta que precise dela restaura o snapshot, incluindo ferramentas instaladas e sessões do navegador.

O que fica ligado entre eventosUma mensagem ou uma rotina inicia um turno que salva cada passo; a VM só acorda se uma ferramenta precisar dela e, depois, nada roda: o processo para após 2 minutos ocioso e a VM dorme como snapshot.SIMNÃOSua mensagemRotina 8:00Turno rodasalva cada passoPrecisa da VM?VM acordavolta do snapshotVM não acordanenhuma ferramenta pediuNada rodaprocesso: 2 min ocioso · VM: snapshot
Entre um evento e outro, nada roda. Um turno só acorda a VM quando uma ferramenta precisa dela; uma rotina que não usa o computador deixa a VM dormindo. fonte: Lee Robinson, CS146S

Suponha que o agente tenha preparado uma planilha e a máquina durma. Ao pedir uma alteração, você provoca uma nova chamada de ferramenta. A VM acorda com os arquivos e o navegador preservados; o servidor mantém a conversa.

Mensagens rápidas precisam virar um pedido só

Executar cada mensagem separadamente cria concorrência sobre o mesmo pedido. O agente pode agir antes de receber a correção seguinte.

Na aula, um roteador elimina eventos duplicados e escolhe a conversa. Cada conversa tem sua fila e executa um turno por vez. Mensagens rápidas entram em lote. Eventos de Slack, grupos, rotinas e helpers aguardam na fila, mas uma mensagem sua no chat individual pode interromper ou parar o turno atual.

Gatilhos: roteador e fila da conversaMensagens suas, do Slack e de ligações, rotinas e webhooks, outros agentes e trabalho pronto ou check-ins passam por um roteador que descarta duplicatas e escolhe a conversa, depois esperam na fila dessa conversa, um turno por vez, exceto sua mensagem no chat individual, que fura a fila.FILA · UM TURNO POR VEZVAI PRO FIMFURA A FILAVocê, em qualquer appSlack, ligação, reuniãoRotina ou webhookOutro agenteTrabalho feito, check-inRoteadordescartaduplicatas,escolhe aconversahelperprontorotina8:00lote×3 msgsvocê1:1Um turno
Todo gatilho passa pelo roteador e entra na fila da sua conversa. Mensagens rápidas esperam como um lote; sua mensagem no chat individual vai para a frente. fonte: Lee Robinson, CS146S

Imagine um pedido pelo Slack: incluir alguém numa reunião, depois acrescentar outra pessoa e, por último, agradecer. Essas mensagens podem virar um único turno. Se você mudar o dia pelo chat individual, com “na verdade, faz na quinta”, a correção entra na frente e redireciona o trabalho em andamento.

Como retomar sem repetir um pagamento

Reiniciar um job do começo depois de uma queda pode repetir uma ação já concluída. No MacBook, testei reiniciar a máquina e derrubar o Wi-Fi; os serviços voltaram sozinhos. Retomar cada etapa de uma tarefa é o teste que quero fazer agora. Em um pagamento, repetir uma etapa pode significar outra cobrança.

A aula usa Temporal para executar workflows duráveis: sequências de trabalho com cada passo registrado. Outro servidor consegue retomar do último passo salvo. As etapas também precisam ser idempotentes, isto é, uma repetição deve manter o mesmo resultado, sem criar outra ação. O motor oferece timers, cancelamento e verificações de saúde (health checks); cada helper tem seu próprio workflow.

Fila de jobs vs. workflow durávelDepois de uma queda no passo 3, a fila de jobs reinicia e roda os passos 1 e 2 de novo, enquanto o workflow durável, que salvou os passos 1 e 2, retoma no passo 3.FILA DE JOBSREINICIA123✕ queda1de novo2de novo3WORKFLOW DURÁVELRETOMA1salvo2salvo3✕ queda123
A mesma queda no passo 3. A fila de jobs repete os passos já feitos; o workflow durável retoma de onde parou. Na aula, o motor é o Temporal, e os passos precisam ser idempotentes. fonte: Lee Robinson, CS146S

Num cenário hipotético, uma compra já autorizada é concluída e o servidor cai antes de salvar o resultado. Ao retomar, o passo de pagamento pode ser tentado novamente. A idempotência precisa reconhecer aquela mesma operação e impedir outra cobrança.

Trocar de aparelho não pode duplicar a conversa

Uma conexão instável pode fazer o aplicativo reenviar a mensagem. Ao mesmo tempo, o celular e o computador precisam mostrar a mesma conversa.

O cliente exibe a mensagem e a salva localmente com um identificador único. O servidor ignora identificadores já recebidos. Outros aplicativos recebem um aviso de mudança e buscam os dados no banco. Os updates têm uma sequência; uma lacuna faz o cliente recarregar os dados.

Imagine enviar um pedido no celular e perder a conexão antes da confirmação. O aplicativo tenta de novo com o mesmo identificador, sem criar outro pedido. Quando você abre o computador, ele busca a conversa salva. Perder um aviso de atualização não apaga a mensagem.

Ferramentas demais ocupam o espaço da tarefa

Carregar a definição completa de toda ferramenta deixa menos contexto para entender o pedido. Muitas dessas ferramentas nem serão usadas naquele turno.

A solução da aula é levar só os nomes no prompt e carregar descrição e schema sob demanda. Essa parte do harness aparece no vídeo (a partir de 18:07). Cada definição fica abaixo de 4.000 caracteres. O conjunto permanece estável: uma ferramenta indisponível devolve um erro claro, em vez de desaparecer. Salvar estado merece uma ferramenta própria; se o modelo passa 90% do tempo repetindo um padrão de shell, esse padrão deve virar ferramenta.

A escolha também segue uma ordem de custo crescente: contexto e memória, API do aplicativo, busca na web, navegador logado, desktop e pergunta ao usuário. Um conector quebrado exige avisar e perguntar, sem contornar a falha pelo navegador.

Escada do mais barato ao mais caroSeis degraus do mais barato ao mais caro (o que o agente já sabe, a API do app, busca na web, o navegador logado, o desktop inteiro e perguntar a você), em que um conector quebrado leva direto a perguntar a você.↓ mais lento, caro e arriscadoconectorquebrado?O que ele já sabecontexto · memóriaAPI do appconector ou MCPBusca na webNavegador logadoDesktop inteiroPerguntar a você123456
Cada degrau é mais lento, mais caro e mais arriscado que o de cima. Conector quebrado significa avisar e perguntar, nunca contornar pelo navegador. fonte: Lee Robinson, CS146S

Suponha que você peça os compromissos de amanhã. O agente carrega a ferramenta de calendário e consulta a API. Se o conector falhar, informa o problema e pede orientação. Ele não abre o navegador por conta própria para tentar recuperar acesso.

O agente precisa saber ficar quieto

Exibir toda resposta do modelo enche o chat de comentários sobre cada tentativa e texto privado. Alguns eventos nem trazem algo útil para dizer.

No harness da aula, o código que organiza o uso do modelo e das ferramentas, SendToUser é a única saída visível. Texto comum e raciocínio ficam privados. O agente pode terminar um turno em silêncio. Quando precisa falar, a ferramenta envia texto, arquivos ou controles para uma decisão; a última mensagem pode encerrar o turno sem outra chamada ao modelo.

No meu servidor, o monitoramento já só me avisa no Telegram quando algo muda. Desliguei o Wi-Fi para testar, e o alerta chegou. Para o loop, quero testar esse mesmo critério: uma rotina autorizada que verifica um documento termina em silêncio se ele continuar igual e avisa quando houver uma mudança relevante.

Delegar mantém a conversa disponível

Uma tarefa longa pode ocupar o agente principal e encher o contexto com screenshots. Você ainda precisa conseguir corrigir o pedido.

Lee separa o principal dos helpers. O principal entrega uma descrição completa da tarefa e recebe um relatório curto. Uma lista acompanha os pedidos, e ele pode consultar, orientar ou parar um helper. Cada helper executa seu próprio workflow durável.

No meu time de agentes, um orquestrador classifica o pedido e o encaminha ao bot fixo daquele projeto. Esse bot planeja o trabalho, fica responsável pelo PR e me devolve o resultado. Na aula, o agente principal passa uma tarefa a um helper e recebe um relatório de volta; no meu time, o bot continua com o projeto e mantém o contexto dele entre tarefas. Quando peço para corrigir um bug no site, por exemplo, o orquestrador passa o pedido ao bot do site, que já conhece o repositório.

O diagrama abaixo mostra a divisão entre o agente principal e os helpers na aula.

Agente principal e helpersVocê conversa com o agente principal, que manda descrições completas de tarefas aos helpers geral, de uso de computador e de vídeo e a um helper de código na nuvem que trabalha numa VM só dele, separada da sua, e recebe relatórios curtos de volta.TAREFA COMPLETARELATÓRIO CURTOVocêAgente principalorquestrador · responde rápidoGeralshell · web · conectoresUso de computadoruma tarefa por vezVídeoassiste e revisa arquivosAgente de código na nuvemmuda código na própria VM
O agente principal continua falando com você enquanto os helpers trabalham. Ele pode consultar um helper, mandar mensagem a ele ou pará-lo. fonte: Lee Robinson, CS146S

O helper de computador concentra os cliques e screenshots. Na web, pode ler a estrutura da página; para ações em pixels, a aula usa uma tela de 1280x800. Quando precisa de intervenção humana, entrega o controle. Já o cloud coding agent faz mudanças de código numa VM só dele, separada da VM do usuário.

Imagine pedir a revisão de um vídeo e depois perguntar sobre o prazo de outra tarefa. O helper cuida do vídeo, enquanto o principal continua disponível no chat. Ao terminar, devolve um relatório curto, sem colocar todo o material analisado na conversa principal.

Cache: o detalhe que define o custo

Mudanças pequenas no prompt podem impedir o uso do cache e aumentar o custo. Até alterar a ordem das ferramentas pode ter esse efeito. A engenharia de contexto começa no vídeo (a partir de 27:26).

Na aula, o prefixo do prompt segue uma ordem fixa e fica idêntico até o próximo resumo. O histórico cresce no final. Horário e notas sobre mudanças entram na mensagem nova. Se um deploy altera a descrição de uma ferramenta, a conversa em andamento recebe uma nota, sem substituir o texto antigo. Hashes por seção ajudam a localizar mudanças; Lee trata cache miss como bug.

Ordem do prompt para o cacheO prompt é montado numa ordem fixa: definições de ferramentas, prompt de sistema e primeira mensagem ficam iguais até o próximo resumo, o histórico só cresce no final e só a mensagem mais nova é nova a cada chamada.IGUAL ATÉ O PRÓXIMO RESUMODefinições de ferramentasmesmo conjunto todo turnohashPrompt de sistemasnapshot de memória e rotinashashPrimeira mensagemambiente + nomes das ferramentashashHISTÓRICO · SÓ CRESCE NO FINALTurnos anterioresentra no prefixo da próximahashNOVO A CADA CHAMADAMensagem mais novahora · notas de mudança · mensagem
Só a mensagem mais nova muda entre chamadas, então tudo acima dela pode vir do cache. Cada seção tem um hash próprio para mostrar o que mudou. fonte: Lee Robinson, CS146S

A compactação aproveita essa estabilidade. No caso apresentado, o sistema espera cerca de 10 minutos depois de um turno que termina com o prompt acima de 100 mil tokens e resume enquanto o cache ainda está quente, numa janela de 10 a 60 minutos. Preserva as últimas mensagens ao usuário palavra por palavra. Se um trabalho em background estiver prestes a acordar o agente, pula essa compactação.

Suponha que uma pesquisa longa termine e você saia do chat. O sistema pode resumir durante a pausa usando o prefixo em cache. Seu próximo pedido começa com o resumo menor. Há perda de informação na compressão, então a qualidade desse resumo continua sendo um problema a resolver.

Memória útil precisa caber no prompt

Guardar tudo não garante encontrar uma preferência quando ela importa. No servidor, instruo os agentes a tratar a memória como histórico e conferir o estado atual no repositório e no histórico de alterações. Foi assim que o agente conferiu o trabalho do meu outro Mac. Carregar tudo no prompt também consome o espaço da tarefa atual.

A aula divide a memória em camadas. O perfil, sempre no prompt, comporta até 100 fatos em 4.000 caracteres. O log traz eventos datados, com os 30 mais relevantes e recentes no contexto. Notas guardam detalhes de curto prazo; o restante fica disponível por busca.

Depois de cada turno, uma revisão adiciona fatos e substitui informações antigas. Ela só pode remover fatos que recebeu para examinar. A memória entra como informação, nunca como instrução com autoridade para comandar ações.

Imagine dizer que prefere reuniões à tarde. Essa preferência vai para o perfil e acompanha os próximos pedidos. Quando você precisar recuperar o motivo de uma reunião antiga, o agente busca o histórico específico, sem carregar todas as conversas para sugerir um horário.

Um email recebido não pode autorizar uma ação

Um email de phishing pode mandar o agente enviar dinheiro e se apresentar como uma instrução urgente. Já limito as permissões dos meus agentes ao que cada um precisa. O loop também precisa preservar a origem do texto para não confundir conteúdo recebido com um pedido meu.

A aula marca textos de email e webhook como não confiáveis. Antes de uma ação arriscada, outro modelo faz o auto-review. Se o modelo bloquear a ação, o usuário recebe um pedido de aprovação. Se o usuário não responder em 15 segundos, a ação continua bloqueada.

Envios irreversíveis passam por rascunho e confirmação. O agente não manda mensagens a outras pessoas sem você pedir, e rotinas novas precisam do seu OK. Segredos ficam em variáveis de ambiente, fora do modelo.

Suponha que um email peça uma transferência para uma conta nova. O agente deve tratar a solicitação como conteúdo externo, sem tomá-la como autorização sua. Qualquer tentativa de ação arriscada passa pela revisão. Se o modelo bloquear a ação, você recebe um pedido de aprovação; sem resposta sua em 15 segundos, ela continua bloqueada. Se você pedir uma resposta por email, verá o rascunho antes de enviar.

Minha proposta: o que eu construiria primeiro

Na parte final da aula (a partir de 34:26), Lee propõe “Delete the product”: construir o mínimo em volta do modelo e remover o que ele não precisa mais.

Este plano é meu, separado da aula do Lee. Eu começaria pela continuidade: uma tarefa interrompida obriga o usuário a descobrir o que já foi feito.

  1. Colocar o loop no servidor e salvar conversas e progresso em Postgres. Os aplicativos buscariam o estado salvo quando recebessem um aviso de atualização.
  2. Usar Temporal para retomar tarefas, com etapas idempotentes. Organizar a fila por conversa, agrupar mensagens rápidas e dar prioridade às correções no chat individual.
  3. Limitar a saída visível a SendToUser. Carregar ferramentas conforme a necessidade e manter o prefixo estável, com memória curta e busca para detalhes antigos.
  4. Começar o computador do agente com um container isolado por usuário. Adicionar helpers às tarefas longas e evoluir para snapshots quando o custo das máquinas ociosas pesar.
  5. Colocar revisão automática antes das ações arriscadas e confirmação nos envios irreversíveis. Transformar cada falha real em um teste que ajude a corrigir o comportamento.

Meu primeiro cenário seria uma pesquisa que continua com o laptop fechado e aceita uma correção pelo celular. Depois eu ampliaria o acesso do agente a ações com consequências externas.

Agora tenho critérios para avaliar se uma tarefa pode continuar depois que eu saio da conversa. Ainda quero testar a retomada no ponto certo e entender como os agentes vão coordenar o trabalho entre si. No próximo capítulo, vou tratar da oficina de agentes, onde pretendo colocar vários deles rodando em loops juntos.