Apostamos cedo na ideia de dar ao LLM o poder de executar código arbitrário. Este post é sobre o porquê dessa aposta e o que é necessário para executar milhares desses ambientes isolados (sandboxes) de uma só vez, criando e encerrando instâncias tão rápido quanto as pessoas iniciam e terminam conversas com o agente.
Cada conversa que um usuário tem com o agente Adapt é sustentada por seu próprio computador. Não apenas um contêiner restrito em um servidor compartilhado, mas uma VM isolada com a qual o modelo pode fazer o que quiser: instalar softwares, escrever e executar programas, navegar na web, falar com APIs. Chamamos isso de sandboxes, e eles são uma das primitivas centrais sobre as quais o Adapt é construído.
Controle Total
LLMs são gênios da programação, e meu trabalho tem sido, em grande parte, construir o ambiente de desenvolvimento perfeito para eles trabalharem.
A maneira usual de conectar uma IA ao mundo exterior é criar integrações manualmente — um conector sob medida para o GitHub, outro para o HubSpot, outro para o Stripe — ou esperar que cada serviço lance um servidor MCP. Isso simplesmente não escala, e eu não sou muito fã de escrever código de integração dia após dia.
Então, em vez de fazer esse trabalho nós mesmos, deixamos o modelo fazê-lo. Qualquer serviço que exponha uma API pode ser acessado pelo Adapt, porque damos ao LLM tudo o que ele precisa para escrever o script ou programa que se comunica com essa API. Isso é uma grande parte do que queremos dizer quando chamamos o Adapt de uma "inteligência horizontal": ele não está conectado a uma lista fixa de ferramentas, ele pode construir a ferramenta de que precisa na hora.
Fundamental para isso é dar ao LLM acesso total ao sandbox. Em vez de entregar ao modelo um conjunto estático de linguagens e ferramentas de CLI com acesso limitado ao sistema de arquivos, damos a ele acesso completo a tudo. Ele roda como root. E, embora nossos sandboxes venham com runtimes comuns como Node e Python, e se o melhor SDK para a API de algum serviço for escrito em Go? O modelo pode simplesmente ir lá, instalar e executar.

O LLM precisa escrever um programa em Go? Vá em frente, instale o Go e execute-o.
Então, se estamos permitindo que o modelo instale o que quiser e execute código que nenhum humano verificou, como garantimos a segurança? Felizmente, não somos as primeiras pessoas que precisaram executar código não confiável. Existem dois runtimes seguros muito populares para exatamente isso: gVisor e Firecracker. Nossa jornada até agora nos deixou muito familiarizados com ambos.
Do gVisor ao Firecracker
Nossa primeira incursão em sandboxes seguros para LLMs foi a abordagem "fácil": executar cada sandbox com gVisor sobre o GKE (Google Kubernetes Engine), usando o GKE Sandbox. Já executamos todos os nossos outros serviços no GKE, então este foi o passo natural para nós.
O gVisor fica entre um contêiner e o kernel do host. Em vez de deixar um programa fazer chamadas de sistema (syscalls) diretamente para o kernel real do Linux — algo que você realmente não quer que um código não confiável manipule —, o gVisor intercepta essas chamadas em seu próprio kernel de espaço de usuário e as atende. Você obtém a maior parte da conveniência de um contêiner normal com uma superfície de ataque muito menor. E o GKE Sandbox empacota tudo isso. Você implanta Pods (contêineres) e eles rodam de forma transparente sob o gVisor, sem que precisemos fazer muita configuração de infraestrutura.
E isso funcionou muito bem no início. Definimos o sandbox "base" como uma imagem Docker e deixamos o GKE escalá-lo para o número de sandboxes que precisávamos a qualquer momento. Atualizações para o software que os sandboxes carregavam eram simples atualizações de Dockerfile e um aumento de versão em um manifesto.

Centenas de Pods de sandbox rodando sob o GKE Sandbox.
Mas a mesma abstração que tornou o gVisor fácil é a que continuamos combatendo. Como o gVisor reimplementa a superfície de chamadas de sistema do Linux no espaço de usuário, nem tudo se comporta exatamente como faria em um kernel real, e as cargas de trabalho que nosso modelo imagina são tão imprevisíveis quanto possível. A interceptação que lhe dá segurança também custa caro em trabalhos pesados de syscall e I/O. E depender do GKE para todo o ciclo de vida significava que as partes que mais queríamos controlar — tempo de inicialização, densidade de empacotamento, rede e a agressividade com que reciclamos máquinas — eram as partes sobre as quais tínhamos menos controle. O Pod "OutOfcpu" perdido acima é o tipo de coisa que você começa a ver quando está forçando o agendador de outra pessoa mais do que ele deseja ir.
Foi isso que nos levou ao Firecracker.
MicroVMs Firecracker são máquinas virtuais reais, cada uma com seu próprio kernel convidado, rodando com virtualização de hardware, mas reduzidas para inicializar em uma fração de segundo com apenas alguns megabytes de sobrecarga. É a mesma tecnologia que a AWS construiu para empacotar um número enorme de cargas de trabalho Lambda e Fargate em hardware compartilhado. Isso nos dá um limite de isolamento mais forte do que um kernel compartilhado, inicializa rápido o suficiente para parecer instantâneo e é pequeno o suficiente para empacotar muitas delas em um único host.
A compensação é que o Firecracker lhe entrega uma VM e pouco mais. Não há uma camada estilo GKE fazendo o agendamento, rede e orquestração do ciclo de vida. Então, nós construímos uma, e a chamamos de orc.
O rootfs é apenas uma imagem
Uma coisa da qual não queríamos abrir mão na mudança dos contêineres foi definir um sandbox como um Dockerfile simples. Contêineres tornam isso trivial; VMs tradicionalmente não, já que uma microVM inicializa um sistema de arquivos raiz (root filesystem), não uma imagem OCI.
Então o orc faz a ponte entre os dois. Quando solicitado a criar uma VM, ele pega uma imagem Docker/OCI comum e gera o sistema de arquivos raiz da VM a partir dela em tempo real, armazenando o resultado em cache para que inicializações posteriores da mesma imagem sejam rápidas. Nosso sandbox base ainda é apenas um Dockerfile, e o orc o transforma em um rootfs inicializável no momento da solicitação.
Isso mantém nosso fluxo de trabalho idêntico aos dias de GKE — editar um Dockerfile, enviar um novo sandbox — enquanto rodamos em VMs reais por baixo. E isso abre uma porta pela qual estamos apenas começando a passar. Como qualquer imagem OCI pode se tornar uma microVM, podemos inicializar sandboxes a partir de imagens diferentes da padrão. Quer uma VM que já tenha Postgres e pgvector integrados? Aponte o orc para essa imagem e você a terá como sua própria máquina isolada. O sandbox deixa de ser um ambiente fixo único e se torna "qualquer imagem que o trabalho precisar, inicializada como sua própria VM".
Executando em Escala
E aqui está o que torna este um problema genuinamente difícil: cada conversa recebe seu próprio sandbox. Uma máquina por conversa. A qualquer momento, temos milhares delas ativas, e esse número nunca está parado. Toda vez que alguém abre um chat, um sandbox precisa aparecer; toda vez que um chat fica silencioso, um precisa desaparecer para que não paguemos por ele. Estamos constantemente criando e encerrando sandboxes.
Dois números dominam tudo: quão rápido podemos preparar um sandbox e quantos podemos colocar em um host.
Latência de inicialização. Uma microVM Firecracker inicializa em algumas centenas de milissegundos. Isso é rápido o suficiente para que não mantenhamos um pool "quente", o que é uma das vitórias mais silenciosas da mudança. Sob o GKE, teríamos que manter capacidade extra por perto para esconder o tempo de inicialização. Com o orc, um novo sandbox está pronto antes que você perceba, então apenas criamos um sob demanda quando um chat começa e o encerramos quando o chat termina. Chega de pool ocioso para cuidar ou pagar.
Densidade. Como cada microVM é minúscula, podemos colocar muitas delas em um host físico. Dimensionamos a CPU e a memória de cada sandbox para o que ele realmente precisa, em vez de superdimensionar, o que é o que nos permite executar milhares delas de forma econômica.
O orc em si é deliberadamente pequeno. É um plano de controle que fala uma API simples: criar uma VM com N vCPUs e M megabytes de memória a partir de uma determinada imagem, transmitir comandos para dentro dela, ler e gravar arquivos lá dentro, marcá-la com etiquetas para que possamos encontrá-la mais tarde e excluí-la quando terminarmos. Cada convidado executa um pequeno processo de inicialização como PID 1 e obtém sua própria rede isolada. É basicamente isso. A mágica não é nenhum truque inteligente, é que essas primitivas são entediantes e rápidas o suficiente para rodar uma frota inteira.
A recompensa por toda essa infraestrutura é a coisa com a qual começamos: um modelo que pode instalar qualquer coisa, escrever um programa, acessar uma API e lhe entregar uma resposta, tudo em um computador real.





