Ship 26 NYC - Atelier - Mini-travailleurs

VVercel
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00Bonjour à tous. Je m'appelle Jonathan Clem, ou vous pouvez m'appeler Jay Clem, et je suis ingénieur logiciel chez
00:00:11Notion où je travaille sur notre plateforme développeur, mais je me concentre particulièrement sur un produit plus récent
00:00:17dont vous avez peut-être entendu parler, appelé Notion Workers. Dans cet atelier aujourd'hui, je vais
00:00:23vous donner un aperçu de ce que sont les Notion Workers et pourquoi nous les avons conçus avec Vercel
00:00:28Sandbox. Ensuite, je vais vous guider à travers une version réduite des Workers,
00:00:36afin que vous puissiez au moins comprendre comment un tel produit est créé avec Vercel Sandbox.
00:00:43Si vous n'êtes pas familiarisé avec les Workers, il s'agit d'un SDK et d'un environnement d'exécution où vous pouvez écrire
00:00:50du code personnalisé pour étendre Notion. Vous pouvez ainsi synchroniser des données tierces dans Notion,
00:00:57ou écrire des appels d'outils personnalisés pour vos agents. Nous avons vu des gens faire des choses folles comme commander
00:01:04leurs courses avec Notion Workers ou contrôler leur maison connectée. Nous avons aussi vu des flux de travail très complexes,
00:01:11notamment dans les domaines de l'IT et de la sécurité. L'avantage des Workers, c'est qu'il n'y a aucune
00:01:17infrastructure à gérer. Vous écrivez simplement du code ou laissez un agent le faire pour vous, et Notion
00:01:23s'assure qu'il soit toujours opérationnel et exécuté. Cela a été un énorme bénéfice pour les utilisateurs de Notion,
00:01:30en particulier les développeurs. Ils n'ont plus à attendre que Notion crée des intégrations natives
00:01:36pour eux. Tout ce qu'ils aimeraient que Notion fasse mais qu'il ne fait pas, ils peuvent l'écrire
00:01:42eux-mêmes. Alors, quand nous avons commencé à concevoir Notion Workers, ma préoccupation principale était
00:01:53la sécurité. J'ai un peu d'expérience dans la création de plateformes qui exécutent du code utilisateur non approuvé.
00:01:59J'ai travaillé sur GitHub Actions, par exemple, pendant longtemps. Et je m'inquiétais surtout de la
00:02:04difficulté à bâtir l'infrastructure pour un produit comme celui-ci, et en particulier la sécurité. Il y a beaucoup
00:02:09de choses dont il faut se soucier. Par exemple, vous voulez vous assurer que le code écrit par les utilisateurs
00:02:14ne puisse pas toucher aux bases de données Notion, ni aux services Notion auxquels ils ne devraient normalement pas
00:02:20avoir accès. Vous voulez aussi vous assurer que les utilisateurs ne s'impactent pas entre eux. Vous ne voulez pas que le code
00:02:26d'un utilisateur puisse accéder au code d'un autre utilisateur ou, évidemment, à ses secrets ou à quoi que ce soit
00:02:31de ce genre. Ce n'est pas seulement une question de sécurité. C'est aussi une question d'équité et de partage des ressources. Si un utilisateur
00:02:37fait quelque chose qui consomme énormément de processeur et de mémoire, vous voulez vous assurer que cela n'impacte pas
00:02:42injustement d'autres utilisateurs qui essaient de faire des choses en même temps. Je vais prendre une minute
00:02:48pour m'écarter un peu du sujet et raconter une histoire. Le partage des ressources est une chose particulièrement
00:02:54délicate. Cela va probablement empiéter sur mon temps, mais j'aime bien cette histoire. Quand nous construisions
00:02:58GitHub Actions, et c'est une chose à laquelle nous avons aussi pensé avec les workers, dès que vous avez une
00:03:02plateforme d'exécution de code arbitraire, des gens vont immédiatement tenter d'y miner de la crypto.
00:03:07Et j'ai eu une conversation avec quelqu'un il y a quelques semaines qui me disait : “Mais
00:03:10comment détectes-tu cela ? Il suffit de voir si un processeur tourne à 100 %, non ?”
00:03:14Eh bien, pas vraiment, parce que dès que votre plateforme a du succès, les mineurs de crypto vont
00:03:20immédiatement commencer à se partager des scripts entre eux pour modifier la façon dont
00:03:26ils exécutent les instructions du processeur, afin que cela ressemble à une activité totalement inoffensive, tout en utilisant
00:03:31le maximum absolu de ressources possible sans se faire signaler par votre système.
00:03:37C'est donc extrêmement difficile, et cela demande des années et plusieurs couches de sécurité et
00:03:42d'observabilité pour y parvenir. Une autre chose dont il faut se soucier avec une
00:03:48plateforme d'exécution de code, c'est le risque que des gens l'utilisent pour lancer des attaques par déni de service
00:03:54ou pour contrôler des réseaux de botnets. Et nous ne voulions pas avoir à nous soucier de tout
00:04:00cela dès le départ avec Notion Workers. Nous voulions nous concentrer sur ce que nous aimons faire, à savoir
00:04:07fournir un produit que nos utilisateurs aiment utiliser. C'est pourquoi nous avons décidé de construire avec Vercel Sandbox.
00:04:14Vercel Sandbox proposait d'emblée une infrastructure très solide qui résolvait une grande partie de ces
00:04:18problèmes pour nous. Je vais donc vous montrer une très courte démo de ce que sont les Notion Workers.
00:04:25J'ai ici un agent personnalisé, et la tâche de cet agent est de me dire si cet atelier est maudit ou non.
00:04:33J'ai écrit un worker appelé Mercure Rétrograde. Je ne sais pas si vous connaissez le principe, mais quand Mercure
00:04:40est aligné de manière à être en rétrograde, c'est généralement un mauvais présage.
00:04:47Ce worker possède un seul outil personnalisé qui utilise une API trouvée en ligne dont le seul rôle
00:04:54est de dire si Mercure est en rétrograde. Je vais donc demander à mon agent : “Est-ce que mon
00:05:01atelier est maudit ?” Il va réfléchir un instant et ensuite, tant que la connexion internet fonctionne,
00:05:14nous devrions le voir effectuer un appel d'outil. Il utilise mon worker Mercure Rétrograde, appelle le
00:05:22seul outil qu'il propose pour vérifier si Mercure est en rétrograde, puis l'agent
00:05:27va nous répondre. Je crois que je l'ai connecté à GPT-54 nano, donc c'est généralement beaucoup
00:05:37plus rapide. Je pense que c'est un problème d'internet, malheureusement. Si quelqu'un a vérifié au préalable si Mercure était en
00:05:46rétrograde, je sais déjà d'avance que oui. C'est sûrement pour cela que ça bloque. Je vais simplement
00:05:53passer cette étape. Nous y reviendrons dans une minute pour voir si nous avons fini par avoir une réponse. Mais je crois qu'on a déjà
00:05:59la réponse. Bien, vous avez maintenant une idée de ce que sont les Notion Workers. Je vais vous montrer
00:06:05un petit script que j'ai écrit qui exécute la plupart des tâches de base consistant à prendre le code utilisateur,
00:06:13le déployer, l'exécuter en toute sécurité et le présenter à un agent afin qu'il puisse
00:06:20effectuer des appels d'outils. Est-ce que ça s'est terminé ? Ah oui, voilà. Ah, je crois que...
00:06:28J'ai dû désactiver l'API Mercure Rétrograde parce qu'apparemment elle n'a pas répondu.
00:06:35Bref, il faudra que j'ouvre un ticket quelque part. Super. J'ai donc un script de base. Je n'attends pas
00:06:40de vous que vous me suiviez pas à pas en écrivant du code. Je vais plutôt survoler les étapes
00:06:44pour vous donner une idée des problèmes que nous avons résolus lors de la création d'un tel
00:06:49produit. Mais si vous voulez, il y a un dépôt make-notion/vercel-ship-2026-workers
00:06:55qui contient tout le code que je vais exécuter ici.
00:07:01Parfait. Laissez-moi ouvrir mes autres diapositives.
00:07:08Alors, quel est notre objectif ? Nous avons un agent de chat en streaming. Nous allons lui permettre d'appeler des outils
00:07:16définis par du code utilisateur personnalisé non vérifié ou dont nous ignorons le contenu.
00:07:22Et nous allons construire cela avec Vercel Sandbox et le service de stockage blob de Vercel.
00:07:26Je vais faire une rapide démonstration, en espérant que nous ne sommes pas vraiment maudits.
00:07:31On peut simplement lui envoyer un seul message. Je vais dire “salut” et j'espère que nous aurons une réponse.
00:07:36Ce sera un peu lent car, comme vous le voyez, il fait des déploiements en arrière-plan.
00:07:40Il a appelé un outil cette fois. Il a appelé l'outil “say hello” et a décidé de me saluer.
00:07:47Je vais lui envoyer un message normal pour lui demander combien font un plus un, afin de le voir
00:07:52générer une réponse classique en streaming.
00:07:58Si vous avez utilisé le SDK IA de Vercel, cela utilise simplement la boucle de l'agent d'outils. Super. Nous avons donc obtenu une réponse
00:08:04en streaming. Vous pouvez aussi appeler des workers beaucoup plus complexes. En voici un autre que je vais vous montrer.
00:08:14Avec celui-ci, nous lui indiquons que je me trouve à l'adresse de ce bâtiment.
00:08:17J'ai 90 minutes devant moi, je veux visiter un site historique et je ne veux pas marcher plus de 15 minutes.
00:08:24Cela illustre pourquoi cette approche est parfois préférable aux serveurs MCP. Avec un serveur
00:08:29MCP, vous avez plein d'outils distincts utilisables. Mais pour accomplir quelque chose
00:08:33de complexe, il faut décrire chaque étape à l'agent, qui va exécuter une étape, consommer des jetons
00:08:38de réflexion, puis passer à la suivante. Ici, l'agent appelle un worker contenant un algorithme de recherche
00:08:45très vaste et complexe qui utilise des API de géolocalisation, de temps de transport et d'itinéraires à New York.
00:08:52Il s'appelle “plan outing”.
00:08:56Et ce worker va renvoyer une proposition de site historique à visiter.
00:09:00Il semble qu'il ait trouvé le Freedom Tree Marker, un mémorial situé près
00:09:07de City Hall Park. Regardons maintenant comment cela fonctionne. Qu'est-ce qu'un worker ?
00:09:14C'est du code utilisateur qui est simplement défini dans un fichier. Normalement, il serait défini
00:09:20par vos utilisateurs ou sur GitHub, puis déployé via une interface CLI. Mais nous avons quelques exemples intégrés
00:09:26dans le dépôt ici. Chacun de ces workers doit exposer le nom de l'outil, une description
00:09:34pour que l'agent choisisse le bon outil, un schéma d'entrée pour que l'agent sache quelles données fournir
00:09:38à chaque appel, ainsi qu'une fonction d'exécution indiquant le code à exécuter
00:09:44lorsque l'agent appelle l'outil. Prenons un exemple avec l'outil d'accueil vu
00:09:51précédemment. C'est très simple. Ce worker est un seul module JavaScript dans le fichier index.ts. Il exporte un seul
00:09:59worker nommé “say hello”. Vous allez voir le fonctionnement du processus de build, mais dans cet exemple,
00:10:04toutes nos clés d'exportation de modules correspondent aux noms de nos outils. Cet outil s'appellera donc
00:10:09“say hello”. Il contient une description simple et un schéma d'entrée. Ici, j'utilise Zod pour définir
00:10:16le schéma, puis je le convertis au format JSON Schema, attendu par les agents.
00:10:22Il y a ensuite une fonction d'exécution simple. Mais on peut en créer de beaucoup plus complexes. Si j'ouvre
00:10:27le flux de travail de “plan outing”, vous verrez une description bien plus longue expliquant à l'agent
00:10:34le fonctionnement de l'outil, quand l'appeler et ce qu'il fait. Le script associé est lui aussi beaucoup
00:10:39plus long et complexe. Je ne vais pas tout détailler, et je n'en ai moi-même lu que de petites parties,
00:10:45mais cela fonctionne très bien. C'est notre réalité aujourd'hui. Ces workers vont donc être
00:10:54compilés et déployés sur le stockage blob de Vercel. Ensuite, nous allons extraire le contenu des
00:10:59workers d'une manière ou d'une autre, puis les exposer à un agent. Enfin, ces outils seront
00:11:03exécutés en toute sécurité dans un sandbox. Pour la phase de build, nous allons procéder par étapes. Avec Notion Workers, nous avons un
00:11:09processus de déploiement et de build basé sur le cloud. Dans ce cas précis, j'ai pré-compilé les workers sur le disque. Chaque
00:11:15worker dispose d'une archive tarball contenant tout le code TypeScript compilé, les dépendances, etc.
00:11:22Ce n'est pas la partie la plus intéressante, donc je vais simplement la passer.
00:11:26La question du jour est : comment passer d'un code utilisateur inconnu et non vérifié
00:11:34à des outils exposés et exécutés en toute sécurité par l'agent ? Cela se décompose en deux parties.
00:11:39La première partie consiste à déterminer, une fois le code utilisateur stocké dans le blob,
00:11:43comment savoir ce qu'il contient. Il faut en effet pouvoir indiquer à l'agent, avant même
00:11:48qu'il n'appelle l'outil, son nom, son schéma d'entrée et sa description.
00:11:53La seconde question est : quand l'agent décide d'appeler cet outil,
00:11:58comment l'exécuter en toute sécurité ? Ce que j'adore dans la conception du SDK Notion
00:12:04Workers et ce que nous avons reproduit ici, c'est que le code s'auto-décrit. Nous ne voulions pas,
00:12:11en concevant Notion Workers, que les utilisateurs doivent écrire du code TypeScript pour définir leur outil,
00:12:16puis créer un fichier de manifeste statique pour décrire leurs workers en réécrivant les mêmes informations.
00:12:21Nous ne voulions pas d'un processus de build local lourd où ils devaient lancer un script, analyser le code,
00:12:27le compiler sur disque puis le déployer. Nous voulions offrir l'expérience développeur idéale :
00:12:34écrire simplement l'outil, et nous laisser gérer la tâche complexe d'en extraire
00:12:40automatiquement les informations nécessaires.
00:12:46Voici un schéma très simple, mais avant de regarder le code, je vais vous expliquer
00:12:52comment nous allons résoudre ce problème. Le code compilé de l'utilisateur se trouve sur le stockage blob.
00:12:59Nous allons utiliser ce code pour créer un sandbox. Lorsque nous exécutons des commandes dans le sandbox,
00:13:04le code utilisateur se trouve à la racine de l'espace de travail. Nous importons le fichier index.js écrit par l'utilisateur,
00:13:11ce qui nous donne les noms des exportations, leurs descriptions et leurs schémas d'entrée.
00:13:19C'est ici que ça devient un peu étrange, mais cela fonctionne très bien. Nous appelons
00:13:23JSON.stringify sur ce module, puis nous l'envoyons dans la sortie standard. Tout cela se déroule dans
00:13:29le sandbox, et notre script de déploiement récupère cette sortie standard, l'analyse et identifie
00:13:34le nom de tous les outils du worker, leurs entrées et leur description. Dans cet exemple,
00:13:40nous les transmettons directement à l'agent. Mais pour Notion Workers, cela fait partie intégrante
00:13:44de notre pipeline de déploiement. Nous enregistrons ces informations en base de données pour pouvoir,
00:13:49à chaque exécution d'un agent personnalisé, récupérer les descriptions d'outils depuis
00:13:53la base de données. Est-ce que le fonctionnement est clair pour l'instant ? Très bien. Regardons maintenant le script
00:14:02de déploiement. Je dois revenir à mon premier commit. Ce script intègre tout notre processus de déploiement,
00:14:12puis l'appel à l'agent. C'est très simple. Ici, nous parcourons simplement tous les répertoires
00:14:18et les workers. Chaque sous-répertoire contient le code d'un worker, comme vu précédemment.
00:14:23Notre rôle est d'extraire les informations sur les outils de chaque worker pour remplir
00:14:29cet objet d'outils. Il est ensuite transmis à un agent à boucle d'outils fourni par le SDK AI
00:14:35de Vercel. Nous envoyons le message de l'utilisateur à cet agent, diffusons le résultat en streaming
00:14:42et l'écrivons sur la sortie standard du script. La première étape consiste à téléverser le code
00:14:48source. C'est facile et rapide. Plutôt que de coder en direct, je vais passer
00:14:53directement d'un bloc de code à l'autre pour vous éviter de me voir saisir du texte. Je vous assure que je sais
00:14:59coder, mais personne n'a envie de regarder ça. En avançant un peu, nous trouvons la fonction
00:15:06uploadSource que je vais détailler. J'ai importé le SDK Vercel Blob. C'est assez
00:15:13simple : dans cette fonction uploadSource, nous appelons la fonction put
00:15:20en indiquant que nous voulons stocker le bundle sous nom_du_worker/bundle.tar.gzip.
00:15:29Nous lisons le fichier sur le disque en streaming et le stockons dans le stockage blob. Une fois cela fait,
00:15:35nous pouvons créer des sandboxes à partir de ce blob. La partie téléversement est donc résolue.
00:15:43L'étape suivante consiste à prendre ce code source regroupé pour créer
00:15:49un sandbox. Pour cela, j'ai importé le SDK Vercel Sandbox. Il y a une autre fonction d'aide
00:15:56plus bas, appelée createSandbox. Je vais passer sur quelques détails, mais pour l'essentiel,
00:16:03une fois l'objet dans le stockage blob, nous générons des URL pré-signées que nous transmettons
00:16:10au service de sandbox. Ainsi, le service peut, durant le délai d'expiration fixé je crois à 10 minutes,
00:16:16récupérer le blob depuis le stockage et remplir un sandbox. C'est l'une des
00:16:23fonctionnalités que j'apprécie vraiment dans Vercel Sandbox : la prise en charge native des archives tarball. Il est facile
00:16:28de créer une archive du code utilisateur, et lorsque le service de sandbox doit en créer un instance,
00:16:35il la récupère depuis le stockage ou l'URL fournie, puis l'extrait automatiquement
00:16:40dans le répertoire racine. Tous les fichiers sont immédiatement prêts à l'emploi. Il suffit d'appeler
00:16:46sandbox.create. Nous n'avons pas encore abordé la partie complexe, mais nous y arrivons.
00:16:52Pour des raisons de concision dans cet exemple, je crée un nouveau sandbox à chaque
00:16:57exécution. En production, il vaut mieux utiliser un système d'instantanés (snapshots) pour éviter
00:17:02de redéployer le sandbox ou de télécharger à nouveau le blob depuis Vercel
00:17:09Blob Storage à chaque appel d'outil. Un mécanisme de mise en cache est directement
00:17:15intégré à la plateforme. Je l'ai omis ici par souci de simplicité.
00:17:19Voilà pour la création du sandbox, et c'est là que cela devient intéressant.
00:17:23La tâche suivante consiste à extraire les informations relatives aux outils
00:17:31définis dans ce worker depuis le sandbox fraîchement créé.
00:17:36Je fais une courte pause pour partager quelques conseils utiles. Si vous développez un service en production
00:17:40de ce type, veillez à toujours faire votre possible pour fermer vos sandboxes. Dans le code,
00:17:44vous verrez le rôle d'extractTools : une fois son travail terminé, nous supprimons explicitement le sandbox.
00:17:51En pratique, il est aussi conseillé de planifier une tâche de nettoyage asynchrone
00:17:56afin de ne pas laisser ces instances ouvertes indéfiniment.
00:18:00Examinons maintenant le fonctionnement de la fonction extractTools. Avec notre sandbox,
00:18:06j'aime beaucoup cet exemple car la solution semble presque trop simple, mais elle s'avère redoutablement efficace. Nous exécutons
00:18:15une commande Node à l'aide de son binaire directement dans le sandbox. Dans le script, nous importons le module
00:18:23écrit par l'utilisateur, à savoir index.js. Nous sérialisons cet objet en chaîne de caractères, puis nous appelons
00:18:30console.log. Gardez à l'esprit qu'un sandbox n'est pas un serveur web classique capable
00:18:36de recevoir une requête et de renvoyer une réponse directes. Les entrées et sorties passent par l'exécution de commandes.
00:18:42Vous pouvez envoyer la réponse à un service externe interrogé à intervalles réguliers, ou bien
00:18:48simplement l'afficher sur la sortie standard. Quelques recommandations toutefois :
00:18:52ne faites pas aveuglément confiance au contenu d'un console.log issu du sandbox. Des paquets tierce partie
00:19:00installés par l'utilisateur ou d'autres pans de son code pourraient écrire simultanément dans cette même sortie.
00:19:07Une bonne pratique consiste à encadrer le résultat par des balises spécifiques facilement analysables,
00:19:12comme des balises de type XML. Je ne l'ai pas inclus ici pour simplifier l'exemple.
00:19:18Veillez également à limiter la taille des journaux récupérés. Dans cet exemple,
00:19:23je rassemble l'intégralité du flux de sortie en mémoire. En environnement de production,
00:19:29cette méthode est déconseillée : un flux de plusieurs gigaoctets risquerait d'épuiser la mémoire de vos serveurs.
00:19:34L'API de sandbox propose des commandes dédiées à la lecture continue des journaux. C'est le choix
00:19:39fait pour Notion Workers : nous lisons le flux jusqu'à détecter la balise de début
00:19:45recherchée, puis nous enregistrons la description voulue en mémoire tampon.
00:19:53Dès l'apparition de la balise de fin, le traitement du flux s'arrête. Cette méthode protège
00:19:58nos serveurs si le sandbox génère un volume anormal de données :
00:20:04nous ignorons le surplus pour ne conserver que les données utiles.
00:20:08Une fois la commande exécutée, nous attendons sa fin pour en lire la sortie standard.
00:20:14Si vous avez déjà utilisé Zod, la suite vous sera familière : nous analysons la chaîne en JSON,
00:20:22Et si vous avez déjà utilisé Zod, cela devrait vous sembler familier. On analyse cette chaîne en JSON.
00:20:27Ensuite, nous avons ici un type Zod qui
00:20:31valide que la structure correspond bien à ce qu'on attend.
00:20:34C'est vraiment important de ne pas faire confiance à ces données car c'est du code utilisateur non sécurisé.
00:20:39Vous n'avez aucune idée de ce que les gens pourraient envoyer. Il faut donc s'assurer de
00:20:43valider la taille et la structure globale. Dans ce cas précis, ce qu'on attend,
00:20:50c'est un enregistrement dont les clés, les chaînes, seront le nom de chacun de nos outils.
00:20:57Et les objets seront la description puis le schéma d'entrée, c'est-à-dire ce
00:21:02schéma JSON. Vous remarquerez que la fonction execute n'est pas là.
00:21:06Elle n'est tout simplement pas renvoyée, ce qui est pratique car JSON.stringify ignore ce qui n'est pas
00:21:11sérialisable. C'est donc ignoré. On en extrait uniquement la description et le schéma d'entrée.
00:21:18De retour ici, nous avons nos outils de worker qui représentent cet
00:21:23enregistrement de tous les outils exposés dans ce worker.
00:21:28L'étape suivante consiste à mettre ces données au format attendu par l'AI SDK.
00:21:36Et nous devons attacher une fonction d'exécution à chacun de ces outils.
00:21:42Évidemment, nous n'avions pas de fonction d'exécution en envoyant juste les journaux sur la sortie standard.
00:21:46Maintenant que nous avons cette description de l'outil, la question est : comment fournir
00:21:51une fonction que le SDK peut appeler à chaque fois qu'il veut faire appel à ce worker ou à cet outil
00:21:58exposé par ce worker ? J'ai donc ce petit wrapper appelé execute tool. Voyons ce
00:22:04qu'il fait. Cela devrait vous sembler familier. Il appelle cette même fonction create sandbox.
00:22:10Il crée un bac à sable tout neuf. Même en utilisant du cache, les bacs à sable Vercel proposent
00:22:16une fonctionnalité de persistance : à chaque fois que le bac à sable s'arrête, il met en cache l'état du disque.
00:22:23Pour une fonctionnalité comme celle-ci, ce n'est pas ce que l'on souhaite. On veut capturer l'état initial
00:22:28contenant le code utilisateur. Mais en général, après cela, on veut s'assurer qu'à chaque
00:22:33exécution de l'outil, on dispose d'une instance totalement propre. Ainsi, si une exécution
00:22:38échoue, elle ne va pas polluer l'environnement de l'outil suivant.
00:22:43On crée donc un nouveau bac à sable et on y exécute un autre script Node. C'est très similaire,
00:22:49mais avec une légère différence. On importe le module écrit par l'utilisateur. On récupère
00:22:56l'outil de ce module, qui correspond au nom de l'outil obtenu lors de la création du wrapper execute tool.
00:23:03On appelle sa fonction execute, en lui passant l'entrée qui a été
00:23:09fournie par le modèle. Ici, je la typera en unknown. C'est assez sûr ici car
00:23:19nous avons fourni un... est-ce que je ne l'ai pas fait ici ? Je crois que j'ai sauté cela dans cet exemple. Mais ce
00:23:27que l'on fait d'habitude... ah non, je l'ai fait. Laissez-moi remonter ici. Oui. Quand on manipule
00:23:34nos outils pour les envoyer à l'agent, on prend ce schéma JSON et on le transforme
00:23:39à nouveau en type Zod. Pas besoin de faire l'analyse nous-mêmes. L'AI SDK et le code de la boucle d'outils
00:23:46vont valider pour nous l'entrée provenant de l'agent à chaque appel d'outil.
00:23:52On peut donc être à peu près sûr que cette valeur est conforme. On la passe
00:23:56dans la fonction execute ici. Une fois la fonction asynchrone terminée, on va la
00:24:03sérialiser en chaîne. On l'écrit dans la sortie standard. Et ensuite on attend... je reviendrai là-dessus
00:24:10dans un instant. On attend que la commande se termine. Et à nouveau, on
00:24:14supprime notre bac à sable. On analyse ensuite ce JSON, qui est la valeur de retour
00:24:21de la fonction d'exécution écrite par l'utilisateur. Et on renvoie cela à l'agent de la boucle d'outils.
00:24:26En gros, le flux est le suivant : l'agent demande à appeler l'outil de planification de sortie
00:24:35qu'on a vu plus tôt et qui utilise les API de transport. Cela finit par appeler cette fonction
00:24:40avec les arguments choisis par l'agent. On crée un bac à sable à partir du blob téléversé
00:24:48contenant le code compilé de l'utilisateur. On lance une commande dans ce bac à sable en appelant
00:24:55la fonction execute de l'utilisateur. On attend le retour de cette fonction. Puis on enregistre cette valeur
00:25:00dans la sortie standard, on la lit et on la renvoie à l'agent, qui va poursuivre la boucle d'outils
00:25:05en effectuant un autre appel ou en répondant à l'utilisateur. Quelques conseils rapides : les mêmes
00:25:13règles de vérification de la sortie s'appliquent en production. Il faut s'assurer que les utilisateurs
00:25:19ne renvoient pas un objet de deux gigaoctets à analyser. Il est aussi fortement conseillé
00:25:28de fournir un SDK. Nous ne le faisons pas dans cet exemple, mais dans le SDK Notion Workers,
00:25:35les types sont définis pour que la valeur de retour de la fonction d'exécution soit sérialisable en JSON.
00:25:41C'est un piège très classique pour les utilisateurs : si la fonction peut renvoyer
00:25:47n'importe quoi, ils finiront par renvoyer des éléments non sérialisables en JSON qui ne pourront être envoyés
00:25:52sur le réseau via stdout ni analysés. Ils seront alors très confus et ne comprendront pas pourquoi le
00:25:57worker ne tourne pas. Voici une petite anecdote vécue avec Notion Workers. Il faut
00:26:05toujours veiller à ce que le processus Node lancé
00:26:11se termine quoi qu'il arrive. Nous avons eu un bug chez Notion : le code utilisateur s'exécutait correctement,
00:26:18mais pour une raison inconnue, le processus Node ne se terminait pas. Il restait bloqué jusqu'à
00:26:25la fin du cycle de vie du bac à sable, soit environ cinq minutes. Ce n'était pas catastrophique,
00:26:30mais cela gaspillait des ressources. Nous avons réalisé — j'avais complètement oublié cette
00:26:38particularité de l'environnement Node — que les utilisateurs exécutaient du code configurant des minuteurs avec
00:26:44setInterval ou setTimeout. Surtout les intervalles, ou même les promesses non résolues.
00:26:52Si le script s'exécute et que des intervalles restent actifs, le processus Node ne
00:26:58s'arrêtera jamais de lui-même tant que ces minuteurs n'auront pas expiré. Dans le cas d'un
00:27:03intervalle, il tournera indéfiniment jusqu'à l'arrêt du bac à sable. Il faut donc s'assurer
00:27:08qu'une fois le code ou la fonction exécutée avec succès, vous ordonniez explicitement
00:27:14au processus de s'arrêter. Cela stoppe le bac à sable et le processus rend la main juste après.
00:27:23Voilà pour le flux global. Je vais le relancer une fois de plus en activant le mode
00:27:29débogage pour que vous puissiez voir ce qu'il se passe.
00:27:42On commence par déployer le worker de départs. Je vais tout détailler. On déploie le
00:27:48worker de départs en téléversant d'abord ce bundle. On crée le bac à sable qui servira à extraire
00:27:54les informations du worker. On a extrait ces outils. Voici ce qu'on obtient en affichant
00:28:01le contenu des modules dans stdout : les clés de chaque outil, la description et le schéma d'entrée.
00:28:08On fait la même chose pour ce simple worker greeter. On passe ensuite tout cela à l'agent pour qu'il
00:28:13puisse exécuter les outils et répondre à l'utilisateur. Voilà l'essentiel. C'est un exemple très simple
00:28:19montrant comment prendre du code utilisateur non sécurisé, le stocker, l'analyser en toute sécurité
00:28:26pour le sauvegarder ou l'envoyer à des agents, puis le faire exécuter sans risque
00:28:31par vos agents. Il nous reste environ 10 minutes. Si vous avez des questions sur
00:28:39Notion Workers, sur les bacs à sable Vercel ou l'exécution de code externe,
00:28:46je serai ravi d'en discuter. Merci.
00:28:56Ah oui ! Si vous voulez échanger sur la plateforme développeur Notion de manière générale,
00:29:01vous pouvez venir me voir ou parler à ma collègue MJ ici présente. Elle est responsable produit de la plateforme développeur chez Notion.
00:29:08Bonjour. C'est une question d'ordre opérationnel : comment limitez-vous, sachant que les utilisateurs peuvent tout faire...
00:29:15Oui. Plusieurs utilisateurs pourraient créer un outil similaire.
00:29:20Plusieurs utilisateurs qui font quoi ? Qui écrivent un outil similaire. Par exemple, pour planifier un voyage à New York.
00:29:24Oui, vous pourriez avoir 10 utilisateurs différents avec 10 codes différents qui font la même chose.
00:29:29Oui, est-ce que vous faites quelque chose pour bloquer ça ou c'est entièrement géré par l'agent ?
00:29:33Non. Si plusieurs utilisateurs veulent faire la même chose, nous les laissons faire.
00:29:37C'est en partie une question de produit. Si ce sont plusieurs utilisateurs au sein d'une même organisation,
00:29:42c'est en effet une question opérationnelle et produit à la fois.
00:29:46Il faut proposer de bonnes fonctionnalités de partage. Ainsi, je peux
00:29:50chercher s'il existe déjà un worker effectuant cette tâche pour ne pas
00:29:55avoir à le réécrire. Mais au niveau de la plateforme globale, nous ne faisons rien : il est très rare
00:30:02que les utilisateurs déploient un code identique. Cela ne vaut pas la peine d'essayer de dédupliquer.
00:30:19D'accord. Je crois qu'il y a d'autres questions. Je ne sais pas qui a le micro.
00:30:24Je n'ai pas entendu. Ah, oui ! Je suis désolé.
00:30:29Je pensais que le micro le capterait.
00:30:33La question était : si de nombreux utilisateurs déploient le même code,
00:30:40faites-vous quelque chose pour rationaliser cela ? Et la réponse est non. C'est un sujet produit.
00:30:45Nous voulons éviter que les utilisateurs ne refassent le travail déjà fait. Nous développons donc de bonnes options de partage
00:30:50sur Notion Workers. Mais d'un point de vue purement infrastructure,
00:30:54si les gens déploient 50 fois le même code, cela nous importe peu.
00:30:56Avez-vous des problèmes de temps d'attente dépassés avec les workers parce que le bac à sable
00:31:04s'arrête trop rapidement pendant l'exécution ?
00:31:07Quelle était la question exacte concernant les timeouts ?
00:31:10Rencontrez-vous des problèmes de timeouts entre Vercel (fonctions, workflows) et
00:31:15votre bac à sable, ou tout fonctionne-t-il parfaitement ?
00:31:20Nous n'avons eu aucun problème avec cela. La plateforme est très stable
00:31:25jusqu'à présent. Ce n'est pas de la promo, c'est très sincère : cela fonctionne très bien.
00:31:32Nos soucis viennent surtout des erreurs de manipulation des utilisateurs. Avec le temps,
00:31:36notre objectif est d'éliminer ces pièges et de rendre la plateforme toujours plus simple d'accès
00:31:42pour les développeurs comme pour les non-développeurs. Super, merci. Je voulais poser une question sur...
00:31:48la sérialisation du code utilisateur initial. J'imagine que c'est pour garantir
00:31:52la sécurité ou la possibilité de l'exécuter. Je souhaitais en savoir un peu plus.
00:31:57Vous avez dit que vous sérialisez le code pour obtenir les tags de worker afin de définir les entrées attendues
00:32:04pour le code utilisateur, ainsi qu'une description de l'outil. Est-ce destiné
00:32:10à votre agent pour exécutez le code ? J'ai remarqué que vous lancez déjà la fonction
00:32:15d'exécution de l'utilisateur. Je suis curieux de savoir pourquoi vous effectuez cette
00:32:21étape supplémentaire pour récupérer les entrées et la description de l'outil.
00:32:24Excellente question. C'est plus clair dans le vrai produit, mais j'ai simplifié ici.
00:32:29Vous demandez pourquoi exécuter le code utilisateur une première fois pour obtenir des informations sur le
00:32:34worker, puis de nouveau lors de l'appel de l'outil ? La raison est qu'avant que l'agent
00:32:39puisse appeler l'outil ou même savoir qu'il existe, nous devons découvrir ce
00:32:45qu'il contient pour le fournir à l'agent via le SDK. Dans Notion
00:32:50Workers, par exemple, lorsqu'on lance NTN Workers Deploy, on passe par le pipeline de build,
00:32:55le bac à sable exécutant le build prend l'archive et la stocke. Ensuite,
00:33:02dans un nouveau worker, nous extrayons le nom des outils, les descriptions et les
00:33:09schémas pour les enregistrer dans DynamoDB. Ainsi, par la suite, nous ne lançons
00:33:14aucun bac à sable avant l'appel effectif de l'outil. Vous avez mentionné le système d'archives. C'est
00:33:21ce qui m'intrigue le plus. Comment fonctionne exactement la distribution avec ces archives ?
00:33:26Avez-vous un catalogue public actuellement ou comment gérez-vous la distribution ?
00:33:31Vous voulez savoir ce que contient l'archive ? Oui, c'est ça.
00:33:33Concernant le fonctionnement technique de ce qui va dans l'archive,
00:33:38nous utilisons esbuild. Le pipeline de déploiement réel fonctionne en exécutant
00:33:47NTN workers deploy, qui appelle une API. Votre machine reçoit une URL pré-signée, regroupe
00:33:52votre code source dans une archive, puis un processus de build esbuild tourne dans le
00:33:58bac à sable. Le résultat est remis sur le stockage blob pour pouvoir être exécuté
00:34:04directement depuis là. Nous n'avons pas encore de marketplace pour les workers. Nous travaillons sur le partage
00:34:11au sein d'un espace de travail Notion d'abord, mais nous prévoyons de créer un catalogue de workers à l'avenir.
00:34:18En attendant, vous pouvez les partager sur GitHub et cela fonctionne très bien. C'est ce que font les gens aujourd'hui.
00:34:23Il suffit de publier un dépôt, quelqu'un peut le cloner, lancer NTN workers deploy et l'utiliser.
00:34:29Tout à fait.
00:34:34Je crois qu'il y a une question au fond.
00:34:37Oui, j'ai une question concernant la facturation.
00:34:40Sur la facturation ?
00:34:40Oui. En cherchant rapidement sur Google — sans trahir de secrets bien sûr —
00:34:44il semble que les agents personnalisés fonctionnent avec un système de crédits. J'imagine
00:34:48que c'est lié à la consommation de ressources.
00:34:51Oui, exactement.
00:34:51Comment cela s'articule-t-il avec la plateforme Vercel dans les grandes lignes ?
00:34:54La question est : comment fonctionne la facturation de ces éléments avec la plateforme Vercel ?
00:35:02L'un des avantages des workers est qu'ils permettent d'éviter d'avoir un énorme ensemble d'instructions
00:35:08via des serveurs MCP fournis aux agents. À chaque appel de tâche,
00:35:16l'agent consomme énormément de jetons de réflexion pour refaire encore et encore la même chose.
00:35:21En prenant ces tâches répétitives de l'agent pour les déployer sous forme de worker,
00:35:33le temps d'exécution vous est toujours facturé, mais cela coûte bien moins cher que des jetons de calcul IA.
00:35:40Lorsqu'un agent personnalisé réfléchit, exécute un outil, puis réfléchit à nouveau,
00:35:48vous payez pour la consommation de jetons de l'agent personnalisée avant l'appel de l'outil.
00:35:53Ainsi, lorsque l'outil s'exécute, vous êtes facturé à un tarif différent qui consomme vos crédits, comme vos crédits Notion AI, à un rythme beaucoup, beaucoup plus bas.
00:36:01Puis les crédits IA habituels s'appliquent à nouveau lorsque l'agent formule sa réponse finale.
00:36:05Si vous utilisez les agents personnalisés Notion, c'est un excellent moyen de réduire les coûts sur les tâches répétitives.
00:36:16C'est exact. L'accès à Notion AI n'est pas obligatoire : nous présentons ici des appels d'outils intégrés à Notion AI,
00:36:24mais les workers gèrent aussi la synchronisation tierce dans Notion, ce qui ne nécessite aucune fonction d'IA.
00:36:31Ce n'est donc pas uniquement dédié à l'IA, les gens s'en servent pour synchroniser des données.
00:36:36J'ai par exemple des workers qui synchronisent mon flux Letterboxd dans Notion. C'est mon préféré.
00:36:46Très bien. D'autres questions ?
00:36:52Une dernière question ?
00:37:06Vous voulez parler d'exposer les agents personnalisés Notion via votre propre interface utilisateur ?
00:37:13Ce n'est pas possible pour le moment.
00:37:16Pardon, merci MJ. La question était : si on a nos propres workers et agents personnalisés,
00:37:22peut-on les intégrer dans notre propre application pour nos utilisateurs ?
00:37:28Aujourd'hui, ce n'est pas possible. Nous avons une version alpha d'une API d'agents personnalisés qui le permettra.
00:37:35En définissant des agents personnalisés et des workers dans votre espace de travail,
00:37:40vous pourrez utiliser une API pour les appeler et recevoir une réponse en streaming. C'est donc envisageable.
00:37:46Peu de gens l'utilisent encore, car c'est une fonctionnalité publique mais en version alpha.
00:37:57Y a-t-il d'autres questions ?
00:38:00Parfait. Merci à tous d'avoir pris le temps
00:38:04d'assister à cette présentation. Si vous avez des questions sur Notion, Notion Workers
00:38:08ou Vercel Sandbox, n'hésitez pas à venir nous voir, MJ et moi. Merci !

Key Takeaway

L'utilisation de Vercel Sandbox permet à Notion Workers d'exécuter du code utilisateur non vérifié en toute sécurité tout en extrayant automatiquement les outils pour les agents d'IA.

Highlights

  • Notion Workers utilise un kit de développement et un environnement d'exécution pour étendre Notion avec du code personnalisé.

  • Vercel Sandbox fournit une infrastructure sécurisée pour exécuter du code non vérifié sans gestion d'infrastructure par les utilisateurs.

  • Le déploiement des workers utilise le stockage blob de Vercel pour stocker les archives tarball du code source.

  • L'extraction des outils analyse automatiquement le code utilisateur pour récupérer le nom, la description et le schéma d'entrée.

  • Les processus Node non terminés par des minuteurs actifs nécessitent un arrêt explicite pour éviter le gaspillage de ressources dans les bacs à sable.

Timeline

Présentation des Notion Workers

  • Notion Workers permet d'exécuter du code personnalisé pour synchroniser des données ou créer des outils pour des agents.
  • La sécurité et l'isolement des ressources constituent les défis majeurs lors de l'exécution de code utilisateur non approuvé.
  • Vercel Sandbox résout les problèmes d'infrastructure et de sécurité pour les plates-formes d'exécution de code arbitraire.

Les utilisateurs créent des intégrations personnalisées ou automatisent des flux de travail complexes sans gérer d'infrastructure. L'exécution de code non vérifié entraîne des risques de consommation excessive de ressources et d'attaques par déni de service. L'adoption de Vercel Sandbox évite de concevoir ces couches de sécurité dès l'origine.

Démonstration des agents et des outils

  • Les agents utilisent des workers pour exécuter des tâches spécifiques via des appels d'outils structurés.
  • L'algorithme de recherche complexe du worker plan outing interroge des API de géolocalisation et de transport.

Une démonstration illustre un agent évaluant si un atelier est maudit grâce à un worker nommé Mercure Rétrograde. Un second exemple montre un agent planifiant une sortie à New York à l'aide d'un algorithme de recherche intégré dans un worker, ce qui évite de multiplier les étapes avec un serveur MCP.

Architecture et extraction des outils

  • Chaque worker expose un nom d'outil, une description, un schéma d'entrée et une fonction d'exécution.
  • Le processus de build compile le code TypeScript et le stocke dans le stockage blob de Vercel.

Le code utilisateur définit l'outil dans un module JavaScript unique. Le système extrait automatiquement les métadonnées sans imposer de fichier de manifeste statique ou de build local lourd. Les archives tarball sont stockées et récupérées pour alimenter les bacs à sable.

Exécution sécurisée dans le sandbox

  • L'extraction des outils exécute une commande Node dans le sandbox et analyse la sortie standard.
  • L'arrêt explicite du processus Node prévient le gaspillage de ressources causé par des minuteurs non résolus.
  • La facturation des workers consomme des crédits à un tarif inférieur à celui des jetons de calcul IA.

Le script analyse la sortie standard du bac à sable pour récupérer les schémas d'entrée et les descriptions validés par Zod. Le nettoyage des bacs à sable et la terminaison forcée des processus garantissent la stabilité de la plate-forme. L'intégration de ces workers réduit les coûts d'inférence des agents sur les tâches répétitives.

Community Posts

View all posts