O artigo anterior tratava de tornar um agente confiável: loop de execução, roteamento de ferramentas, compactação de contexto, sessões seguras contra falhas. Essa camada responde "como um único agente realiza uma tarefa sem cair?" Este artigo é sobre a camada acima dela: o que acontece quando um agente não é suficiente e um trabalho precisa ser dividido entre uma equipe.
Isso é o que as pessoas geralmente querem dizer com orquestração multiagente: um agente líder, dono da conversa, divide uma solicitação em partes, entrega cada parte a um subagente especializado, espera que as corretas terminem antes de iniciar a próxima, repassa os resultados e mantém tudo sob controle quando uma etapa falha. Orkas executa isso inteiramente na máquina do usuário. Veja abaixo como essa camada de orquestração é construída: código limpo e generalizado, mas a estrutura é real.
Agente líder e subagentes
O modelo mental é uma equipe pequena com uma cadeia de comando clara. Um agente líder (nós o chamamos de comandante) é o dono da conversa e do contexto geral. Ele não faz todo o trabalho sozinho; sua função é decidir o que precisa ser feito, em que ordem e por quem. Os subagentes são especialistas — cada um configurado com seu próprio prompt de sistema, suas próprias ferramentas permitidas e seu próprio conjunto de habilidades. Um subagente é bom em uma parte do trabalho e é invocado quando essa parte surge.
Duas coisas fazem com que isso seja mais do que uma palavra da moda. Primeiro, subagentes e habilidades são unidades de primeira classe, e não truques imediatos: um subagente é um agente real, configurado separadamente, e despachar para um deles é uma transferência real com seu próprio contexto. Em segundo lugar, a coordenação não é deixada às boas intenções do modelo – é impulsionada por um artefacto explícito que o sistema, e não o modelo, mantém honesto. Esse artefato é o plano.
Um plano é um gráfico, não um script
Quando o agente principal decide que uma solicitação precisa de mais de uma etapa, ele escreve um plano. O plano não é uma prosa de formato livre e não é uma lista de verificação linear – é um pequeno gráfico de dependência (um DAG). Cada nó é uma etapa, e uma etapa contém tudo o que o orquestrador precisa para despachá-lo:
interface PlanStep {
índice: número; // Baseado em 1, estável, nunca renumerado
título: sequência; // legível por humanos, mostrado na UI
cessionário: string; // quem o executa: "usuário" | "comandante" | um subagente
entrada?: string; // a carga útil do despacho — um modelo (veja abaixo)
esperar_por?: número[]; // índices de etapas upstream; o padrão é [índice - 1]
on_failure?: "abort_plan" | "continuar" | "ask_commander";
// --- estado de tempo de execução, de propriedade do orquestrador, NÃO escrito pelo modelo ---
status: "pendente" | "em_progresso" | "feito" | "falhou" | "ignorado" | "bloqueado";
resumo_saída?: string; // breve resumo do que a etapa produziu
arquivos_de_saída?: string[]; // arquiva o passo produzido
motivo_da_falha?: string;
}O campo wait_for é o que transforma uma lista em um gráfico. Por padrão, uma etapa espera pela etapa anterior (uma cadeia simples), mas uma etapa pode declarar que depende de várias etapas anteriores - "resumir" pode esperar tanto por "pesquisar o mercado" quanto por "pesquisar concorrentes". Isso é um diamante, não uma linha, e o orquestrador o trata como tal.
Quem é o dono da verdade: o orquestrador, não o modelo
Veja novamente a divisão nessa estrutura. O modelo preenche a intenção — títulos, responsáveis, entradas, dependências — quando escreve o plano pela primeira vez. Mas tudo sobre o estado de execução — status, output_summary, failure_reason — é de propriedade exclusiva do orquestrador. O modelo propõe o plano uma vez; ele nunca marca suas próprias etapas como "concluídas".
Essa separação é deliberada e é a decisão mais importante em todo o design. Um modelo de linguagem é perfeitamente capaz de anunciar alegremente "etapa 3 concluída" quando a etapa 3 falhou, ou de perder o controle de quais etapas ainda estão pendentes no meio de uma longa conversa. Se o Estado vivesse na cabeça do modelo, o plano afastar-se-ia da realidade. Ao tornar o estado um artefato estruturado que apenas o executor escreve – e escreve apenas em resposta a uma etapa realmente concluída – o plano permanece um espelho preciso do que realmente aconteceu. O modelo decide a forma da obra; o tempo de execução decide o que é verdade sobre seu progresso.
Despachando as etapas que estão prontas
Quando um plano existe, um pequeno mecanismo — o executor — o impulsiona. A operação principal é “encontrar as etapas que estão prontas e despachá-las”. Uma etapa está pronta quando ainda está pendente e cada etapa que ela espera atingiu um estado terminal, com sucesso suficiente:
function findReadySteps(plan): PlanStep[] {
retornar plano.steps.filter((s) => {
if (s.status! == "pendente") retornar falso;
const deps = s.wait_for ?? (s.index > 1? [s.index - 1]: []);
retornar deps.every((d) => isTerminal(plan.step(d).status)); //feito ou ignorado
});
}Isso não é executado em um cronômetro, mas em resposta a eventos. Cada vez que uma etapa termina – um subagente retorna, o comandante conclui um turno de síntese – o executor reconcilia: ele registra o resultado da etapa que acabou de ser concluída, depois verifica novamente o que ficou pronto como resultado e o despacha. A orquestração é esse ciclo de reconciliação, que gira continuamente até não restar nenhuma etapa.
O envio passa pelo mesmo chat, não por um canal paralelo
Aqui está uma opção que mantém o sistema honesto: o despacho de uma etapa não usa um canal RPC oculto. Ele publica uma mensagem na mesma conversa em grupo que o usuário está assistindo, do agente principal, @mencionando o subagente. Para o subagente, ser despachado é indistinguível de ser abordado no chat – ele apenas executa seu turno normal. Não há um segundo caminho de execução para manter a sincronia com o primeiro.
Um cessionário pode ser de três tipos, e cada um envia de forma um pouco diferente:
- Um subagente — o caso comum. O executor resolve o nome do agente para seu id e posta
@do comandante. O subagente o pega e executa um turno completo do agente. - O usuário — quando uma etapa realmente precisa de intervenção humana, a etapa se transforma em um formulário e pausa o plano (mais sobre isso abaixo). Nada ocorre até que o usuário responda.
- O próprio comandante — para síntese ou etapas de decisão ("leia tudo acima e escreva o resumo"). Este é um despertar privado que não publica uma mensagem redundante visível ao usuário; o agente principal simplesmente dá uma volta com o contexto coletado em mãos.
Passando contexto de uma etapa para a próxima
Uma equipe só é útil se o trabalho fluir entre seus membros. O mecanismo é o modelo input. Quando o agente líder escreve uma etapa, a entrada não é uma string congelada — ela pode fazer referência a resultados anteriores, e o executor renderiza essas referências no momento do envio:
// etapa 3.input, conforme escrito pelo agente líder
"Usando as descobertas abaixo, esboce a nota de lançamento.\n\n{{step_1.output_summary}}"
// o que o subagente realmente recebe no momento do despacho
"Usando as descobertas abaixo, esboce a nota de lançamento.\n\n- O mercado está crescendo cerca de 20% em relação ao ano anterior; dois operadores históricos…"Observe o que é transmitido: output_summary, um breve resumo de cada etapa concluída — não sua transcrição completa. Esta é uma decisão orçamentária e é o mesmo instinto da compactação de contexto do chicote. Se cada etapa downstream herdasse o histórico completo, token por token, de tudo o que foi feito antes dela, o contexto aumentaria e o custo explodiria após apenas alguns saltos. Os resumos mantêm cada transferência barata e mantêm cada subagente focado no que realmente precisa do upstream, em vez de analisar como o upstream chegou lá. A mensagem inicial do usuário e quaisquer anexos também são transportados, portanto, um terceiro passo na cadeia ainda conhece a solicitação original.
Serial por padrão — e por quê
Você pode esperar que, quando vários passos voltam prontos ao mesmo tempo — dois ramos de um diamante, digamos — o orquestrador dispara todos eles em paralelo. Poderia; em vez disso, hoje ele despacha um passo pronto de cada vez, o primeiro por índice, e deixa o restante aguardar pela próxima reconciliação. Liderar uma equipe inteira estritamente individualmente é uma escolha deliberada e conservadora, e vale a pena ser honesto sobre o motivo.
O motivo é a correção sob simultaneidade. Imagine dois subagentes terminando quase ao mesmo tempo. Ambas as conclusões acionam uma reconciliação; ambos os reconciliados leram o plano; ambos veem a mesma etapa downstream ainda em pendente - e ambos a despacham. Agora a mesma etapa é executada duas vezes. Para tornar isso impossível, cada ciclo de leitura-modificação-envio em uma determinada conversa é serializado atrás de um bloqueio por conversa:
// todos os caminhos de mudança de estado para uma conversa são executados em um mutex
planLock(uid, cid).runExclusive(async () => {
const plano = aguarda readPlan(uid, cid);
applyOutcomeOfFinishedStep(plano); //marca como concluído/com falha/ignorado
aguardar expediçãoReady(plano); // despacha o próximo passo pronto
});O bloqueio garante que "registrar o que terminou" e "decidir o que vem a seguir" aconteçam como uma unidade indivisível, de modo que uma etapa posterior nunca possa ser despachada duas vezes. Com esse bloqueio instalado, despachar as etapas uma de cada vez é a coisa mais simples e obviamente correta. A distribuição paralela genuína é uma extensão tratável no topo dessa base - mas a base é um executor serializado e sem corrida, e essa ordem de prioridades (corretar primeiro, rápido depois) é o ponto.
Quando uma etapa dá errado
Executando em uma máquina real em APIs de modelo externo, a falha é rotineira, e o orquestrador a classifica em alguns casos em vez de tratar todos os erros da mesma forma.
Primeiro, ele pergunta se a falha foi meramente transitória — uma queda de conexão, um limite de taxa, um problema. Se for assim, e a etapa ainda não tiver esgotado um pequeno orçamento de novas tentativas, a etapa será silenciosamente revertida para pendente para que a próxima reconciliação a reenvie. (Isso fica acima das novas tentativas em execução do equipamento; o plano só é reenviado depois que as próprias tentativas do agente se esgotam, com um limite máximo para que uma etapa realmente interrompida não possa ser repetida para sempre.)
Se a falha for real, a política on_failure declarada na etapa decidirá o que a equipe fará em seguida:
abort_plan— esta etapa foi importante o suficiente para que nada posterior fizesse sentido sem ela. Marque como falha e cascata: cada etapa ainda pendente é marcada comoignorada. O plano termina de forma limpa, em vez de ser construído sobre uma base que falta.continuar— esta etapa era opcional. Marque-o comoignoradoe deixe as etapas subsequentes prosseguirem como se simplesmente não produzissem nada.ask_commander(o padrão) — nem abortar cegamente nem continuar cegamente. Marque a falha e desperte o agente principal para ver o que aconteceu e decidir: tentar novamente de forma diferente, contornar o problema ou parar e perguntar ao usuário.
Há um sexto status que vale a pena destacar: bloqueado. Um subagente no meio de sua etapa pode perceber que precisa de algo que somente o usuário pode fornecer e apresentar um formulário ou uma pergunta. A etapa não falha — ela vai para bloqueada e todo o plano é pausado. No momento em que o usuário responde, o executor se reconcilia e a equipe continua exatamente de onde parou. Um plano bloqueado é um plano pausado, não quebrado.
Cada etapa é uma execução completa do agente
Vale a pena fechar o ciclo com o artigo anterior. Quando o orquestrador despacha uma etapa para um subagente, esse subagente não executa nenhuma rotina simplificada - ele executa o loop de chicote completo: seu próprio loop de execução de streaming, suas próprias chamadas de ferramenta, sua própria janela de contexto com compactação, sua própria sessão segura contra falhas. A orquestração fica bem sobre o tempo de execução de agente único; nunca chega dentro dele. O agente principal decide o formato do trabalho e a ordem das transferências; cada subagente, uma vez entregue sua fatia, é um agente completo por si só.
Essas camadas são a razão pela qual os dois artigos são compostos. O equipamento torna um agente confiável para uma tarefa. O orquestrador reúne vários agentes confiáveis em uma equipe que pode assumir uma tarefa muito grande ou muito variada para qualquer um deles.
Algumas decisões importantes
O orquestrador é dono do estado de execução, o modelo é dono da intenção. O modelo propõe o plano; apenas o tempo de execução marca as etapas concluídas, com falha ou ignoradas e apenas em resposta a algo que realmente está acontecendo. Esse limite é o que mantém o plano como um espelho honesto da realidade, em vez da estimativa otimista do modelo.
Envio pela mesma conversa, não por um canal secundário. Uma etapa despachada é apenas uma mensagem do agente principal para um subagente. Um caminho de execução, nada oculto que possa ficar fora de sincronia, e o usuário pode observar a equipe trabalhando no mesmo thread que já está lendo.
Resumos, não transcrições, entre as etapas. Cada transferência traz um breve resumo da saída do upstream. Ele mantém os orçamentos de contexto sãos em cadeias longas e mantém cada subagente focado no que precisa, não em como o anterior chegou lá.
Corrigir antes de paralelo. Um bloqueio por conversa serializa cada ciclo de leitura-modificação-envio e as etapas despacham uma de cada vez. Estritamente serial é a versão obviamente livre de corridas de despacho duplo; a distribuição paralela é uma otimização para colocar em camadas sobre uma base que já está correta.
Concluindo
A camada de orquestração do Orkas não tem nenhum algoritmo exótico em sua essência. Seu valor está em alguns limites mantidos firmemente: um plano que é um gráfico de dependência em vez de um script; estado de execução pertencente ao tempo de execução e não ao modelo; envio que flui pelo mesmo chat que o usuário está assistindo; contexto que se move entre as etapas como resumos; e um executor que coloca a correção à frente da simultaneidade. Cada um é simples por si só. Juntos, eles transformam um único agente confiável em uma equipe que divide o trabalho, repassa o trabalho e se recupera quando algo dá errado.
Se você quiser a camada abaixo desta, leia como um único agente é projetado para funcionar de maneira confiável. Se você deseja a camada que faz com que cada agente melhore com o uso, leia como os agentes Orkas aprendem com seu próprio trabalho. E se você prefere dirigir essa camada em vez de construí-la, o Orkas a entrega como orquestração de agentes de IA open source que roda na sua máquina.