Projetando um runtime programável para orquestração de agentes

@realmcore_
INGLÊShá 21 horas · 03 de jul. de 2026
283K
260
45
10
456

TL;DR

O Onyx apresenta um runtime programável para agentes de IA, permitindo que desenvolvedores criem sistemas determinísticos usando TypeScript, estado persistente e tratamento de erros estruturado.

Este artigo apresenta o Onyx, nossa VM para orquestração programável de agentes. E, por extensão, um runtime que transforma orquestração em engenharia de software. Ao final deste artigo, você entenderá as restrições e decisões de design envolvidas na construção da VM, bem como criará seus próprios programas e arquitetará seus sistemas de agentes.

Introdução

Agentes são inerentemente não determinísticos. Esse é o ponto principal. Se você quisesse determinismo, estaria escrevendo software.

Mas em algum momento, todos que usam agentes quiseram ir além. Aprendemos que dividir a execução em etapas estruturadas melhora o desempenho: Planejar, Implementar, Revisar, QA, etc. Depois, aparentemente concordamos em escrever scripts, ferramentas e habilidades para direcionar cada agente, compartilhar contexto entre eles e estabelecer proteções. Em seguida, juntamos esses scripts conectando texto entre agentes e, como estamos apenas passando texto, isso meio que funciona.

Se você dedicasse tempo suficiente ao problema e fosse particularmente engenhoso, teria descoberto como obter garantias do seu sistema para ter execução condicional com base em um determinado estado. E você provavelmente armazenaria esse estado em um arquivo de marcação analisável ou em um conjunto de arquivos de marcação para direcionar seus scripts bash. Você pode até ter construído uma CLI personalizada para seus agentes usarem.

Como engenheiros, isso é familiar; usamos scripts enquanto fazemos engenharia de software. No entanto, o software moderno não é construído encadeando scripts bash e ferramentas CLI. Em vez disso, temos linguagens de programação, runtimes e cadeias de ferramentas para nos ajudar a projetar nossos sistemas. Escrevemos software com linguagens de programação porque elas vêm com uma biblioteca padrão, semântica clara e um modelo de execução no qual podemos confiar. Elas possuem ecossistemas ricos com cadeias de ferramentas para todas as nossas necessidades.

As garantias que elas nos dão sobre nossos sistemas nos permitem raciocinar em níveis mais altos de abstração.

Mas não há equivalente para projetar sistemas de agentes. Para construir sistemas, a orquestração de agentes precisa ser programável exatamente da mesma forma que o software moderno.

Hoje, estamos apresentando a especificação para PROGRAMS (*.program.ts), e Onyx, nossa VM construída para orquestração determinística de agentes. Este artigo explora um histórico da orquestração de agentes, a semântica estática e de runtime de uma VM que pode executar um programa e suas implicações para onde o campo está caminhando.

Parece caro, mas na verdade não é. Explico isso mais adiante no artigo.

Para os curiosos, é assim que o Autoresearch de Andrej Karpathy se parece como um programa:

akira - inline image

Problemas Não Resolvidos na Orquestração de Agentes

Para entender o que um runtime para orquestração de agentes deve incluir, precisamos entender as limitações dos agentes.

Um agente LLM pode ser pensado como um gerador de fluxo JSON, alimentado em um analisador, que então despacha chamadas de ferramenta para um ambiente em um loop.

Cada chamada de ferramenta tem exatamente a mesma forma de esquema externo, mas o conteúdo desse fluxo de saída não é determinístico.

A combinação de determinismo e não-determinismo é o que tornou os agentes tão valiosos. Eles são flexíveis o suficiente para encadear sequências de ações de maneiras únicas, mas determinísticos o suficiente para interagir com um computador por meio de chamadas de ferramenta.

A capacidade de composição é quase gratuita se você estiver disposto a abrir mão do requisito de que o conteúdo desse fluxo seja tipado. Os modelos são bons o suficiente para canalizar texto para dentro e para fora dentro dos trilhos que fornecemos a eles: prompts, mensagens e chamadas de ferramenta.

Isso expõe uma interface muito componível: texto

Texto é uma interface universal. Tudo em um computador pode ser serializado em texto, mesmo que seja apenas código de máquina. Se você puder ter um LLM que insere e gera texto através desta interface universal, você obtém capacidade de composição sobre fluxos de texto.

Isso significa que a confiabilidade do comportamento do seu agente está diretamente relacionada à consistência da saída do modelo. Alta variabilidade de saída significa comportamento de agente mais errático.

Depois de ter uma interface para compor peças, a próxima restrição que você considera é a dirigibilidade:

o que você quer que o agente faça e como você o faz consistentemente fazer o que você quer

Direcionamos agentes deslocando a distribuição da qual ele amostra, em outras palavras, prompting.

Em 2022, o ReAct surgiu e essencialmente foi pioneiro na dirigibilidade de agentes. Na verdade, podemos ir além e dizer que ele tornou os agentes como os conhecemos uma realidade. Pensar e raciocinar sobre uma saída de ferramenta antes de dar o próximo passo é o que mantém o loop coerente.

akira - inline image

Ainda precisávamos que os agentes fossem mais inteligentes. O uso de dimensionamento de computação em tempo de teste, industrializado pela série O de modelos da @OpenAI, deu aos laboratórios de modelos a capacidade de incorporar melhor comportamento de agente [[11]](http://localhost:5173/blog/onyx#ref-11). Gerar mais tokens antes de chamar uma ferramenta permite que o modelo escape da distribuição de saída na qual teria ficado preso se tivesse sido restringido no comprimento da saída de raciocínio. Você pode optar por treinar como o modelo percorre sua paisagem de distribuição de saída e, portanto, ter liberdade para treinar um comportamento de agente mais claro nas tarefas que lhe interessam.

À medida que o comprimento do contexto cresce ilimitadamente, direcionar o agente se torna difícil e a conclusão da tarefa se torna menos provável. Mesmo com um modelo de raciocínio, não há garantia de recuperação, e o agente morre ali mesmo. O agente pode atingir seu limite de contexto, declarar uma conclusão precoce, ficar preso em um loop, etc.

Extraindo Garantias de um Sistema Não Determinístico

As soluções para isso foram variadas, mas uma se destaca: O Loop Ralph, criado por @GeoffreyHuntley. [[3]](http://localhost:5173/blog/onyx#ref-3

Ele introduziu a ideia de que você poderia limitar a execução do agente e, em seguida, usar esses limites para raciocinar sobre a conclusão da tarefa. Isso permite que o Loop Ralph faça algo mágico: ele fornece algo em que você pode confiar em um sistema não determinístico.

Uma centelha de determinismo.

É melhor garantir a falha e progredir gradualmente em direção a algo correto do que puxar a alavanca da máquina caça-níqueis mais uma vez. Esse limite definido fornece algo concreto para raciocinar e, uma vez que você pode raciocinar sobre os limites de algo, pode construir um sistema a partir disso.

Enfrentando os Limites do Comprimento do Contexto

Há um problema, no entanto: um agente novo perde a coerência entre execuções, mas um único agente fica sem contexto com tempo suficiente.

Surge então o RLM, de @lateinteraction @a1zhang. O RLM nos deu um conceito de como interagir com contexto longo (ou seja, uma execução de agente) de forma estruturada [[4]](http://localhost:5173/blog/onyx#ref-4). O RLM foi inspirado pelo CodeAct, um artigo de 2024 que demonstrou o uso de código para orquestrar operações [[5]](http://localhost:5173/blog/onyx#ref-5). O agente escreve scripts que orquestram operações dentro de um REPL para então recuperar uma saída. O RLM opera da mesma forma, com a ressalva adicional de que usa variáveis para armazenar contexto e realizar operações nesse contexto. Ele também permite chamadas LLM recursivas no REPL. Você pode perder alguma reatividade que outros loops têm, mas ganha a capacidade de trabalhar programaticamente com o contexto. O ponto chave aqui é que os scripts no REPL são efêmeros. Você obtém um runtime de script e gerenciamento de contexto, mas não há reutilização ou capacidade de composição. Apenas escreva o script, execute-o e ele desaparece. Em termos de construção de sistemas, isso é estritamente pior do que apenas encadear agentes e arquivos markdown com scripts bash, porque você perde a persistência e a execução limitada.

De Loops Individuais para Orquestração em Escala

O Deep Research da OpenAI [[6]](http://localhost:5173/blog/onyx#ref-6) foi um dos primeiros exemplos de um fluxo de trabalho determinístico que tinha uma forma ou esquema de execução geral com pequena variabilidade em cada execução. A forma como funciona é planejando um lote de consultas, executando-as na web, revisando os resultados e planejando o próximo lote de consultas. Cada lote investiga mais profundamente o espaço do problema.

akira - inline image

O Cursor levou a ideia de determinismo muito além quando @wilsonzlin demonstrou um harness que orquestrava agentes para construir um navegador. Ele construiu um harness personalizado para coordenar grandes quantidades de trabalho usando agentes planejadores paralelos e agentes de tarefa [[7]](http://localhost:5173/blog/onyx#ref-7). O que é relevante aqui é que a relação entre cada parte do harness é fixa. Existem planejadores, que exploram o estado atual do sistema e geram tarefas, e executores, que pegam as tarefas e as implementam em paralelo. Existem proteções fixas entre agentes e canais fixos para comunicar informações. Para fazer uma boa coordenação, você precisa de garantias nas interfaces.

Usando Condições de Término para Execução Limitada

Em maio, a Codex introduziu a ideia de uma meta que usa um loop de verificação para escalar em direção a algum estado final desejado até que uma tarefa seja concluída. Você pode pensar nisso como uma versão pronta para produção do loop Ralph, incorporada ao Codex. Ele permite que você especifique um objetivo e tem um loop automatizado que executa e revisa, embutido.

akira - inline image

O autoresearch de Karpathy [[9]](http://localhost:5173/blog/onyx#ref-9) é semelhante ao /goal da Codex e ao loop Ralph. Ele combina a condição de término verificável da meta com a limitação de execução de um loop Ralph ao longo das iterações, permitindo que ele se mova continuamente em direção a um objetivo. Ele progride pesquisando o espaço de ideias, melhorando iterativamente ao longo do tempo.

akira - inline image

Até este ponto, todas as soluções que externalizam a orquestração para fora do agente são fixas na forma de seu gráfico de execução. Elas são executadas usando um padrão escrito à mão e têm uma espécie de esquema para as formas permitidas nas quais podem operar. Elas não se adaptam por tarefa ou não têm fortes garantias sobre a forma do gráfico de execução.

Tornando a Orquestração Flexível

Em março deste ano, apresentamos o Slate, o primeiro agente de codificação a usar código para orquestração ao vivo de subagentes no estilo RLM. Ainda é o único agente de codificação bem utilizado que usa código para fazer orquestração ao vivo de agentes. No Slate, threads podem ser geradas, pausadas, retomadas e direcionadas em tempo real. O agente principal entende profundamente como orquestrar todos os subagentes em execução para que você não precise. No entanto, semelhante ao RLM, ainda enfrentávamos o desafio de compartilhar estado entre subagentes e scripts efêmeros, o que não é algo que você encontraria usando um script bash e um arquivo markdown.

Ainda assim, se o modelo é quem faz a orquestração, como você o direciona? Você diz a ele para escrever seu código de orquestração de uma maneira específica? O que você faz?

Nossa solução inicial (como um patch antes de lançarmos o runtime Onyx) foi chamada de habilidades de orquestração [[13]](http://localhost:5173/blog/onyx#ref-13). A ideia era simples: permitir que o usuário fornecesse uma habilidade para direcionar como o agente aborda sua orquestração. Só isso. Funcionou razoavelmente bem, mas teve muitos problemas.

Ou seja, uma habilidade não é um contrato comportamental vinculante. Você não pode obter uma garantia a partir do texto.

Isso significa que o orquestrador não precisava seguir o padrão de execução desejado porque não havia uma maneira real de impô-lo. Um dos maiores benefícios do runtime Onyx é que resolvemos esse problema.

Nenhum dos sistemas mencionados possui contratos comportamentais vinculantes.

Bem, então, e se o agente pudesse escrever seu código de orquestração em um script por tarefa para que o gráfico de execução fosse fixo? É isso que os fluxos de trabalho dinâmicos do Claude são. [[10]]([http://localhost:5173/blog/onyx#ref-10)[[12]](http://localhost:5173/blog/onyx#ref-12](http://localhost:5173/blog/onyx#ref-10)[[12]](http://localhost:5173/blog/onyx#ref-12) Da mesma forma que o RLM e o Slate, ao escrever código para orquestrar subagentes, os fluxos de trabalho dinâmicos permitem que o Claude escreva e salve formas de fluxo de trabalho. Isso se combina com /loop para poder iterar sobre padrões específicos. Ele fornece um contrato declarativo para o comportamento de um conjunto de agentes. Ainda não é o mesmo que escrever software, pois carece de coisas como composição funcional, mas você obtém persistência e uma forte garantia sobre como a tarefa será executada. Eles são scripts de fluxo de trabalho escritos dinamicamente para uma determinada tarefa ad hoc. [[12]](http://localhost:5173/blog/onyx#ref-12) E como são persistidos em disco, eles têm um benefício adicional: podem ser reexecutados e envolvidos com cola de orquestração como /loop.

akira - inline image

Se você notar, todas as soluções acima estão buscando a mesma coisa: uma maneira determinística de controlar como os agentes executam ao longo do tempo.

Esta é uma história que já vimos se desenrolar na engenharia de software como um campo. Começamos juntando sistemas díspares e scripts de jobs, e então nossas linguagens se tornaram mais flexíveis e poderosas. Ganhamos cada vez mais alavancagem sobre o processo de engenharia com ecossistemas mais fortes, permitindo-nos construir sistemas mais confiáveis em níveis de abstração mais altos.

Agora, os agentes estão na mesma trajetória, e hoje estamos lançando o próximo passo nessa trajetória para permitir que você projete os sistemas que executam seus agentes. Linguagens de programação geralmente usam interpretadores ou VMs para agendar recursos automaticamente. É isso que lhe dá alavancagem como engenheiro usando a linguagem.

Se uma VM fizesse sentido para orquestração de agentes, você precisaria de algumas coisas:

  1. Gerenciamento de estado persistente: devemos ser capazes de definir estado, referenciá-lo pelo nome, persistí-lo e manipulá-lo programaticamente.
  1. Garantias de tipo. Devemos respeitar as formas de entrada e saída definidas e segui-las, e ser capazes de confiar nelas.
  1. Primitivas de fluxo de controle, de preferência bem conhecidas que um LLM entenderia.
  1. Estrutura clara para tratamento de erros (por exemplo, try-catch).
  1. Gerenciamento de recursos: controles definidos sobre recursos como paralelismo de agentes, custo, quais modelos estão em execução, etc.
  1. Isolamento de execução: um determinado agente ou programa em execução deve ser isolado de outro, a menos que o estado seja explicitamente compartilhado.
  1. Controle de ciclo de vida: como é um programa de agente e semântica para executar, cancelar e direcionar. Sem isso, você não tem um caminho claro para limpeza e não pode controlar o gerenciamento do ciclo de vida.
  1. Capacidade de composição: Os programas devem se compor entre si e devem ser chamáveis com tipos de entrada e saída definidos.
  1. Visibilidade: Devemos ser capazes de saber o que foi executado, quando, e ser capazes de rastrear uma falha de execução na origem.
  1. Durabilidade: Devemos ter um modelo claro de como podemos nos recuperar de falhas e retomar.

Cada um desses é um problema que já foi resolvido por linguagens de programação décadas atrás. A orquestração de agentes está apenas encontrando todos eles novamente pela primeira vez.

Para realmente ser capaz de escrever software para isso, um programa "program.ts" deve ser criado em um runtime que suporte tudo o que foi mencionado acima, para que possamos raciocinar sobre o que acontecerá quando um programa não funcionar e projetar em torno da falha.

É por isso que construímos o Onyx. É uma VM de orquestração de agentes projetada precisamente para suportar tanto programas persistentes e componíveis quanto uma camada de script interpretada. Aqui está como funciona e o que um runtime compatível com "program.ts" precisa suportar.

Projetando o Runtime

Quando projetamos uma linguagem e um runtime para essa linguagem, precisamos pensar nas restrições sobre as quais queremos ser capazes de raciocinar e no que nos importa que seja facilmente expressável. Então, podemos dividir a semântica resultante em duas categorias: semântica estática e semântica de runtime.

A semântica estática são todas as coisas que podem ser inferidas sobre um programa apenas olhando para ele. As coisas que um compilador ou verificador de tipos sabe sobre um determinado programa.

A semântica de runtime define o que o código realmente significa e como o programa realmente é executado. Isso inclui os mecanismos subjacentes de alocação de recursos e agendamento.

Nosso objetivo com um runtime para agentes é transformar o fluxo de controle da orquestração em código, e queremos tornar o estado de execução persistente e tipado para que possamos usá-lo de forma confiável para direcionar a orquestração.

Alguns requisitos da VM

Existem 3 coisas específicas da VM com as quais nos importamos além da execução normal do TypeScript.

  1. Como um runtime de orquestração de agentes, ele precisa ser capaz de orquestrar agentes. Isso significa criá-los, rastrear seus ciclos de vida, etc. Queremos que o runtime seja capaz de executá-los de forma bloqueante ou não bloqueante e agendá-los corretamente.
  1. Queremos controle sobre as formas de saída dos agentes e queremos uma aplicação estrita do contrato de saída.
  1. Queremos ter controle em tempo de execução sobre recursos externos como modelos e custo.

Executando Agentes e Programas

Para executar um agente, selecionamos dois verbos básicos: run e spawn. Run executa um agente bloqueante em primeiro plano. Spawn executa um agente em segundo plano. Isso está alinhado com o entendimento comum de spawn, como posix_spawn, facilitando para um modelo entender nossos novos verbos, já que eles estão conceitualmente nos dados de treinamento. Spawn e run permitem que você invoque diretamente agentes e programas lidos do disco, retornando informações suficientes para um identificador de execução.

Run também suporta algumas coisas. Ele suporta tipos de saída diretamente aplicados através do zod @colinhacks, e suporta substituições diretas de modelo, facilitando a escrita e execução de programas onde faz sentido distribuir para vários modelos diferentes para diferentes soluções ou diferentes etapas de uma tarefa.

typescript
1function run<S extends z.ZodType>(
2 name: string,
3 options: ...
4): Promise<z.infer<S>>

Run permite que você encadeie diretamente subagentes inline.

typescript
1// execução de agente simples
2const out = await run({ type: "read", prompt: () => "Responda com: ok" })
3// execução nomeada (string = workflowId filho)
4const review = await run("reviewer", {
5 type: "general",
6 prompt: () => "Revise o diff",
7})
8// saída estruturada (resultado tipado)
9const Verdict = z.object({ risk: z.enum(["low", "high"]), why: z.string() })
10const v = await run({
11 type: "general",
12 prompt: () => "Avalie o risco",
13 output: Verdict,
14})

Spawn é semelhante ao run, mas cria um agente em segundo plano. Subagentes gerados com spawn não são aguardados e o fluxo de controle segue em frente. Spawn é muito útil para iniciar vários agentes de execução não bloqueante.

typescript
1// agente em segundo plano
2const h = await spawn("worker", { type: "general", prompt: "Tarefa longa" })

Interagindo com Agentes em Execução

Queremos ser capazes de realizar dois tipos de operações em agentes em execução: direcionamento e parada.

Uma mensagem de direcionamento é uma mensagem enviada ao agente que o LLM receberá enquanto estiver em execução para empurrá-lo em uma direção. Isso é útil para atualizar o contexto da tarefa do agente sem precisar destruir o worker.

O cancelamento também é importante; queremos ser capazes de derrubar ativamente um subagente se ele não deveria estar em execução.

Ser capaz de executar essas operações tanto do REPL ao vivo quanto de um programa pré-criado dá ao Slate sua capacidade de orquestrar tudo em tempo real. Ele pode definir dinamicamente a forma da orquestração em tempo de execução, ou pode criar e iterar em software real para fazer a orquestração.

O Slate é capaz de escrever programas em arquivos \.program.ts. Um arquivo de programa tem algumas coisas: seu nome (é assim que o Slate sabe o que é), uma descrição JSDoc e, em seguida, o corpo real do programa*. Uma declaração de programa se parece com isso:

typescript
1program(async (ctx) => {
2 // modelo barato para busca — só precisa encontrar arquivos
3 const findings = await run("search", {
4 type: "read",
5 prompt: "Encontre todos os arquivos relacionados à autenticação",
6 model: "codex/gpt-4.1-mini", // usa sua chave codex embutida
7 })
8})

Os programas seguem o mesmo modelo de execução assíncrona, o que nos permite executar um programa tanto em primeiro plano quanto em segundo plano e interagir com ele enquanto está em execução.

typescript
1// agente em segundo plano
2const h = await spawn("worker", { type: "general", prompt: "Tarefa longa" })
3await h.notify("foco no parser primeiro") // mensagem de direcionamento para o agente em execução
4const result = await h.result() // aguarda a conclusão depois// distribuir e depois reunir
5const a = await spawn({ prompt: "tarefa A" })
6const b = await spawn({ prompt: "tarefa B" })
7const [ra, rb] = [await a.result(), await b.result()]// programa em segundo plano
8import Audit from "deep-audit"
9const ah = await spawn(Audit, { input: { pr: 42 } })
10const auditResult = await ah.result()

Saída Estruturada e Estado

Esta é uma limitação primária de todos os outros sistemas até hoje. O estado, em todos os outros sistemas, é mal externalizado e não é isolado com segurança. Se for um arquivo no sistema, você não pode garantir que não haverá corrupção. Se puder, ainda não pode garantir a capacidade de análise. Você não pode se inscrever em mudanças de estado para conduzir operações e não pode garantir a adesão ao tipo.

Lembra como queríamos estado persistente que também fosse estruturado e pudesse ser referenciado?

O estado, no Onyx, é diferente. Namespaces de estado são declarados, nomeados diretamente e persistidos ao longo do tempo. Isso significa que um armazenamento de estado pode ser reutilizado repetidamente, permitindo que você construa sistemas de agentes de longa duração com dados reais.

Tanto agentes quanto código leem o estado, e o determinismo que queríamos de um runtime emerge disso. Os agentes leem o estado através de uma ferramenta dedicada que permite que eles sempre interajam com ele de forma segura e estruturada. Agentes e programas são ambos consumidores que podem ser direcionados para modificar o estado, o que permite que o runtime confie no objeto de estado para conduzir a orquestração.

O estado e a adesão ao esquema controlam a conclusão do subagente. Por causa disso, o estado fornece uma superfície unificada para direcionar todo o programa.

Objetos de estado também podem ser passados como variáveis de runtime para sessões filhas compartilhadas com o agente principal. Esse acesso por referência em toda a hierarquia de agentes (que é uma novidade) permite a comunicação entre agentes através de um canal de estado compartilhado.

akira - inline image

Loops de Longa Duração

Alguns programas precisam funcionar mais como sistemas em execução. Considere o openclaw, por exemplo. Você pode realmente representar o openclaw como um programa, dadas as primitivas certas. Para isso, usamos duas primitivas: sleep e checkpoint.

Sleep faz o que você espera: ele pausa.

Agora, aqui está a questão: digamos que você queira algum gerenciamento de tarefas de longa duração em segundo plano. Um gráfico de execução predefinido pode travar ou quebrar, e por isso é importante que o agente principal esteja ciente do status do programa.

Para suportar isso, introduzimos a primitiva checkpoint.

Um checkpoint pode ser qualquer coisa, mas a razão pela qual é chamado de checkpoint é porque ele notifica o agente principal com um objeto de forma fixa. Isso permite que o agente principal rastreie coisas como o progresso da tarefa e seja notificado diretamente sobre mudanças no estado do programa. Por sua vez, o agente principal pode então gerenciar mais efetivamente um programa em execução.

O Onyx suporta fazer um loop de agente como o Openclaw, ou seja, um agente persistente com um batimento cardíaco.

Isso é realmente muito legal: você pode compor os primitivos em um tipo completamente diferente de agente apenas usando um loop while, um sleep e um checkpoint.

O Openclaw pode ser simplesmente representado como um arquivo de programa!

typescript
1// Um programa para executar um loop de auto-pesquisa de longa duração
2for (let i = 0; i < maxExperiments; i++) {
3 const idea = await run("propose", { ... })
4 const result = await run("train", { ... })
5 checkpoint({ message: `experimento ${i}`, data: { idea, result } })
6 await sleep(30_000) // pausa entre experimentos
7}
8
9// Um programa para executar um agente persistente no estilo Openclaw
10while(true) {
11 const status = await run("status_check", { ...insira um modelo barato aqui... })if(status.pending_tasks) {checkpoint({ tasks: status.pending_tasks }) // retorna o estado importante e acorda o agente principal}
12 await sleep(30_000) // pausa entre experimentos
13}

Composição

Com o Onyx, o Slate pode escrever um *.program.ts para você. Isso persiste e pode (e deve) ser tratado como código normal. Ele tem tipos que vêm prontos, é executado em um runtime sem globals de runtime, e é apenas TypeScript, então seu modelo de composição é simplesmente importar e chamar outro programa.

Por ser apenas TypeScript, você obtém coisas como paralelismo (Promise.all) e loops de graça.

Veja como você importaria um programa e o usaria em outro:

typescript
1import Audit from "deep-audit"program (() => {const ah = await spawn(Audit, { input: { pr: 42 } })
2 const auditResult = await ah.result()
3 const fixer = await run("fixer", ... saída da auditoria) // isso executaria e corrigiria a saída do programa de auditoria.
4})

Semântica de erros

Erros, na VM ideal, são lançados de forma ruidosa. Eles devem ser lançados em problemas de sintaxe de runtime, falhas de agente, travamentos, etc.

Especificamente, definimos erros de orquestração como:

  • Um agente está bloqueado em uma tarefa
  • Um agente falhou ao completar uma tarefa
  • Um agente ficou sem etapas ou orçamento para uma tarefa
  • Um programa ficou sem orçamento para uma execução
  • O modelo de orquestração falhou ao escrever código sintaticamente correto
  • Uma modificação de estado ilegal sendo feita

Todos esses casos de erro específicos definem a semântica de runtime. Eles dizem: "Você pode esperar que este runtime lance um erro, porque vemos uma falha na execução do agente da mesma forma que vemos um erro no código". Pode parecer irritante no início, mas esse mecanismo de falha ruidosa lhe dá algo em troca: uma maneira explícita de se preparar e programar em torno de falhas. Então, na realidade, isso lhe dá mais controle, não menos.

typescript
1// erros são try/catch — igual a qualquer programa TypeScript
2program(async (ctx) => {
3 try {
4 const result = await run("risky-refactor", {
5 type: "general",
6 prompt: "Refatore o módulo de autenticação",
7 model: "claude-sonnet",
8 maxSteps: 20,
9 })
10 } catch (err) {
11 // o agente falhou — mas sabemos exatamente o porquê.
12 // o trace tem cada chamada de ferramenta, cada requisição de modelo,
13 // cada escrita de estado que levou até aqui.
14
15 // tente novamente com um modelo diferente
16 const result = await run("risky-refactor-retry", {
17 type: "general",
18 prompt: `A tentativa anterior falhou: ${err.message}. Tente uma abordagem diferente.`,
19 model: "claude-opus",
20 maxSteps: 30,
21 })
22 }
23})

Seleção de modelo, controle de orçamento e BYOK

A seleção de modelo integrada permite que você tenha um controle ainda mais preciso. A skill /models dá ao Slate acesso total à lista de modelos disponíveis, permitindo que o Slate crie programas com vários modelos diferentes realizando tarefas distintas. Quer que o Fable seja o planejador, mas o GLM 5.2 implemente dentro de um harness determinístico? Claro. Quer distribuir uma pergunta entre o Gemini, GPT 5.5 e DeepSeek? Isso também funciona.

Além disso, o runtime suporta dois tipos de substituições de configuração para programas:

  • Os modelos globais padrão usados para execução do agente
  • O orçamento para executar um programa

Você pode definir diretamente um orçamento de execução para limitar os gastos de um determinado loop.

Além disso, o runtime suporta o uso de suas assinaturas existentes do OpenAI e Github Copilot.

typescript
1program(async (ctx) => {
2 // modelo barato para busca — ele só precisa encontrar arquivos
3 const findings = await run("search", {
4 type: "read",
5 prompt: "Encontre todos os arquivos relacionados à autenticação",
6 model: "codex/gpt-4.1-mini", // usa sua chave codex integrada
7 })
8
9 // modelo de raciocínio para a parte difícil — ele precisa pensar
10 const plan = await run("architect", {
11 type: "general",
12 prompt: `Projete uma correção com base em: ${findings.output}`,
13 model: "openai/o3", // Acaba usando créditos da api
14 output: z.object({
15 approach: z.string(),
16 files: z.array(z.string()),
17 risk: z.enum(["low", "medium", "high"]),
18 }),
19 })
20
21 // modelo intermediário para implementação — ele só precisa editar
22 const handles = await Promise.all(
23 plan.files.map(f => spawn("fix-" + f, {
24 type: "general",
25 prompt: `Aplique esta correção em ${f}: ${plan.approach}`,
26 model: "anthropic/claude-sonnet-5",
27 maxSteps: 15,
28 }))
29 )
30 await Promise.all(handles.map(h => h.result()))
31})

Definindo a superfície de autoria

Houve dois fatores principais no design da superfície de autoria para programas: o quão fácil é para um agente entendê-la e o quão fácil é para um humano lê-la. Escolhemos verbos relativamente simples que soam como inglês e decidimos explicitamente que queríamos modelar a orquestração proceduralmente, em vez de declarativamente.

A seleção de TypeScript como linguagem também foi importante. Há tanto código TypeScript procedural por aí que um modelo entenderá implicitamente a semântica do TypeScript, mesmo sem pós-treinamento.

akira - inline image

Peças de engenharia da nossa fábrica de software

A próxima pergunta a ser respondida é: o que tudo isso te proporciona?

Isso te proporciona a capacidade de escrever software real para sua orquestração de agentes. Agora você pode projetar sua própria orquestração de agentes do início ao fim.

Você pode projetar a fábrica.

Por exemplo, você pode criar um programa que monitora o Github em um loop, e um programa separado que executa um agente de implementação com um agente de QA para revisão. Ambos são padrões individualmente úteis que você pode encontrar por aí. Então você pode juntá-los para criar um sistema que ouve comentários em um PR, gera um implementador para abordar esses comentários e, em seguida, gera um agente de QA para garantir que a correção seja válida.

Você pode então usar este programa conectado a uma fila de tarefas para delegar e monitorar o trabalho em sua base de código, e fazê-lo responder automaticamente a comentários de PR.

E você pode fazer tudo isso usando modelos de pesos abertos rápidos. Porque é apenas código, você não precisa de um LLM poderoso para pensar na orquestração depois que ela é criada pela primeira vez.

Agora, a parte divertida: é hora de compartilhar alguns dos programas que usamos para aumentos massivos de produção.

Pesquisa Profunda de Base de Código

Usamos este programa para ajudar a definir o escopo das tarefas. Ele faz uma pesquisa profunda sobre o estado do nosso monorepo e prepara um pacote de pesquisa para um implementador consultar. Nós o usamos o tempo todo. Parece caro, mas na verdade não é. Você pode executar este programa no Slate com DeepSeek V4 Flash e o processo de pesquisa é completo, mas extremamente barato.

akira - inline image

Goal-Review-PR

Este é um que usamos para implementar uma tarefa assim que a pesquisa é concluída. Felizmente, quando a pesquisa chega ao programa de meta, a maior parte da ambiguidade da tarefa já foi resolvida, o que torna a execução da tarefa ainda mais rápida. Carregar a pesquisa com um modelo OSS leve facilita o uso de um modelo caro como Opus para o que importa: escrever código realmente bom e verificar o estado do sistema. Você poderia até modificar o programa para usar GPT 5.5 para revisar adversariamente o trabalho do Opus 4.8.

akira - inline image

Autopesquisa como um Programa

A Autopesquisa[[9]](http://localhost:5173/blog/onyx#ref-9) era originalmente totalmente impulsionada por LLM. Direcione um agente para o prompt program.md e ele decide o que tentar e como progredir.

Não é surpresa que a Autopesquisa é, na verdade, apenas um programa.

Os programas de agente permitem que você inverta isso e coloque o fluxo de controle no runtime. O programa possui o fluxo de controle enquanto os agentes fazem o trabalho com efeitos colaterais (editar código, executar git, fazer SSH para a GPU remota, treinar). Para o programa de autopesquisa, a decisão de manter/reverter é código determinístico:

typescript
1kept = status === "ok" && valBpb != null && valBpb < best

No nosso caso, o programa executa um agente de configuração para preparar um repositório novo e verificar se a A100 remota está acessível. Se a configuração falhar, ele retorna antecipadamente com uma saída limpa com base em um valor tipado. Caso contrário, ele entra no loop de experimentos.

Cada experimento recebe um novo agente. O agente recebe a melhor configuração atual e o histórico de ideias e resultados anteriores, para que não se repita e possa construir sobre o que foi mantido. Ele propõe uma alteração, edita train.py, faz commit, rsync para a máquina remota, treina e classifica o resultado.

O agente e o programa compartilham estado. O agente escreve dados no estado, e o programa avalia o estado para o fluxo de controle. Com base no resultado, um agente gravador atualiza results.tsv e, opcionalmente, redefine a execução se o programa decidiu descartar o experimento. Isso deixa o HEAD do git sempre apontando para o melhor branch atual da árvore de experimentos.

Existem duas diferenças principais que valem a pena notar: 1) isso é executado em um programa, então podemos gerar um novo agente por experimento e 2) podemos decidir qual tarefa o agente deve realizar com base no estado ativo do programa.

akira - inline image
akira - inline image

E fica assim no código:

typescript
1// ---------- Programa ----------
2
3program(async (ctx) => {
4 const c = cfg(ctx.input)
5 const total = ctx.input?.maxExperiments ?? 20
6
7 const setup = await run("ar-setup", {
8 prompt: setupPrompt(c),
9 type: "general",
10 maxSteps: 40,
11 output: SetupResult,
12 })
13 if (!setup.ready) {
14 return { aborted: true, reason: `configuração falhou: ${setup.note}`, setup }
15 }
16
17 let best = c.baselineValBpb
18 let bestCommit = setup.baselineCommit
19 const history = []
20
21 for (let i = 1; i <= total; i++) {
22 let exp
23 try {
24 exp = await run(`ar-exp-${i}`, {
25 prompt: experimentPrompt(c, i, total, best, historyText(history)),
26 type: "general",
27 maxSteps: 80,
28 output: ExperimentResult,
29 })
30 } catch (err) {
31 // Agente com erro/bloqueado — trata como uma falha, restaura o repo para o melhor, continua.
32 exp = {
33 description: `experimento ${i} erro de agente`,
34 commit: "error",
35 status: "crash",
36 valBpb: null,
37 peakVramMb: null,
38 numSteps: null,
39 exitCode: -1,
40 retries: 0,
41 note: String(err?.message ?? err).slice(0, 200),
42 }
43 }
44
45 const kept = exp.status === "ok" && exp.valBpb != null && exp.valBpb < best
46
47 await run(`ar-record-${i}`, {
48 prompt: recordPrompt(c, exp, kept, bestCommit),
49 type: "general",
50 maxSteps: 20,
51 output: RecordResult,
52 })
53
54 if (kept) {
55 best = exp.valBpb
56 bestCommit = exp.commit
57 }
58
59 history.push({
60 idx: i,
61 description: exp.description,
62 status: exp.status,
63 valBpb: exp.valBpb,
64 kept,
65 commit: exp.commit,
66 retries: exp.retries,
67 })
68
69 await checkpoint({
70 name: `experimento-${i}`,
71 message: `exp ${i}/${total}: ${exp.status}${kept ? " MANTIDO" : ""} val_bpb=${exp.valBpb ?? "n/a"} (melhor=${best})`,
72 data: { i, total, status: exp.status, valBpb: exp.valBpb, kept, best, bestCommit },
73 })
74 }
75
76 const kepts = history.filter((h) => h.kept)
77 return {
78 baselineValBpb: c.baselineValBpb,
79 bestValBpb: best,
80 bestCommit,
81 improvement: c.baselineValBpb - best,
82 experimentsRun: history.length,
83 kept: kepts.length,
84 crashes: history.filter((h) => h.status === "crash").length,
85 infraFails: history.filter((h) => h.status === "infra_fail").length,
86 localRepo: c.localRepo,
87 branch: c.branch,
88 history,
89 }
90})

Trabalho futuro

O único requisito restante da VM que ainda não definimos é o modelo de durabilidade para programas. Não está claro qual é o modelo correto para retomar e gerenciar o ciclo de vida de um programa, e qual nível de controle deve ser exposto no runtime.

Além disso, há tantas coisas empolgantes que adicionaremos para suportar diferentes cargas de trabalho e formatos de tarefas, para que possamos escrever software real para orquestrar agentes de forma melhor. Temos certeza de que muitos dos padrões surgirão de pessoas usando programas de maneiras criativas por conta própria.

Estamos realmente ansiosos para ver o que você vai construir.

  • Equipe RL

Referências

  1. Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models," 2022
  2. Geoffrey Huntley, "The Ralph Loop"
  3. Geoffrey Huntley, "everything is a ralph loop," January 2026
  4. Zhang, Kraska, Khattab, "Recursive Language Models," December 2025
  5. Wang et al., "Executable Code Actions Elicit Better LLM Agents," ICML 2024
  6. OpenAI, "Introducing Deep Research," February 2025
  7. Cursor, "Scaling Agents," January 2026
  8. OpenAI, "Using Goals in Codex"
  9. Andrej Karpathy, "autoresearch"
  10. Anthropic, "Introducing Dynamic Workflows in Claude Code"
  11. OpenAI, "Learning to Reason with LLMs," September 2024
  12. Anthropic, "A harness for every task: dynamic workflows in Claude Code"
  13. Random Labs, "Skill Chaining"

Turn one viral article into a full content workflow

Collect the source, decode the pattern, create assets, draft the story, and distribute from one AI workspace.

Explore YouMind
Para criadores

Transforme seu Markdown em um artigo 𝕏 impecável

Quando você publica seus próprios textos longos, formatar imagens, tabelas e blocos de código para o 𝕏 é uma dor de cabeça. O YouMind transforma um rascunho completo em Markdown em um artigo 𝕏 impecável e pronto para publicar.

Experimente Markdown para 𝕏

Mais padrões para decifrar

Artigos virais recentes

Explorar mais artigos virais