Learn to orchestrate multiple AI agents using the patterns from Brandon's production SDLC harness — parallel worktrees, dependency-ordered waves, and ordered merges.
A primeira vez que executei meu harness SDLC e vi oito agentes reescreverem oito módulos de aprendizagem independentes em paralelo — cada um no seu próprio worktree git, nenhum ciente dos outros — o resultado chegou em menos de 45 minutos. Executar o mesmo spec sequencialmente leva cerca de três horas e eventualmente esbarra em limites de contexto.
Esse harness é agora a espinha dorsal de produção de como gerencio grandes reescritas. Ele roda num Mac Mini atrás de um Cloudflare Tunnel, despacha tarefas em ondas ordenadas por dependência e mescla resultados em main em uma sequência controlada. Este módulo é sobre os padrões por trás disso — não como arquitetura abstrata, mas como decisões que tomei após encontrar problemas reais.
Pense em um único agente como um freelancer habilidoso que só pode pegar um projeto por vez. Um sistema multi-agente é uma empresa coordenada: especialistas trabalham em paralelo em problemas diferentes, e um gerente de projeto (o orquestrador) distribui trabalho, aguarda resultados e integra saídas em uma ordem definida.
A mudança fundamental: Você para de perguntar "como faço o agente fazer X?" e começa a perguntar "qual agente é dono de X, e o que ele precisa do orquestrador?" Os limites de propriedade são todo o design.
Três problemas que me empurraram para padrões multi-agentes:
Limites de contexto. Uma grande reescrita de spec tem 10 tarefas. Um agente acumula contexto de cada tarefa que faz, eventualmente degradando em qualidade e atingindo limites de token. Oito agentes paralelos, cada um com escopo de uma tarefa, permanecem focados.
Paralelismo. As tarefas 2 a 8 do meu harness SDLC são independentes (cada uma toca arquivos diferentes). Executá-las sequencialmente significa que a tarefa 3 espera a tarefa 2 terminar mesmo que não compartilhem nada. A execução paralela elimina esse tempo morto.
Isolamento de falhas. Se a tarefa 5 falha, quero tentar novamente apenas a tarefa 5 — não re-executar todo o spec. Agentes que cada um é dono de uma tarefa tornam a lógica de retry trivial.
Antes de continuar lendo: No seu próprio trabalho, nomeie um processo recorrente que tem subtarefas independentes que não compartilham arquivos. Essa é a juntura onde padrões multi-agentes compensam. Se você ainda não consegue nomear um, o restante deste módulo aguçará o que procurar.
Sub-agentes não são threads em segundo plano — eles são sessões Claude separadas, cada uma com uma janela de contexto nova. Eles não sabem nada sobre sua conversa, seu projeto ou um ao outro. O orquestrador é a fonte da verdade; agentes são especialistas que executam exatamente o que lhes é dito e escrevem saídas em locais conhecidos.
Aqui está como a coordenação funciona no meu harness SDLC:
A regra crucial: agentes coordenam apenas através do orquestrador. Eles não conversam diretamente entre si. Suas saídas — commits git e relatórios estruturados — são o único canal de volta ao orquestrador. Tentar ter agentes se comunicando peer-to-peer cria condições de corrida que são quase impossíveis de depurar.
Regra de limite: Cada agente escreve apenas nos arquivos que possui. Se dois agentes precisam modificar o mesmo arquivo, a divisão de tarefa está errada — conserte o limite de propriedade, não os agentes.
Imagine cinco equipes de construção construindo asas separadas do mesmo edifício. Cada equipe trabalha a partir de um trecho de projeto cobrindo apenas sua asa. O gerente de projeto mantém o projeto mestre e inspeciona cada asa antes de integrá-la à estrutura geral. As equipes não entram nas asas umas das outras para fazer ajustes.
Worktrees paralelos funcionam exatamente assim. Cada agente tem sua própria cópia do repositório, trabalha independentemente, e o orquestrador mescla resultados em uma ordem fixa.
Antes de escrever qualquer YAML ou código, responda a três perguntas: quais arquivos esse agente lê, quais arquivos ele escreve e o que ele está explicitamente proibido de tocar?
No meu harness SDLC, cada agente de implementação recebe:
Essa restrição previne os conflitos de merge que fizeram duas execuções paralelas precoces exigirem intervenção manual para desembaraçar.
O campo description é o que o orquestrador lê para decidir quando delegar. Torne-o concreto: triggers, entradas esperadas e restrições de propriedade.
O único canal de um sub-agente de volta ao orquestrador é sua saída — arquivos escritos em caminhos conhecidos e um commit estruturado. No meu harness, cada agente escreve um relatório em um local previsível e então commita. O orquestrador lê esse relatório para determinar se passa ou tenta novamente.
Padrão: Escreva saídas em caminhos de arquivo determinísticos. O orquestrador lê arquivos, não saída conversacional — isso funciona de forma confiável entre limites de processo.
Um meta-agente é um agente cujo trabalho é gerar configuração para outros agentes. Em vez de escrever cada YAML de sub-agente manualmente, você descreve o que precisa e um meta-agente produz uma configuração completa e pronta para uso.
Na prática uso isso quando um projeto introduz um novo tipo de tarefa que ainda não automatizei. Em vez de projetar a configuração do agente manualmente, descrevo a tarefa a um meta-agente e itero em sua saída.
O padrão de meta-agente é mais útil para bootstrapping. Uma vez que uma configuração de sub-agente esteja funcionando bem, ela raramente precisa mudar. Pense no meta-agente como um acelerador de configuração pontual, não uma dependência de tempo de execução.
Quando usar: Quando você está construindo uma nova categoria de agente (por exemplo, "agente de revisão" vs "agente de implementação") e quer um ponto de partida bem estruturado em vez de escrever o YAML do zero.
Este é o padrão central do meu harness SDLC. As tarefas são agrupadas em ondas onde todas as tarefas de uma onda são independentes. O orquestrador lança todas as tarefas de uma onda em paralelo, aguarda todas completarem e então mescla resultados em ordem de número de tarefa antes da próxima onda começar.
O detalhe chave: merge_wave_results mescla em ordem de número de tarefa, não em ordem de conclusão. Isso torna o resultado do merge determinístico — essencial quando tarefas podem tocar arquivos de configuração sobrepostos.
Por que merges ordenados importam: Se a tarefa 3 e a tarefa 5 ambas atualizam um arquivo de configuração compartilhado, quem mescla por último vence. Fixar a ordem de merge torna esse resultado previsível e reproduzível entre execuções.
Quando as tarefas têm dependências explícitas — a tarefa B precisa da saída da tarefa A — execute-as sequencialmente. Meu harness usa isso dentro de uma onda para tarefas estruturais que devem pousar antes dos rewrites de módulo começarem:
O limite de onda é sua primitiva de sequenciamento. Use-a quando trabalho anterior cria uma dependência que trabalho posterior consome.
O orquestrador em si pode chamar o SDK do Anthropic para lidar com modos de falha que lógica if/else baseada em regras perderia. Quando uma tarefa falha, em vez de fazer correspondência de padrões em strings de erro, peço ao Claude Haiku para classificar a falha e recomendar uma estratégia de retry:
Uma triagem baseada em regras só captura falhas que você antecipou no tempo de design. Um LLM pode classificar mensagens de erro novas usando raciocínio geral — útil na primeira vez que você encontra um modo de falha que não planejou.
Considere um spec onde as tarefas 3 e 5 ambas modificam metadata.json. Você poderia executá-las em paralelo, mas o merge vai conflitar. Quais são suas opções?
Não há uma única resposta certa — mas pensar nisso antes de começar é muito mais barato do que desembaraçar um conflito de merge depois.
Quando oito agentes estão escrevendo em oito worktrees em paralelo, você precisa saber o que está acontecendo sem ler oito janelas de terminal separadas.
Meu harness registra quatro eventos por agente: started, checkpoint-passed, completed e merged. Quatro eventos por tarefa são suficientes para reconstruir a linha do tempo completa de qualquer execução e detectar qual agente travou ou falhou.
Uma linha JSON por evento. Sem banco de dados, sem servidor, sem dependências. Para ver o que aconteceu, leia o arquivo JSONL. Para construir uma linha do tempo, ordene por ts. Para encontrar agentes travados, procure por started sem completed correspondente.
O modo de falha multi-agente mais comum que encontro é dois agentes modificando o mesmo caminho de arquivo. A correção é arquitetural (divisões de tarefa melhores), mas a detecção é simples:
Antes de escalar para 10+ agentes: execute 3 agentes primeiro e corrija cada conflito que encontrar. Um conflito pequeno com 3 agentes é catastrófico com 10.
Quando uma onda produz cinquenta páginas de logs, uso o Claude Haiku para produzir um resumo de um parágrafo de cada sessão de agente. Ao preço atual, resumir uma execução de oito agentes custa bem menos de $0,05 — menos do que o custo cognitivo de ler os logs manualmente.
Você agora conhece os padrões centrais. Este projeto pede que você construa um wave runner mínimo: despachar duas tarefas independentes em paralelo, aguardar ambas completarem e mesclar resultados em ordem de id de tarefa.
Delimite deliberadamente: Um wave runner de produção tem retries, limpeza de worktree e relatórios estruturados. Construa a versão mais simples que demonstra o padrão central. Você pode estendê-lo depois que funcionar.
Task com id, spec_excerpt e output_pathrun_task(task) simula trabalho async e escreve um arquivo de resultado em output_pathrun_wave(tasks) despacha todas as tarefas em paralelo usando asyncio.gathertask.id antes do merge — independentemente da ordem de conclusãomerge_resultsDicas escalonadas:
asyncio.gather(*[run_task(t) for t in tasks], return_exceptions=True) executa todas as tarefas e captura exceções como valores de retorno em vez de lançá-las.isinstance(r, Exception) na lista de resultados para detectar falhas antes do merge.sorted(results, key=lambda r: r["task_id"]) coloca os resultados em ordem de merge determinística.A linha crítica é sorted(..., key=lambda r: r["task_id"]). asyncio.gather retorna resultados na ordem de conclusão, que é não-determinística. Ordenar por task id torna a ordem de merge reproduzível em cada execução.
1. Em um wave runner ordenado por dependência, os resultados de uma onda são mesclados em ordem de conclusão. Verdadeiro ou falso?
Correct Answer: B
Os resultados são sempre mesclados em ordem de task-id. A ordem de conclusão do asyncio.gather é não-determinística — ordenar por task id torna o resultado reproduzível em cada execução, o que importa quando duas tarefas podem tocar arquivos sobrepostos.
2. Dois agentes em uma onda ambos modificam o mesmo arquivo metadata.json. Qual é a correção correta?
Correct Answer: C
A causa raiz é um problema de design: os limites de tarefa estão errados. Serializar ou bloquear são soluções alternativas que mascaram a falha. Corrija a divisão de propriedade para que cada arquivo tenha exatamente um agente dono — então o paralelismo é seguro.
3. Qual é o canal de comunicação principal entre um sub-agente e seu orquestrador neste harness?
Correct Answer: C
Agentes escrevem saídas — relatórios e commits — em caminhos de arquivo determinísticos. O orquestrador lê esses caminhos após cada agente terminar. Isso evita acoplamento rígido e funciona de forma confiável entre processos separados.
4. Você chama asyncio.gather(return_exceptions=True). Como isso difere do comportamento padrão?
Correct Answer: C
Com return_exceptions=True, gather retorna objetos Exception como elementos de lista em vez de lançá-los. Isso permite que você inspecione todos os resultados — incluindo quais tarefas tiveram sucesso — antes de decidir como lidar com as falhas.
5. O orquestrador chama o Claude Haiku para classificar uma falha de tarefa. O que isso fornece que um if/else baseado em regras não consegue?
Correct Answer: D
Uma triagem baseada em regras só reconhece falhas que você enumerou quando escreveu o código. Um LLM pode classificar mensagens de erro novas usando raciocínio geral — útil na primeira vez que você encontra um modo de falha que não planejou.
6. Quais quatro eventos por agente o logger de observabilidade rastreia neste padrão do módulo?
Correct Answer: B
Started, checkpoint-passed, completed e merged. Esses quatro eventos são suficientes para reconstruir a linha do tempo completa de qualquer execução e detectar qual agente travou, falhou ou nunca foi mesclado.
7. Na solução trabalhada, por que `sorted(raw, key=lambda r: r['task_id'])` é necessário antes de chamar merge_results?
Correct Answer: A
asyncio.gather retorna resultados na ordem em que as tarefas foram concluídas — não na ordem em que foram submetidas. A ordem de conclusão de tarefas é não-determinística (afetada por I/O, agendamento, etc.). Ordenar por task_id torna a ordem de merge determinística e reproduzível.
Muito bem. Veja o que vem a seguir.
Voltar para esta trilhaOu agende a chamada de diagnóstico