Deixe as tarefas rodarem sozinhas — o agent não bloqueia
"Fire and forget — the agent doesn't block while the command runs."
A dor das chamadas bloqueantes
A ferramenta bash do S02 é síncrona: subprocess.run(..., timeout=120) — se o comando for npm install (uns 90 segundos), o loop inteiro do agent trava por 90 segundos. O usuário fica encarando o terminal sem saber se travou ou está trabalhando.
A solução do s08: dar ao agent uma ferramenta background_run. Ela retorna imediatamente um task_id, e o comando roda em outra thread. O agent continua seu loop e faz outras coisas; quando a bg task termina, empurra o resultado para uma fila de notificações.
def run(self, command: str) -> str: task_id = str(uuid.uuid4())[:8] self.tasks[task_id] = {"status":"running", ...} thread = threading.Thread(target=self._execute, args=(task_id, command), daemon=True) thread.start() return f"Background task {task_id} started" # retorno imediato
Como o resultado volta para o agent?
A chave é uma fila thread-safe: a bg thread insere na fila quando termina; a thread principal drena a fila antes de cada chamada à LLM e injeta as notificações de conclusão nas messages como role user.
def agent_loop(messages): while True: # Drain bg notifications before each LLM call notifs = BG.drain_notifications() if notifs: messages.append({ "role": "user", "content": f"<background-results>{notif_text}</background-results>", }) response = client.messages.create(...) ...
Assim: na rodada N o agent faz spawn de uma bg task; na rodada N+3 essa task termina; na próxima chamada à LLM, o resultado é automaticamente incluído — o modelo vê o bloco <background-results> e entende: "ah, aquela tarefa terminou, posso continuar".
Demonstração da linha do tempo
O widget abaixo simula: a thread principal faz tick uma vez por segundo (cadência do agent loop); você pode fazer spawn de bg tasks a qualquer momento. Observe como as duas linhas se encontram no "ponto de drain".
Quais comandos valem ser mandados para o background?
Nem todo comando deve ir para o background. Dois critérios:
- Duração: comandos de poucos segundos são mais simples de rodar de forma síncrona — manter a fila não compensa.
- Dependência do resultado: se o próximo passo usa imediatamente o resultado (por exemplo,
cat file.txtseguido degrep), background não faz sentido — você vai ter que esperar de qualquer jeito.