Ship 26 NYC - Oficina - Mini-trabalhadores

VVercel
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00Olá a todos. Meu nome é Jonathan Clem, ou podem me chamar de Jay Clem, e sou engenheiro de software na
00:00:11Notion, onde trabalho na nossa plataforma para desenvolvedores, mas estou focado especialmente em um produto mais novo
00:00:17do qual vocês talvez já tenham ouvido falar, chamado Notion Workers. Neste workshop de hoje, vou dar
00:00:23a vocês uma visão geral do que são os Notion Workers e por que os construímos usando o Vercel
00:00:28Sandbox. E depois vou guiá-los por uma pequena versão demonstrativa dos Workers,
00:00:36para que vocês possam ter uma noção de como um produto assim é construído usando o Vercel Sandbox.
00:00:43Se você não conhece os Workers, eles são um SDK e um ambiente de execução onde você pode escrever
00:00:50código personalizado e estender o Notion com ele. Assim, você pode sincronizar dados de terceiros no Notion,
00:00:57escrever chamadas de ferramentas personalizadas para seus agentes. Vimos pessoas fazendo coisas incríveis e loucas, como pedir
00:01:04compras de mercado com o Notion Workers ou controlar a casa inteligente. Também vimos fluxos de trabalho realmente complexos,
00:01:11especialmente nas áreas de TI e segurança. O bom dos Workers é que não há
00:01:17infraestrutura para você gerenciar. Você apenas escreve o código ou pede para um agente de codificação escrever, e o Notion
00:01:23garante que ele esteja sempre ativo, disponível e rodando. Isso tem sido um grande benefício para os usuários do Notion,
00:01:30especialmente os desenvolvedores. Eles não precisam mais esperar que o Notion crie integrações nativas
00:01:36para eles. Qualquer coisa que eles gostariam que o Notion fizesse, mas não faz, eles mesmos podem simplesmente
00:01:42escrever. Então, quando começamos a planejar o desenvolvimento do Notion Workers, minha principal preocupação era
00:01:53a segurança. Eu tenho um pouco de experiência construindo plataformas que executam código não confiável de usuários.
00:01:59Trabalhei no GitHub Actions, por exemplo, por muito tempo. E eu estava preocupado principalmente com a
00:02:04dificuldade de construir a infraestrutura para um produto como este e, especialmente, com a segurança. Há muitas
00:02:09coisas com as quais se preocupar. Por exemplo, você quer garantir que o código escrito pelos usuários
00:02:14não consiga acessar os bancos de dados do Notion, é claro, ou serviços do Notion que normalmente não deveriam poder
00:02:20acessar. Você também quer garantir que um usuário não afete os outros. Você não quer que o código de um usuário
00:02:26possa acessar o código de outro usuário ou, obviamente, seus segredos e coisas do gênero.
00:02:31Isso não é apenas sobre segurança. É também sobre justiça e compartilhamento de recursos. Se houver um usuário
00:02:37fazendo algo que consome muita CPU e muita memória, você quer garantir que isso não afete
00:02:42injustamente outros usuários que estão tentando fazer coisas ao mesmo tempo. Vou tirar um minuto
00:02:48para me desviar um pouco e contar uma história. Esse compartilhamento de recursos é algo especialmente
00:02:54complicado. Isso provavelmente vai consumir parte do meu tempo, mas eu gosto dessa história. Quando estávamos construindo
00:02:58o GitHub Actions — e isso foi algo em que pensamos para os Workers também —, assim que você cria uma plataforma
00:03:02de execução de código arbitrário, as pessoas tentam imediatamente minerar criptomoedas nela.
00:03:07E eu estava conversando com alguém há algumas semanas e a pessoa disse: “Bem,
00:03:10como você detecta isso? Dá para saber se a CPU estiver travada em 100%, certo?”
00:03:14Na verdade não, porque assim que sua plataforma faz sucesso, os mineradores de cripto começam a
00:03:20compartilhar scripts entre si para modificar a forma como executam as instruções
00:03:26de CPU, de modo que pareça uma atividade totalmente inofensiva, mas eles estão usando
00:03:31a quantidade máxima absoluta de recursos possível sem serem sinalizados pelo sistema.
00:03:37Então é extraordinariamente difícil e leva anos e muitas camadas de segurança e
00:03:42observabilidade para acertar isso. Outra coisa com a qual você precisa se preocupar em uma plataforma
00:03:48de execução de código é com pessoas realizando ataques de negação de serviço ou usando a plataforma
00:03:54para redes de comando e controle de botnets. E nós simplesmente não queríamos ter que nos preocupar com tudo
00:04:00isso logo de cara no Notion Workers. Queríamos focar no que amamos fazer, que é
00:04:07entregar um produto que nossos usuários adorem usar. Por isso, decidimos construir com o Vercel Sandbox.
00:04:14Vimos de imediato que o Vercel Sandbox era uma infraestrutura muito sólida que resolvia
00:04:18grande parte desse problema para nós logo de início. Vou mostrar uma demonstração bem rápida
00:04:25do que são os Notion Workers. Eu tenho um agente personalizado aqui, e o trabalho dele é me dizer
00:04:33se este workshop está amaldiçoado ou não. Escrevi um Worker chamado “Mercury Retrograde”. Não sei se alguém
00:04:40conhece isso, mas quando Mercúrio está alinhado de forma a ficar retrógrado, costuma ser
00:04:47um mau sinal. Esse Worker tem uma única chamada de ferramenta personalizada que usa uma API que encontrei, a qual só faz
00:04:54dizer se Mercúrio está em retrógrado ou não. Vou enviar um comando para o meu agente e perguntar: “Meu
00:05:01workshop está amaldiçoado?”, e ele vai pensar um pouco e então, desde que a internet ajude,
00:05:14começaremos a vê-lo fazendo a chamada de ferramenta. Ele está usando meu Worker do Mercúrio retrógrado, chamando a
00:05:22única ferramenta que esse Worker expõe e descobrindo se Mercúrio está retrógrado ou não, e depois o
00:05:27agente vai nos responder. Acho que conectei isso ao GPT-54 nano ou algo assim, então costuma ser muito
00:05:37mais rápido. Infelizmente, acho que é a internet. Se alguém por acaso conferiu se Mercúrio está em
00:05:46retrógrado, eu sei de antemão que está. E por isso provavelmente isso está acontecendo. Vou apenas
00:05:53pular essa parte. Voltaremos em um minuto para ver se eventualmente recebemos uma resposta. Mas acho que já
00:05:59sabemos a resposta, pelo visto. Certo, agora que vocês têm uma ideia do que são os Notion Workers, vou
00:06:05guiá-los por um pequeno script que escrevi e que realiza muitas das tarefas básicas envolvidas em pegar
00:06:13o código do usuário, implantá-lo, executá-lo de forma segura e informar o agente sobre esse código para que ele possa
00:06:20fazer chamadas de ferramenta para ele. Isso chegou a ser concluído? Ah, sim, aqui está. Ah, acho que talvez,
00:06:28devo ter tirado a API do Mercúrio retrógrado do ar, porque pelo visto a API não respondeu.
00:06:35Enfim, terei que abrir um chamado em algum lugar. Legal. Então tenho um script básico. Não espero
00:06:40que vocês acompanhem tudo escrevendo código nem nada disso. Vou passando rápido
00:06:44para dar uma ideia de alguns dos problemas que resolvemos e que você precisa resolver ao criar um
00:06:49produto como este. Mas, se quiserem, há um repositório make notion / vercel ship 2026 workers
00:06:55que tem todo o código que vou executar aqui.
00:07:01Legal. Deixe-me abrir meus outros slides aqui.
00:07:08Então, qual é o nosso objetivo? Temos um agente de chat em tempo real. Vamos permitir que ele chame ferramentas
00:07:16definidas por código personalizado de usuários que não confiamos ou cujo conteúdo desconhecemos.
00:07:22E vamos construir isso com o Vercel Sandbox e o serviço de armazenamento em blob da Vercel.
00:07:26Vou fazer uma demonstração rápida aqui e tomara que não estejamos realmente amaldiçoados.
00:07:31Você pode simplesmente passar uma única mensagem. Vou dizer “oi” e espero que tenhamos uma resposta
00:07:36de volta. Vai ser um pouco lento porque, como verão, ele está fazendo algumas implantações em segundo plano.
00:07:40Ele chamou uma ferramenta desta vez. Chamou uma ferramenta chamada “say hello” e ela decidiu me cumprimentar.
00:07:47Vou enviar uma mensagem normal perguntando quanto é um mais um, para que possamos vê-lo
00:07:52transmitindo uma resposta normal.
00:07:58Se você já usou o Vercel AI SDK, isso é apenas o ciclo do agente de ferramentas. Legal. Recebemos uma resposta
00:08:04em tempo real. Você também pode chamar Workers muito mais complexos. Tenho outro aqui que vou mostrar.
00:08:14Com este, estamos dizendo ao agente que estou no endereço deste prédio,
00:08:17que tenho 90 minutos, quero ver algum ponto histórico e não quero caminhar mais de 15 minutos.
00:08:24Isso ilustra por que eles às vezes são melhores que servidores MCP. Com um servidor
00:08:29MCP, você tem várias chamadas de ferramentas isoladas que pode fazer. Se estiver tentando fazer algo
00:08:33complexo, precisa descrever essas etapas ao agente, e ele vai executar uma etapa, gastar alguns
00:08:38tokens de raciocínio e executar a etapa seguinte. Então, isso vai chamar um Worker que possui um algoritmo
00:08:45de busca grande e complexo, que usa APIs de geolocalização, tempo de trânsito e planejamento de rotas de Nova York.
00:08:52Ele se chama “plan outing”.
00:08:56E esse Worker vai responder com uma sugestão de local histórico para visitarmos.
00:09:00E parece que ele encontrou o marco da Freedom Tree, que é um memorial de prisioneiros de guerra perto do
00:09:07City Hall Park. Legal. Vamos ver como isso funciona. O que é um Worker?
00:09:14É um código de usuário que, neste caso, está definido em um arquivo. Normalmente, isso seria definido
00:09:20pelos seus usuários ou em algum lugar no GitHub e implantado via CLI. Mas temos alguns exemplos
00:09:26no repositório. E cada um desses Workers precisa expor o nome da ferramenta, uma descrição para
00:09:34que o agente saiba qual usar, o esquema de entrada para o agente saber quais dados precisa
00:09:38fornecer ao chamá-la, e uma função de execução que define o código executado
00:09:44quando a ferramenta é acionada. Vamos ver um exemplo. Aqui está a ferramenta de saudação que você viu
00:09:51antes. É muito simples. Este Worker é um módulo JavaScript neste arquivo index.ts. Ele exporta um único
00:09:59Worker chamado “say hello”. Vocês verão como funciona esse processo de compilação, mas, neste exemplo,
00:10:04todas as chaves de exportação do nosso módulo estão mapeadas para os nomes das nossas ferramentas. Então esta ferramenta vai se chamar
00:10:09“say hello”. Ela tem uma descrição simples e um esquema de entrada. Neste caso, estou usando o Zod para definir
00:10:16o esquema. E depois o converto em JSON Schema. Esse é o formato que esses agentes esperam.
00:10:22E depois há uma função de execução simples. Mas você pode criar outras muito mais complexas. Se eu
00:10:27abrisse o fluxo de trabalho “plan outing”, veriam que ele tem uma descrição muito mais longa informando o agente
00:10:34sobre como essa ferramenta funciona, quando chamá-la e o que ela faz. E o script para isso é muito, muito
00:10:39mais longo e complexo. Não vou passar por tudo isso. Eu li apenas pequenas partes
00:10:45dele, mas funciona muito bem. É a realidade em que estamos agora. Então esses Workers serão
00:10:54compilados e implantados no armazenamento em blob da Vercel. Depois, vamos obter o conteúdo dos
00:10:59Workers de alguma forma. E vamos expô-los a um agente. Então, essas ferramentas serão
00:11:03executadas com segurança em uma sandbox. A parte de compilação nós vamos pular. Com os Notion Workers, temos um
00:11:09processo de implantação e compilação na nuvem. Neste caso, eu já pré-compilei esses Workers no disco. Cada
00:11:15Worker tem um arquivo tarball com todo o código TypeScript compilado, dependências e coisas assim.
00:11:22Não é a parte mais interessante, então vou simplesmente pular isso.
00:11:26A questão principal é: como vamos do código de usuário inédito e não confiável
00:11:34para ferramentas expostas e executadas com segurança pelo agente? Há duas partes nisso.
00:11:39A primeira é: uma vez que temos esse código no armazenamento em blob, por exemplo,
00:11:43como descobrimos o que há nele? Precisamos informar o agente, antes que ele
00:11:48chame a ferramenta, qual é o nome dela, o esquema de entrada e a descrição
00:11:53da ferramenta. E a segunda questão é: quando o agente decide chamar essa ferramenta,
00:11:58como a executamos com segurança? Uma das coisas que adoro na solução que adotamos para o
00:12:04SDK do Notion Workers — e que repetimos aqui — é que o código se autodescreve. O que nós não
00:12:11queríamos ao projetar o Notion Workers era que os usuários tivessem que escrever um código TypeScript
00:12:16definindo a ferramenta e depois criar um arquivo de manifesto estático descrevendo
00:12:21seus Workers, basicamente reescrevendo a mesma coisa. Não queríamos um processo de compilação local chato em que
00:12:27tivessem que rodar um script, fazer análises, compilar no disco e depois implantar. Queríamos começar
00:12:34com a melhor experiência para o desenvolvedor: você apenas escreve a ferramenta, e o problema complexo
00:12:40de descobrir como extrair as informações fica por nossa conta.
00:12:46Este é um diagrama muito simples, mas antes de vermos o código, vou explicar
00:12:52como resolveremos isso. Temos o código compilado do usuário no armazenamento em blob.
00:12:59Usaremos esse código para criar uma sandbox. Ao executar comandos na sandbox, o código desse usuário
00:13:04estará na raiz do espaço de trabalho. Vamos importar o arquivo index.js que ele escreveu.
00:13:11Isso nos dará os nomes das exportações, as descrições e os esquemas de entrada.
00:13:19É aqui que as coisas ficam um pouco estranhas, mas funciona muito bem. Vamos chamar
00:13:23json.stringify nesse módulo e exibi-lo no stdout. Tudo isso acontece na
00:13:29sandbox, e nosso script de implantação lê essa saída padrão e a analisa: “Certo,
00:13:34agora sei o nome de todas as ferramentas deste Worker, os dados de entrada e a descrição”. E neste
00:13:40caso, vamos passar isso diretamente para o agente. Mas no caso dos Notion Workers, por exemplo,
00:13:44isso faz parte do nosso pipeline de implantação. Salvamos essas informações em um banco de dados para que,
00:13:49sempre que você rodar um agente personalizado, as descrições das ferramentas sejam buscadas nesse
00:13:53banco de dados. Ficou claro até aqui como isso funciona? Beleza. Vamos dar uma olhada no script de implantação.
00:14:02Tenho que voltar ao meu primeiro commit aqui. Esse script tem toda a nossa implantação e a chamada
00:14:12ao agente integradas. É super simples. Neste caso, estamos apenas iterando sobre todos os diretórios
00:14:18e Workers. Cada um desses subdiretórios contém o código do Worker, como vocês viram há pouco.
00:14:23Nosso trabalho é descobrir como extrair as informações das ferramentas de cada Worker e preencher
00:14:29este objeto de ferramentas. Isso é passado para um agente de ciclo de ferramentas. Isso é apenas parte do AI SDK feito
00:14:35pela Vercel. E então vamos enviar a mensagem do usuário para esse agente, transmitir a saída e gravá-la
00:14:42no stdout a partir do script. A primeira coisa que precisamos descobrir é como enviar
00:14:48o código-fonte. Essa parte é bem fácil e rápida. Em vez de codificar ao vivo, vou apenas pular
00:14:53entre esses blocos para que vocês não precisem me ver digitando. Prometo que sei escrever
00:14:59código, só acho que ninguém quer ver isso. Avançando um pouco, temos esta função
00:15:06chamada upload source na qual vou me aprofundar. Como podem ver, importei o SDK do Vercel Blob. É bem
00:15:13simples aqui. Olhando para essa função upload source, basicamente chamamos esta função put
00:15:20e indicamos que queremos armazenar o pacote do usuário sob o nome do Worker / bundle.tar.gzip.
00:15:29Vamos transmitir o arquivo do disco e armazená-lo no armazenamento em blob. Feito isso,
00:15:35podemos criar sandboxes a partir desse blob. Assim resolvemos a parte do envio.
00:15:43A próxima coisa que precisamos fazer é pegar esse código-fonte empacotado e criar
00:15:49uma sandbox a partir dele. Para isso, importei o SDK do Vercel Sandbox. Há outra função auxiliar
00:15:56aqui chamada create sandbox. Vou pular algumas partes dela. Mas, essencialmente, o que estamos
00:16:03fazendo é usar URLs pré-assinadas para passar esse objeto no armazenamento em blob
00:16:10para o serviço de sandbox. Assim, o serviço de sandbox — acho que a expiração está configurada para 10 minutos —
00:16:16pode puxar esse arquivo do armazenamento em blob e usá-lo para preencher uma sandbox. Esse é um dos
00:16:23recursos que realmente gosto no Vercel Sandbox: ele funciona com arquivos tarball como esse. É fácil
00:16:28gerar um tarball com o código do usuário; você pede para criar uma sandbox a partir desse arquivo,
00:16:35o serviço o baixa do armazenamento ou de qualquer URL fornecida e o descompacta automaticamente
00:16:40na raiz do espaço de trabalho. Assim, todos os arquivos ficam prontos para uso. Fazemos isso apenas chamando
00:16:46sandbox.create. Ainda não chegamos à parte supercomplicada, mas chegaremos lá em breve.
00:16:52Outro detalhe é que, neste exemplo, por concisão, estou criando sandboxes do zero a cada
00:16:57execução. Na prática, você deve usar snapshots para não ter que reinstalar a sandbox
00:17:02ou baixá-la novamente do armazenamento em blob da Vercel (ou de outro armazenamento em nuvem usado)
00:17:09toda vez que uma ferramenta for chamada. Há basicamente um mecanismo de cache embutido
00:17:15na plataforma. Eu só o pulei aqui para fins de simplicidade.
00:17:19Isso é a criação da sandbox, e é aqui que as coisas ficam interessantes.
00:17:23A próxima coisa a fazer é extrair as informações sobre as ferramentas
00:17:31definidas naquele Worker dentro da sandbox recém-criada.
00:17:36Vou fazer uma pausa rápida aqui. Há algumas dicas espalhadas pela apresentação.
00:17:40Ao construir um serviço em nível de produção como este, você deve sempre tentar
00:17:44encerrar suas sandboxes. Neste caso, vocês verão o que a extractTools faz, mas quando terminamos,
00:17:51garantimos a exclusão explícita da sandbox. Na prática, também vale a pena enfileirar
00:17:56uma tarefa assíncrona de limpeza. Você não quer deixar essas sandboxes abertas indefinidamente.
00:18:00Vamos ver o que a função extractTools faz. Temos nossa sandbox, e gosto bastante destes
00:18:06exemplos porque parecem engraçados de tão simples, mas funcionam muito bem. Estamos executando um
00:18:15comando Node usando o próprio binário do Node na sandbox e, no script, importamos o módulo que
00:18:23o usuário escreveu, que é apenas o index.js. Convertemos esse objeto em string e depois chamamos
00:18:30o console.log. Lembre-se de que sandboxes não são como um servidor web onde você envia
00:18:36uma requisição e recebe uma resposta. Toda entrada e saída ocorre via execução de comando e,
00:18:42então, você pode fazer com que ele envie uma resposta para um serviço externo ou, como neste caso, simplesmente
00:18:48exibir no stdout. Algumas dicas aqui: normalmente você não deve apenas dar console.log e confiar
00:18:52em toda a saída obtida da sandbox. Pode haver outros pacotes instalados pelo usuário ou
00:19:00outros códigos em execução que imprimam coisas simultaneamente no fluxo de saída padrão.
00:19:07Por isso, é bom envolver sua saída em alguma tag analisável,
00:19:12como uma tag no estilo XML. Não farei isso neste exemplo.
00:19:18Você também deve limitar o tamanho dos logs consumidos. Neste exemplo, estou apenas
00:19:23coletando toda a saída em uma stream. Em uma aplicação de produção, isso não é recomendável,
00:19:29pois a saída pode ter gigabytes de dados e estourar a memória (OOM) dos servidores.
00:19:34A API do sandbox possui comandos para transmitir logs via streaming. É isso que fazemos
00:19:39no Notion Workers: consumimos a stream até identificar o token de saída
00:19:45desejado. A partir daí, armazenamos a descrição impressa intencionalmente.
00:19:53Paramos de processar a stream assim que atingimos a tag de fechamento. Dessa forma, se algo
00:19:58der errado na sandbox e grandes volumes de dados forem registrados,
00:20:04eles não se acumularão na memória. Podemos descartá-los e esperar até obter os dados relevantes.
00:20:08Com o comando em execução, aguardamos sua conclusão e capturamos o stdout.
00:20:14Para quem já usou o Zod, o processo será familiar: analisamos a string como JSON
00:20:22E se você já usou algo como o Zod antes, isso deve parecer bastante familiar. Estamos apenas analisando essa string como JSON.
00:20:27E depois temos um tipo Zod aqui que está
00:20:31validando para garantir que está no formato que queremos.
00:20:34É realmente importante não confiar nesses dados, porque é código de usuário não confiável. Você não tem ideia
00:20:39do que as pessoas podem estar registrando nos logs. Então você quer garantir que está
00:20:43validando o tamanho e validando o formato geral dele. Então, neste caso, o que estamos esperando
00:20:50é um registro cujas chaves, a string, serão o nome de cada uma das nossas ferramentas.
00:20:57E os objetos serão a descrição e depois o schema de entrada, que é aquele
00:21:02JSON Schema. Você vai notar que a função de execução não está aqui. Ela simplesmente
00:21:06não é retornada, o que é ótimo porque o JSON stringify ignora coisas que não são
00:21:11serializáveis. Então ela é ignorada. Assim, obtemos apenas a descrição e o schema de entrada dela.
00:21:18De volta aqui para cima, temos nossas ferramentas de worker que são aquele
00:21:23registro de cada trabalho, de cada ferramenta exposta naquele worker.
00:21:28A próxima coisa que fazemos é colocar esses dados no formato que o AI SDK
00:21:36espera. E precisamos anexar uma função de execução a cada uma dessas ferramentas.
00:21:42Obviamente não obtivemos uma função de execução quando estávamos apenas registrando dados no stdout.
00:21:46A questão agora é: já que temos essa descrição da ferramenta, como fornecemos
00:21:51uma função que o SDK possa chamar toda vez que quiser chamar este worker ou esta ferramenta exposta por
00:21:58este worker? Tenho esse pequeno wrapper aqui chamado execute tool. Vamos dar uma olhada no que
00:22:04ele faz. Deve parecer bem familiar agora. Ele está chamando a mesma função create sandbox. Ou seja,
00:22:10está criando um sandbox limpo. Mesmo que você use cache, há um recurso nos sandboxes da Vercel chamado
00:22:16persistência, onde toda vez que o sandbox suspende ou para, ele salva em cache o estado de tudo o que está
00:22:23no disco. Para um recurso como esse, você na verdade não quer isso. Você quer um snapshot do seu estado inicial
00:22:28onde está todo o código do usuário. Mas, em geral, depois disso, você quer garantir que, a cada
00:22:33execução da ferramenta, você tenha uma instância totalmente limpa. Dessa forma, em uma execução da ferramenta,
00:22:38se algo der errado, não vai poluir o ambiente da ferramenta que for executada em seguida.
00:22:43Então criamos um novo sandbox e rodamos outro script Node nele. Isso deve parecer bem familiar,
00:22:49mas é um pouco diferente. Estamos importando o módulo que o usuário escreveu. Estamos pegando a
00:22:56ferramenta desse módulo, que é apenas o nome da ferramenta que recebemos quando criamos esse wrapper execute tool.
00:23:03Estamos chamando essa função de execução nela. E estamos passando para essa função de execução a entrada que foi
00:23:09fornecida pelo modelo. Aqui estou tipando isso como unknown. É bem seguro fazer isso aqui, no entanto, porque
00:23:19nós fornecemos um... eu não fiz isso aqui? Acho que posso ter pulado isso neste exemplo. Mas o que
00:23:27você normalmente faria é... Ah, não, acho que fiz sim. Deixe-me voltar aqui em cima. É. Quando estamos
00:23:34manipulando nossas ferramentas para enviá-las ao agente, pegamos aquele JSON schema e o transformamos
00:23:39de volta em um tipo Zod. Assim, não precisamos fazer nenhuma análise manualmente. O AI SDK, o código do agente do loop de ferramentas,
00:23:46toda vez que essa ferramenta for chamada, vai validar para nós a entrada que veio do agente.
00:23:52Então podemos confiar, mais ou menos, que este valor é o que esperamos. Vamos passá-lo
00:23:56para a função de execução aqui. Quando essa função assíncrona for concluída, vamos
00:24:03convertê-la em string. Vamos registrá-la no stdout. E então vamos esperar... Já vou detalhar isso
00:24:10aqui em um segundo. Vamos esperar a conclusão desse comando. E, novamente, vamos
00:24:14excluir nosso sandbox quando terminarmos. Depois, faremos o parse daquele JSON, que é o valor de retorno
00:24:21daquela função de execução que o usuário escreveu. E vamos enviar isso de volta para o agente do loop de ferramentas.
00:24:26Basicamente
00:24:35que vimos antes, onde ele usa todas aquelas APIs de transporte público. Isso acaba chamando esta função aqui com
00:24:40quaisquer entradas que o agente decidir. Criamos um sandbox usando o blob que enviamos do
00:24:48código compilado do usuário anteriormente. Em seguida, executamos um comando nesse sandbox onde chamamos a função
00:24:55de execução do usuário. Esperamos o retorno dessa função. E então registramos esse valor de retorno no
00:25:00stdout, fazemos o parse dele e o enviamos de volta para o agente, que continuará o loop de ferramentas
00:25:05e fará outra chamada de ferramenta ou responderá ao usuário. Algumas dicas rápidas aqui: as mesmas
00:25:13regras de verificação de saída se aplicam a um sistema de produção. Novamente, você quer garantir que os usuários não possam
00:25:19retornar um objeto que leve cerca de dois gigabytes para ser analisado. Você também provavelmente
00:25:28deve fornecer um SDK. Não fazemos isso neste exemplo, mas no SDK do Notion Workers,
00:25:35os tipos são configurados de modo que o valor de retorno dessa função de execução precisa ser serializável em JSON.
00:25:41Essa é uma armadilha muito comum para os seus usuários: se você permitir que essa função
00:25:47retorne qualquer coisa, eles acabarão retornando coisas que não podem ser serializadas em JSON para envio
00:25:52pela rede via stdout e posterior análise. Eles ficarão muito confusos, sem entender por que o
00:25:57worker não está funcionando. E agora, uma historinha que aconteceu com o Notion Workers: você também
00:26:05deve ter muito cuidado para garantir que o processo, o processo Node que você está gerando
00:26:11aqui, seja encerrado, aconteça o que acontecer. Tivemos um bug no Notion onde o código do usuário rodava e chegava ao fim,
00:26:18mas por algum motivo, o processo Node não encerrava. Ele simplesmente travava até que o tempo limite
00:26:25do ciclo de vida do sandbox expirasse, o que levava uns cinco minutos. Não era catastrófico,
00:26:30mas desperdiçava recursos. O que descobrimos ou nos lembramos — eu tinha esquecido totalmente que essa
00:26:38é uma propriedade do runtime do Node — é que os usuários estavam executando códigos que configuravam temporizadores como
00:26:44setInterval ou setTimeout. Especialmente com intervalos, ou acredito que promises pendentes causam o mesmo efeito.
00:26:52Se o script rodar e houver intervalos ou algo semelhante em execução, o processo Node
00:26:58nunca retornará. Ele não vai encerrar sozinho até que todos esses temporizadores terminem. Se for um
00:27:03intervalo, vai rodar para sempre até que o sandbox seja encerrado. Portanto, certifique-se sempre de que,
00:27:08quando o código ou a função que você está tentando executar for concluída, você ordene explicitamente
00:27:14o encerramento do processo. Assim, o sandbox é interrompido e o processo é finalizado em seguida.
00:27:23Esse é praticamente todo o fluxo de trabalho. Vou executá-lo mais uma vez e vou ativar a
00:27:29depuração para que você possa ver o que acontece.
00:27:42Primeiro, estamos fazendo o deploy do nosso worker de partidas. Explicarei isso em um segundo. Fazemos o deploy do nosso
00:27:48worker de partidas enviando aquele pacote primeiro. Criamos um sandbox que usaremos para extrair
00:27:54informações sobre esse worker. Extraímos estas ferramentas. É isto que obtemos quando registramos os
00:28:01conteúdos do módulo no stdout: as chaves para cada ferramenta, a descrição e o schema de entrada.
00:28:08Fazemos o mesmo procedimento para aquele worker simples de saudações. Pegamos tudo isso e passamos para o agente para que
00:28:13ele possa fazer chamadas de ferramentas e responder ao usuário. É basicamente isso. É um exemplo bem simples
00:28:19de como você pode pegar código não confiável de usuários, armazená-lo, extrair dados dele com segurança para
00:28:26guardá-lo em um armazenamento durável ou enviá-lo diretamente aos agentes, permitindo que eles executem esse
00:28:31código com segurança. Temos cerca de 10 minutos restantes. Se alguém tiver alguma dúvida sobre
00:28:39Notion Workers, uso geral dos sandboxes da Vercel ou execução de código não confiável,
00:28:46ficarei feliz em conversar a respeito. Obrigado.
00:28:56Ah, sim. Se quiserem conversar sobre a plataforma de desenvolvedores do Notion no geral depois,
00:29:01podem falar comigo ou com a minha colega MJ ali. Ela é a gerente de produto da plataforma de desenvolvedores no Notion.
00:29:08Olá. É uma pergunta operacional, mas como vocês limitam... já que os usuários podem fazer qualquer coisa?
00:29:15Sim. Podem existir vários usuários criando uma ferramenta semelhante.
00:29:20Vários usuários fazendo o quê? Criando uma ferramenta semelhante. Por exemplo, planejar uma viagem para Nova York.
00:29:24Entendi. Podem existir 10 usuários diferentes com 10 códigos diferentes fazendo a mesma coisa.
00:29:29Sim. Existe algo que vocês fazem para bloquear isso ou fica a critério do agente?
00:29:33Não, nós simplesmente permitimos. Se vários usuários forem fazer a mesma coisa, deixamos que façam.
00:29:37É em parte uma questão de produto. Se forem vários usuários na mesma organização,
00:29:42você quer garantir... então sim, é uma questão operacional e de produto também.
00:29:46Você quer garantir boas primitivas de compartilhamento e coisas assim. Para que eu possa
00:29:50pesquisar e verificar se já existe um worker que faz essa tarefa, evitando que ela seja
00:29:55recriada. Mas, em termos gerais de plataforma, não fazemos nada para... é muito improvável que usuários
00:30:02implantem código idêntico, e simplesmente não vale a pena tentar desduplicar isso.
00:30:19Certo. Acho que temos mais algumas perguntas. Não sei quem está com o microfone.
00:30:24Eu não ouvi. Ah, sim. Me desculpe.
00:30:29Achei que tinha saído nos fones de ouvido.
00:30:33Ah, sim. A pergunta feita foi: se houver muitos usuários implantando o mesmo código,
00:30:40nós fazemos algo para operacionalizar isso? E não fazemos. É mais uma questão de produto.
00:30:45Queremos garantir que os usuários não refaçam o mesmo trabalho, por isso estamos desenvolvendo boas
00:30:50primitivas de compartilhamento no Notion Workers. Mas operacionalmente, a nível de plataforma,
00:30:54se as pessoas implantarem o mesmo código 50 vezes, não nos importamos.
00:30:56Vocês têm problemas com os workers estourando o tempo limite porque o sandbox
00:31:04encerra a execução cedo demais?
00:31:07Qual foi exatamente a pergunta sobre tempos limite?
00:31:10Vocês estão tendo problemas de timeout entre a Vercel, outros produtos e fluxos de trabalho,
00:31:15por exemplo, funções e o seu sandbox? Ou está tudo funcionando perfeitamente?
00:31:20Não tivemos nenhum problema com isso. A plataforma tem sido extremamente sólida para nós
00:31:25até agora. Não estou fazendo propaganda, estou falando sério. Tem sido muito boa.
00:31:32Encontramos muito mais problemas com os próprios usuários cometendo erros acidentais. Então,
00:31:36com o tempo, nosso foco é eliminar essas armadilhas e tornar a plataforma cada vez mais fácil de
00:31:42usar, tanto para desenvolvedores quanto para leigos. Legal. Foi ótimo. Eu gostaria de perguntar sobre,
00:31:48quando você converte o código inicial do usuário para string. Suponho que esteja tentando garantir
00:31:52que ele seja seguro ou que possa executá-lo. Gostaria de entender um pouco mais.
00:31:57Você disse que converte o código em string e obtém as tags do worker para pegar as entradas desejadas
00:32:04para o código do usuário e a descrição da ferramenta. Isso serve para o seu agente
00:32:10executar o código? Percebi que você já executa a função de execução
00:32:15ou o executor do usuário. Fiquei curioso sobre o motivo dessa etapa
00:32:21adicional para obter as entradas e a descrição da ferramenta em si.
00:32:24Boa pergunta. Fica mais claro no produto real, mas aqui está simplificado.
00:32:29A pergunta é: por que executo o código do usuário uma vez para obter informações sobre o
00:32:34worker e depois faço isso de novo quando o código da ferramenta é chamado? O motivo é que, antes que o agente
00:32:39possa chamar a ferramenta ou saber da sua existência, precisamos descobrir o que
00:32:45há nela e expô-la ao agente por meio do SDK. O que acontece no Notion
00:32:50Workers, por exemplo, quando você roda NTN Workers Deploy, é que passamos pelo pipeline de build,
00:32:55o sandbox que está executando o build pega o arquivo tarball e o armazena em algum lugar. Nós então
00:33:02extraímos em um novo worker os nomes das ferramentas, descrições e schemas,
00:33:09e os salvamos em um banco como o DynamoDB. Dessa forma, nunca precisamos rodar
00:33:14sandboxes até que a ferramenta seja realmente chamada. Você mencionou o sistema de tarball. Acho que é isso que me deixa
00:33:21mais curioso. Em termos de distribuição, como funciona exatamente em relação ao tarball?
00:33:26Existe um marketplace aberto atualmente ou como funciona a distribuição?
00:33:31Ah, sobre o que vai dentro do arquivo tarball? Sim, sim.
00:33:33Mecanicamente, o que colocamos dentro desse tarball
00:33:38é gerado usando o esbuild no momento. Todo o pipeline real de deploy funciona assim: você executa
00:33:47NTN workers deploy, chama um endpoint da API, seu computador recebe uma URL pré-assinada, empacota todo
00:33:52o código-fonte, criamos um tarball com esse código, executamos o processo de build com esbuild naquele
00:33:58sandbox e a saída é armazenada de volta no storage de blobs para ser executada
00:34:04a partir dali. Ainda não temos um marketplace real para workers. Estamos trabalhando primeiro em primitivas de compartilhamento
00:34:11dentro de um workspace do Notion, mas definitivamente há planos para algum tipo de marketplace de workers no futuro.
00:34:18Enquanto isso, você pode simplesmente distribuí-los pelo GitHub e funciona muito bem. É o que as pessoas fazem hoje.
00:34:23Sim, basta publicar um repositório para que qualquer pessoa possa clonar, rodar NTN workers deploy e usar.
00:34:29Isso.
00:34:34Acho que há uma pergunta ali atrás.
00:34:37Sim, eu tenho uma pergunta sobre faturamento.
00:34:40Sobre cobrança?
00:34:40Sim. Dei uma olhada rápida no Google — claro, sem pedir que revele todos os segredos —
00:34:44mas vi que os agentes customizados usam um sistema de créditos. Imagino
00:34:48que isso esteja atrelado ao consumo de recursos.
00:34:51Sim, sim.
00:34:51Como isso funciona em relação à plataforma Vercel, em linhas gerais?
00:34:54Certo. A pergunta é: como funciona a cobrança disso na plataforma Vercel?
00:35:02Uma das vantagens dos workers é permitir que equipes substituam
00:35:08um conjunto enorme de instruções de servidores MCP fornecidos aos agentes. A cada tarefa chamada,
00:35:16o agente gastaria muitos tokens de processamento repetindo a mesma coisa continuamente.
00:35:21Se você puder pegar essas tarefas repetitivas do agente e implantá-las como um worker,
00:35:33você ainda é cobrado pelo tempo de execução dos workers, mas é muito mais barato do que tokens de computação de IA.
00:35:40Se você roda um agente customizado, ele processa, executa uma ferramenta e depois processa mais um pouco.
00:35:48Você é cobrado pelo consumo de tokens no agente customizado antes de ele chamar a ferramenta.
00:35:53Quando a ferramenta roda, a cobrança é feita em uma taxa diferente, consumindo seus créditos de IA do Notion a um custo significativamente menor.
00:36:01E então você é cobrado pelos créditos normais de IA quando o agente finalmente responde.
00:36:05Portanto, se você usa agentes customizados do Notion, essa é uma maneira de reduzir bastante os custos em tarefas repetitivas.
00:36:16Isso também é verdade. Você não precisa necessariamente ter o Notion AI. Estamos mostrando chamadas de ferramentas integradas ao Notion AI,
00:36:24mas os workers também fazem sincronização de terceiros para o Notion, sem exigir nenhum recurso de IA.
00:36:31Portanto, não é um produto exclusivo de IA. As pessoas usam isso para sincronização.
00:36:36Tenho workers que sincronizam meu feed do Letterboxd no Notion. Pessoalmente, é o meu favorito.
00:36:46Certo. Mais alguma pergunta?
00:36:52Tem mais uma ali?
00:37:06Ah, expô-los através da sua própria interface de usuário, como expor agentes customizados do Notion na sua própria aplicação.
00:37:13Isso não é algo que temos disponível hoje.
00:37:16Desculpe. Obrigado, MJ. A pergunta foi: se você tiver seus próprios workers e agentes customizados,
00:37:22existe uma maneira de expô-los através da sua própria aplicação para seus clientes?
00:37:28Atualmente não há como fazer isso. Temos uma versão alpha de uma API de agentes customizados onde você
00:37:35com certeza poderia fazer isso. Se você definir agentes customizados e workers no seu workspace,
00:37:40poderá usar uma API para chamar esses agentes e obter uma resposta em streaming. Então é possível.
00:37:46Acho que pouca gente está usando isso ainda, pois é um recurso em fase alpha pública.
00:37:57Mais alguma pergunta?
00:38:00Certo. Muito obrigado a todos pelo tempo dedicado
00:38:04para vir nos ouvir hoje. Se tiverem dúvidas sobre Notion, Notion Workers,
00:38:08ou Vercel Sandbox, venham falar comigo ou com a MJ. Obrigado.

Description

Vercel.com

Community Posts

View all posts