Ship 26 NYC - Taller - Mini-trabajadores

VVercel
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00Hola a todos. Mi nombre es Jonathan Clem, o pueden llamarme Jay Clem, y soy ingeniero de software en
00:00:11Notion, donde trabajo en nuestra plataforma para desarrolladores, pero me enfoco particularmente en un producto más nuevo
00:00:17del que quizás hayan oído hablar llamado Notion Workers. En este taller de hoy voy a darles
00:00:23una pequeña visión general de qué son los Notion Workers y por qué los construimos usando Vercel
00:00:28Sandbox. Y luego los llevaré a través de una versión pequeña de Workers,
00:00:36para que puedan tener al menos una idea de cómo se construye un producto así utilizando Vercel Sandbox.
00:00:43Así que si no están familiarizados con lo que son los Workers, son un SDK y un entorno de ejecución donde pueden escribir
00:00:50código personalizado y extender Notion con él. Por lo tanto, pueden hacer cosas como sincronizar datos de terceros en Notion,
00:00:57pueden escribir llamadas a herramientas personalizadas para sus agentes. Hemos visto a gente hacer cosas locas y divertidas como pedir
00:01:04sus comestibles con Notion Workers o controlar su casa inteligente. También hemos visto flujos de trabajo muy complejos
00:01:11especialmente de las áreas de TI y seguridad. Lo bueno de los Workers es que no hay
00:01:17infraestructura que deban administrar. Simplemente escriben código o hacen que un agente de codificación lo escriba por ustedes, y Notion
00:01:23se encarga de asegurar que siempre esté activo, disponible y funcionando. Esto ha sido un gran beneficio para los usuarios de Notion,
00:01:30especialmente para los desarrolladores. Ya no tienen que esperar a que Notion construya, ya saben, integraciones nativas
00:01:36para ellos. Cualquier cosa que desearían que Notion pudiera hacer pero que no hace, pueden simplemente escribirla
00:01:42ellos mismos. Así que cuando comenzamos a diseñar Notion Workers, mi principal preocupación era
00:01:53la seguridad. Tengo algo de experiencia construyendo plataformas donde ejecutas código de usuario no confiable.
00:01:59He trabajado en GitHub Actions, por ejemplo, durante mucho tiempo. Y me preocupaba principalmente la
00:02:04dificultad de construir infraestructura para un producto como este y especialmente la seguridad. Hay muchas
00:02:09cosas de las que debes preocuparte. Por ejemplo, quieres asegurarte de que el código escrito por los usuarios
00:02:14no pueda tocar las bases de datos de Notion, por supuesto, o no pueda tocar los servicios de Notion a los que normalmente no deberían poder
00:02:20acceder. También quieres asegurarte de que los usuarios no puedan afectar a otros usuarios. No quieres que el código de un usuario,
00:02:26ya sabes, pueda acceder al código de otro usuario o, por supuesto, a sus secretos ni nada de eso.
00:02:31Esto no se trata solo de seguridad. También se trata de equidad y compartición de recursos. Si hay un usuario
00:02:37haciendo algo que consume una tonelada de CPU y una tonelada de memoria, quieres asegurarte de que eso no esté
00:02:42afectando injustamente a otros usuarios que intentan hacer cosas al mismo tiempo. Me voy a tomar un minuto
00:02:48y salirme un poco del tema para contar una historia aquí. Esto es, lo de compartir recursos es especialmente algo muy
00:02:54complicado. Probablemente esto me consuma tiempo, pero me gusta esta historia. Cuando estábamos construyendo
00:02:58algo como GitHub Actions, y esto es algo en lo que pensamos con los workers también, tan pronto como tienes una
00:03:02plataforma de ejecución de código arbitrario, la gente inmediatamente va a intentar hacer criptominería en ella.
00:03:07Y estaba teniendo una conversación con alguien hace un par de semanas donde me decían, bueno,
00:03:10¿cómo detectas eso? Puedes simplemente saber si la CPU está al cien por ciento, ¿verdad?
00:03:14Bueno, en realidad no, porque una vez que eres una plataforma exitosa, inmediatamente los mineros de criptomonedas van a
00:03:20empezar a hacer cosas como compartir scripts entre ellos que modifican la forma en que
00:03:26ejecutan las instrucciones de la CPU para que parezca una actividad totalmente inocua, pero están usando
00:03:31la cantidad absoluta máxima de recursos posible sin que sean marcados en tu sistema.
00:03:37Así que es extraordinariamente difícil, y toma muchos años y muchas capas de seguridad y
00:03:42observabilidad para hacer esto bien. Otra cosa de la que tienes que preocuparte es que cuando tienes una
00:03:48plataforma de ejecución de código, tienes que preocuparte de que la gente realice ataques de denegación de servicio con ella,
00:03:54usándola para redes de comando y control de botnets. Y simplemente no queríamos tener que preocuparnos por todo esto
00:04:00desde el principio con los Notion Workers. Queríamos centrarnos en lo que nos encanta hacer, que es
00:04:07entregar un producto que a nuestros usuarios les encante usar. Así que por eso decidimos construir con Vercel Sandbox.
00:04:14Vercel Sandbox, pudimos ver de inmediato, era una infraestructura realmente sólida que resolvió muchos de
00:04:18estos problemas, muchos de estos problemas para nosotros desde el primer momento. Así que voy a mostrarles una demostración súper rápida
00:04:25de qué son los Notion Workers. Así que tengo un agente personalizado aquí y el trabajo de este agente personalizado es decirme
00:04:33si este taller está maldito o no. Escribí un worker llamado Mercurio Retrógrado. No sé si alguien está
00:04:40familiarizado con esto, pero cuando Mercurio está alineado de tal manera que está en retrógrado, suele ser
00:04:47un mal augurio. Así que este worker tiene una sola llamada a herramienta personalizada que usa una API que encontré que no hace otra cosa
00:04:54más que decirte si Mercurio está en retrógrado o no. Así que voy a darle una instrucción a mi agente y decirle: “¿Está mi
00:05:01taller maldito?” y va a pensar por un minuto y luego deberíamos, siempre y cuando el internet se esté portando bien,
00:05:14empezar a verlo hacer una llamada a herramienta. Así que está usando mi worker de Mercurio en retrógrado, llamando a la
00:05:22única herramienta que ese worker expone y averiguando si Mercurio está en retrógrado o no, y luego el
00:05:27agente nos va a responder. Creo que tengo esto conectado a algo como GPT-54 nano. Así que suele ser mucho
00:05:37más rápido. Creo que esto es el internet, desafortunadamente. Si alguien dio la casualidad de verificar si Mercurio está en
00:05:46retrógrado, sé de antemano que lo está. Y por eso es probable que esto esté pasando. Voy a
00:05:53saltármelo. Volveremos a ello en un minuto y veremos si eventualmente obtuvimos una respuesta. Pero creo que ya
00:05:59sabemos la respuesta, al parecer. Bien, ahora ya tienen una idea de qué son los notion workers. Voy a llevarlos
00:06:05a través de un pequeño script que escribí que hace muchas de las tareas básicas involucradas en tomar
00:06:13código de usuario, desplegarlo, ejecutarlo de forma segura y contarle a un agente sobre ese código para que pueda
00:06:20hacer llamadas a herramientas hacia él. ¿Esto llegó a completarse? Ah, sí, aquí vamos. Oh, creo que tal vez,
00:06:28 supongo que debo haber bajado la API de Mercurio en retrógrado porque aparentemente la API no respondió.
00:06:35En fin, tendré que abrir un ticket en alguna parte. Genial. Así que tengo un script básico. No espero
00:06:40que sigan el ritmo totalmente y escriban código ni nada de eso. Voy a saltar un poco
00:06:44y darles una idea de algunos de los problemas que resolvimos que tienen que resolver cuando están construyendo un
00:06:49producto como este. Pero si quieren, hay un repositorio make notion slash for cell ship 2026 workers
00:06:55que tiene todo el código que voy a estar ejecutando aquí.
00:07:01Genial. Permítanme abrir mis otras diapositivas aquí.
00:07:08Entonces, ¿hacia qué estamos trabajando? Tenemos un agente de chat en streaming. Vamos a permitirle llamar a herramientas que
00:07:16están definidas por código de usuario personalizado en el que no confiamos o cuyo contenido desconocemos.
00:07:22Y vamos a construir esto con Vercel sandbox y el servicio de almacenamiento blob de Vercel.
00:07:26Así que voy a ejecutar una demostración rápida aquí y esperemos que no estemos malditos en realidad.
00:07:31Básicamente puedes pasarle un solo mensaje. Voy a decir hola y esperemos que obtengamos una respuesta
00:07:36de vuelta. Va a ser un poco lento porque verán que está haciendo algunos despliegues en segundo plano.
00:07:40Así que llamó a una herramienta esta vez. Acabo de llamar a una herramienta llamada decir hola y decidió saludarme.
00:07:47Le enviaré un mensaje normal aquí preguntándole qué es uno más uno para que podamos ver que simplemente
00:07:52transmite una respuesta normal.
00:07:58Si han usado el SDK de IA de Vercel, esto simplemente está usando el bucle de agente de herramientas. Genial. Así que obtuvimos una respuesta
00:08:04en streaming de vuelta. También puedes llamar a workers mucho más complejos. Así que tengo otro aquí que les mostraré.
00:08:14Con este le estamos diciendo que estoy en la dirección de este edificio.
00:08:17Tengo 90 minutos y quiero ir a ver algún sitio histórico y no quiero caminar más de 15 minutos.
00:08:24Así que esto ilustra por qué a veces se prefieren sobre los servidores MCP. Con un servidor
00:08:29MCP tienes un montón de llamadas a herramientas dispares que puedes hacer. Y si estás intentando hacer algo
00:08:33complejo, tienes que describir esos pasos al agente y este va a hacer un paso, gastar algunos
00:08:38tokens de pensamiento, hacer un paso. Así que esto va a llamar a un worker que tiene un tipo de algoritmo de búsqueda
00:08:45muy grande y complejo que usa un montón de tiempos de tránsito de geolocalización de la ciudad de Nueva York y APIs de planificación de rutas.
00:08:52Se llama planificar salida.
00:08:56Y ese worker va a responder con un sitio histórico propuesto que podemos visitar.
00:09:00Y parece que encontró el marcador del árbol de la libertad, que es un monumento conmemorativo de la MIPOW que está cerca de
00:09:07City Hall Park. Genial. Echemos un vistazo a cómo funciona esto. Entonces, ¿qué es un worker?
00:09:14Es código de usuario que en este caso está definido simplemente en un archivo. Normalmente esto sería, ya sabes, definido
00:09:20por tus usuarios o en algún lugar de GitHub y desplegado con una CLI. Pero tenemos algunos ejemplos guardados
00:09:26en el repositorio aquí. Y cada uno de estos workers necesita exponer el nombre de la herramienta, una descripción de la herramienta para que
00:09:34el agente pueda enrutar a la herramienta correcta, el esquema de entrada para que el agente sepa qué entradas necesita
00:09:38proporcionar cada vez que llama a esa herramienta. Y una función de ejecución que dice cuando el agente llama a esta herramienta,
00:09:44¿cuál es el código real que se ejecuta? Así que veamos un ejemplo. Aquí está la herramienta de saludo que vieron
00:09:51anteriormente. Es muy simple. Este worker es un módulo de JavaScript aquí en este archivo index.ts. Exporta un solo
00:09:59worker llamado decir hola. Así que verán cómo funciona este proceso de compilación. Pero en este ejemplo,
00:10:04todas las claves de exportación de nuestros módulos se mapean a los nombres de nuestras herramientas. Así que esta herramienta se va a llamar
00:10:09decir hola. Y tiene una descripción simple, un esquema de entrada. En este caso, simplemente estoy usando Zod para definir
00:10:16el esquema. Y luego lo estoy convirtiendo en un esquema JSON. Ese es el formato que estos agentes esperan.
00:10:22Y luego hay una función de ejecución simple. Sin embargo, puedes hacer otras mucho más complejas. Si tuviera que
00:10:27abrir el flujo de trabajo de planificar salida, pueden ver que tiene una descripción mucho más larga que le dice al agente
00:10:34todo sobre cómo funciona esta herramienta, cuándo llamarla, qué hace. Y el script para esto es mucho, mucho,
00:10:39mucho más largo y complejo. No voy a repasar todo esto. Y solo he leído pequeñas partes
00:10:45de esto, pero funciona bastante bien. Ese es el mundo en el que estamos ahora. Así que estos workers van a ser
00:10:54construidos y desplegados en el almacenamiento blob de Vercel. Luego vamos a aprender sobre el contenido de los
00:10:59workers de alguna manera. Y vamos a exponerlos a un agente. Y luego esas herramientas van a ser
00:11:03ejecutadas de manera segura en un sandbox. La parte de compilación, vamos a omitirla un poco. Con los Notion workers, tenemos un
00:11:09proceso de compilación y despliegue basado en la nube. En este caso, simplemente he precompilado estos workers en el disco. Así que cada
00:11:15worker tiene un tarball que contiene todo el código TypeScript compilado, dependencias y cosas por el estilo.
00:11:22No es la parte súper interesante, así que simplemente la voy a pasar por alto.
00:11:26Así que la pregunta del día es, ¿cómo pasamos de código de usuario que nunca hemos visto y en el que no confiamos
00:11:34a herramientas que son expuestas y ejecutadas de forma segura por el agente? Así que hay dos partes en esto.
00:11:39La primera parte es, una vez que tenemos ese código de usuario en el almacenamiento blob, digamos,
00:11:43¿cómo aprendemos qué hay en ese código? Porque tenemos que ser capaces de decirle al agente antes de que
00:11:48llame a la herramienta, ¿cuál es el nombre de la herramienta, cuál es el esquema de entrada y cuál es la descripción
00:11:53de la herramienta? Y luego la segunda pregunta es, cuando el agente decide llamar a esa herramienta,
00:11:58¿cómo la ejecutamos realmente de forma segura? Una de las cosas que amo de lo que hicimos con el
00:12:04SDK de Notion Workers y que hemos hecho aquí de alguna manera es que el código se describe a sí mismo. Lo que no
00:12:11queríamos cuando diseñábamos Notion Workers es que los usuarios tuvieran que escribir código TypeScript
00:12:16definiendo su herramienta y luego decir, bueno, ahora tengo que crear un archivo de manifiesto estático que describa
00:12:21mis workers y básicamente reescribir lo mismo. No queríamos un proceso de compilación local incómodo donde
00:12:27tuvieran que ejecutar un script, hacer algo de análisis, compilarlo en el disco y luego desplegarlo. Queríamos empezar
00:12:34con lo que se sentía como la experiencia de desarrollo óptima, donde simplemente escribes la herramienta y luego es nuestro
00:12:40problema resolver lo difícil de averiguar cómo extraer información de eso.
00:12:46Este es un diagrama súper simple, pero antes de entrar en el código, les hablaré sobre
00:12:52cómo vamos a resolver esto. Tenemos el código compilado del usuario en el almacenamiento blob.
00:12:59Vamos a usar ese código para crear un sandbox. Así que cuando ejecutamos comandos en el sandbox, el código de ese usuario
00:13:04va a estar en la raíz del espacio de trabajo. Vamos a importar el archivo index.js del usuario que escribieron.
00:13:11Eso nos dará todos los nombres de las exportaciones, esas descripciones y los esquemas de entrada.
00:13:19Aquí es donde las cosas se ponen un poco extrañas, pero funciona muy bien. Vamos a llamar
00:13:23a json.stringify en ese módulo y luego vamos a registrarlo en la salida estándar (stdout). Todo eso ocurre en
00:13:29el sandbox y luego nuestro script de despliegue consume esa salida estándar, la analiza y dice, de acuerdo,
00:13:34así que ahora conozco el nombre de todas las herramientas de este worker, las entradas y la descripción. Y en este
00:13:40caso, se lo pasamos directamente al agente. Pero en el caso de los notion workers, por ejemplo,
00:13:44eso es parte de nuestra tubería de despliegue. Así que tomamos toda esa información y la almacenamos en una base de datos para que
00:13:49cada vez que ejecutes un agente personalizado, obtengamos esas descripciones de herramientas de la
00:13:53base de datos. ¿Tiene más o menos sentido hasta ahora cómo funciona esto? Bien. Echemos un vistazo al script
00:14:02de despliegue. Tengo que volver a mi primer commit aquí. Así que este script tiene todo nuestro despliegue y luego la llamada
00:14:12al agente integradas en él. Es súper simple. En este caso, simplemente estamos iterando sobre todos los directorios
00:14:18en workers. Cada uno de esos subdirectorios contiene código de worker como vieron hace un momento.
00:14:23Nuestro trabajo es averiguar cómo extraer toda la información de herramientas de cada uno de esos workers y poblar
00:14:29este objeto tools. Eso se pasa a un agente de bucle de herramientas. Esto es solo parte del SDK de IA creado
00:14:35por Vercel. Y luego vamos a enviar el mensaje del usuario a ese agente, transmitir la salida y escribirla
00:14:42en la salida estándar desde el script. Así que lo primero que necesitamos averiguar es ¿cómo subimos
00:14:48el código fuente? Esa parte es bastante fácil. Es bastante rápida. En lugar de codificar en vivo, voy a
00:14:53saltar entre cada uno de estos fragmentos para que no tengan que verme escribir. Prometo que puedo escribir
00:14:59código. Solo creo que nadie quiere verme hacer eso. Así que avanzando un poco, tenemos esta función
00:15:06llamada upload source en la que profundizaré. Pero pueden ver que he importado el SDK de Vercel blob. Esto es bastante
00:15:13simple aquí. Si miramos esta función upload source, básicamente estamos llamando a esta función put
00:15:20y estamos diciendo que queremos almacenar este bundle del usuario bajo el nombre del worker barra bundle.tar.gzip.
00:15:29Vamos a transmitir el archivo desde el disco y lo vamos a almacenar en el almacenamiento blob. Una vez que eso esté hecho,
00:15:35podemos crear sandboxes a partir de ese blob. Así que la parte de la subida está resuelta.
00:15:43Lo siguiente que debemos hacer es tomar ese código fuente empaquetado y necesitamos crear
00:15:49un sandbox a partir de él. Para hacer eso, he importado el SDK de Vercel sandbox. Hay otra función auxiliar
00:15:56más abajo llamada create sandbox. Omitiré un par de partes de esto. Pero esencialmente lo que estamos
00:16:03haciendo es cuando tenemos ese objeto en el almacenamiento blob, usamos URLs pre-firmadas que vamos a
00:16:10pasar al servicio de sandbox. Para que el servicio de sandbox pueda, por decir, creo que esto está configurado con 10 minutos
00:16:16de expiración, puede traer ese blob desde el almacenamiento blob y usarlo para poblar un sandbox. Esta es una de las
00:16:23características que realmente me gustan de Vercel sandbox: que funciona con tarballs como este. Es fácil
00:16:28construir un tarball de código de usuario, un servicio de sandbox del que quieras crear un sandbox a partir de un tarball,
00:16:35lo toma del almacenamiento o de cualquier URL que le des y extrae automáticamente ese tarball en la
00:16:40raíz del espacio de trabajo. Así que todos los archivos están ahí listos para que trabajes con ellos. Y eso se hace simplemente llamando
00:16:46a sandbox.create. No estamos en la parte súper complicada de esto, pero llegaremos pronto.
00:16:52Otra cosa es que en este ejemplo, solo por brevedad, estoy creando sandboxes nuevos cada vez
00:16:57que los ejecutamos. En realidad, querrás usar instantáneas (snapshots) para no estar constantemente como si cada
00:17:02vez que se llama a una herramienta, redeployaras el sandbox o lo retransmitieras desde el almacenamiento blob de Vercel
00:17:09o cualquier otro almacenamiento blob en la nube que estés usando. Básicamente hay un mecanismo de caché incorporado
00:17:15en la plataforma. Lo he omitido aquí por simplicidad.
00:17:19Así que eso es crear el sandbox y aquí es donde se pone interesante.
00:17:23Lo siguiente que debemos hacer es extraer información sobre las herramientas
00:17:31que están definidas en ese worker a partir de ese sandbox que acabamos de crear.
00:17:36Voy a hacer una pausa rápida aquí y hay un par de consejos que he repartido
00:17:40a lo largo. Quieres asegurarte en general, si estás construyendo un servicio de nivel de producción como
00:17:44este, siempre debes detener, hacer el mejor esfuerzo para detener tus sandboxes. Así que en este caso, verás
00:17:51lo que hace extract tools, pero cuando terminamos con él, nos aseguramos de eliminar el sandbox explícitamente.
00:17:56En realidad, también querrás asegurarte de encolar probablemente un trabajo de limpieza asíncrono
00:18:00o algo por el estilo. No quieres que estos sandboxes se queden ahí indefinidamente.
00:18:06Así que echemos un vistazo a lo que hace la función extract tools. Tenemos nuestro sandbox y realmente me gustan estos
00:18:15ejemplos porque parece muy gracioso lo simple que es, pero funciona muy, muy bien. Así que ejecutamos un
00:18:23comando node usando el binario de node en el sandbox y en el script importamos el módulo que
00:18:30el usuario escribió, que es simplemente index.js. Convertimos ese objeto a cadena (stringify) y luego llamamos
00:18:36a console.log. Lo que hay que tener en cuenta es que los sandboxes no son como un servidor web donde puedes
00:18:42enviar una solicitud y obtener una respuesta de vuelta. Toda la entrada y salida se realiza mediante la ejecución de un comando y luego
00:18:48puedes hacer que envíe una respuesta a algún servicio que consultes o, en este caso, simplemente puedes hacer que
00:18:52registre en la salida estándar. Un par de consejos aquí. Normalmente no quieres simplemente usar console.log y confiar en toda
00:19:00la salida que obtienes del sandbox. Podría haber otros paquetes que el usuario haya instalado o
00:19:07otro código que estén ejecutando que pueda registrar cosas al mismo tiempo en el flujo de salida estándar.
00:19:12Así que algo bueno que hacer es envolver tu salida en algún tipo de etiqueta que puedas analizar.
00:19:18Esto es como una etiqueta de tipo XML que haríamos aquí. Simplemente no lo estoy haciendo en el ejemplo.
00:19:23También quieres asegurarte de limitar el tamaño de los registros que consumes. No estoy
00:19:29recopilando toda la salida en este ejemplo en un flujo. En una aplicación de nivel de producción, no
00:19:34quieres hacer eso porque podría ser, ya sabes, un gigabyte de flujos y vas a agotar la memoria (OOM) de tus servidores.
00:19:39Así que también hay comandos que la API de sandbox tiene para transmitir los registros. Y eso es lo que hacemos
00:19:45con notion work: básicamente consumimos ese flujo hasta que vemos el inicio del token de salida
00:19:53que nos importa. Luego comenzamos a almacenar en búfer esa descripción, lo que registramos intencionalmente.
00:19:58Y dejamos de procesar el flujo de salida tan pronto como alcanzamos nuestra etiqueta de cierre. Así que si algo
00:20:04raro sale mal en el sandbox y se registran enormes cantidades de datos,
00:20:08no se acumulará todo en la memoria. Podemos simplemente descartarlo y esperar hasta obtener lo que nos importa.
00:20:14Así que una vez que tenemos ese comando ejecutándose, esperamos a que termine y tomamos su salida estándar.
00:20:22Y si has usado algo como Zod antes, esto te resultará bastante familiar. Solo estamos analizando esa cadena como JSON.
00:20:27Y luego tenemos un tipo de Zod aquí que está
00:20:31validando que tenga la forma que queremos.
00:20:34Es muy importante no confiar en estos datos porque es código de usuario no confiable. No tienes idea
00:20:39de lo que la gente podría estar registrando. Así que quieres asegurarte de estar
00:20:43validando el tamaño y validando su forma general. Así que en este caso, lo que esperamos
00:20:50es un registro cuyas claves, en forma de cadena, van a ser el nombre de cada una de nuestras herramientas.
00:20:57Y los objetos van a ser la descripción y luego el esquema de entrada, que es ese
00:21:02esquema JSON. Notarás que la función de ejecución no está aquí. Eso simplemente
00:21:06no se devuelve, lo cual es genial porque stringify en JSON simplemente omite las cosas que no son
00:21:11serializables. Así que más o menos se ignora. Por lo tanto, solo obtenemos la descripción y el esquema de entrada.
00:21:18Así que volviendo arriba, tenemos nuestras herramientas de trabajador que son ese
00:21:23registro de cada herramienta que se expuso en ese trabajador.
00:21:28Lo siguiente que hacemos es poner esos datos en la estructura que espera el SDK de IA.
00:21:36Y necesitamos adjuntar una función de ejecución a cada una de esas herramientas.
00:21:42Así que obviamente no obtuvimos una función de ejecución cuando solo estábamos registrando cosas en la salida estándar.
00:21:46Entonces la pregunta ahora es, ya que tenemos esta descripción de la herramienta, ¿cómo proporcionamos
00:21:51una función que el SDK pueda llamar cada vez que quiera invocar a este trabajador o a esta herramienta expuesta por
00:21:58este trabajador? Así que tengo este pequeño envoltorio aquí llamado execute tool. Echemos un vistazo a lo que
00:22:04hace. Debería resultar bastante familiar ahora. Está llamando a esa misma función create sandbox. Así que está
00:22:10creando un espacio aislado nuevo. Incluso si estás usando almacenamiento en caché, hay una función de los espacios aislados de Vercel llamada
00:22:16persistencia, donde cada vez que el espacio aislado se suspende o se detiene, almacena en caché el estado, como todo lo que está
00:22:23en el disco. Para una función como esta, en realidad no quieres eso. Quieres tomar una instantánea de tu estado inicial
00:22:28donde tiene todo el código del usuario. Pero en general, después de eso, debes asegurarte de que cada vez
00:22:33que se ejecute esa herramienta, probablemente quieras una instancia totalmente nueva. De ese modo, una ejecución de una herramienta,
00:22:38si algo sale mal, no contaminará el entorno de la herramienta que se ejecute después.
00:22:43Así que creamos un nuevo espacio aislado y ejecutamos otro script de Node en él. Esto debería parecer familiar,
00:22:49pero es un poco diferente. Estamos importando el módulo que escribió el usuario. Estamos tomando
00:22:56la herramienta de ese módulo, que es simplemente el nombre de la herramienta que obtuvimos al crear este envoltorio execute tool.
00:23:03Estamos llamando a esa función de ejecución en ella. Y le pasamos a esa función de ejecución la entrada que fue
00:23:09proporcionada por el modelo. Aquí simplemente tipifico esto como unknown. Sin embargo, esto es bastante seguro aquí, porque
00:23:19proporcionamos un... ¿acaso no lo hice aquí? Creo que pude haberme saltado esto en este ejemplo. Pero lo que
00:23:27normalmente harías es nuestro... oh, no, creo que sí lo hice. Déjame volver arriba. Sí. Así que cuando estamos
00:23:34manipulando nuestras herramientas para enviarlas al agente, estamos tomando ese esquema JSON y lo estamos convirtiendo
00:23:39de nuevo en un tipo de Zod. Así que no tenemos que hacer ningún análisis nosotros mismos. El SDK de IA, el código del agente del bucle de herramientas,
00:23:46cada vez que se llame a esa herramienta, va a validar por nosotros la entrada proveniente del agente.
00:23:52Así que más o menos podemos confiar en que este valor es el que esperamos. Por lo tanto, vamos a pasarlo
00:23:56a la función de ejecución aquí. Luego, cuando esa función asíncrona se complete, vamos a
00:24:03serializarla. La registraremos en la salida estándar. Y luego esperaremos... hablaré de estas cosas
00:24:10en un segundo. Esperaremos a que ese comando se complete. Y de nuevo, vamos a
00:24:14eliminar nuestro espacio aislado y habremos terminado con él. Y luego analizaremos ese JSON, que es el valor de retorno
00:24:21de esa función de ejecución que escribió el usuario. Y se lo enviaremos de vuelta al agente del bucle de herramientas.
00:24:26Así que esencialmente el flujo aquí es que el agente del bucle de herramientas dice: Quiero llamar a la herramienta plan outing
00:24:35que vimos antes, donde utiliza todas esas API de transporte. Eso termina llamando a esta función aquí con
00:24:40cualesquiera que sean las entradas que decida el agente. Creamos un espacio aislado usando el blob que subimos desde el
00:24:48código compilado del usuario antes. Luego ejecutamos un comando en ese espacio aislado donde llamamos a la función de ejecución
00:24:55del usuario. Esperamos a que esa función devuelva un valor. Y luego registramos ese valor de retorno en
00:25:00la salida estándar, lo analizamos y luego se lo enviamos de vuelta al agente, quien continuará el bucle
00:25:05de herramientas y realizará otra llamada o le responderá al usuario. Un par de consejos rápidos aquí. Se aplican las mismas
00:25:13reglas de verificación de salida en un sistema de producción. De nuevo, quieres asegurarte de que los usuarios no puedan
00:25:19devolver un objeto que tarde unos dos gigabytes o algo así en analizarse. También querrás
00:25:28probablemente proporcionar un SDK. No hacemos esto en este ejemplo, pero en el SDK de Notion Workers,
00:25:35los tipos están configurados de tal manera que el valor de retorno de esa función de ejecución debe ser serializable en JSON.
00:25:41Ese es un error muy común y fácil para tus usuarios si haces que esa función
00:25:47pueda devolver cualquier cosa, terminarán devolviendo cosas que no se pueden serializar como JSON para enviarse
00:25:52a través de la red mediante la salida estándar y luego analizarse. Y se confundirán mucho sin entender por qué
00:25:57el trabajador no se está ejecutando. Y luego, esta es una pequeña historia que ocurrió con Notion Workers. También
00:26:05quieres tener mucho cuidado de asegurarte de que el proceso, el proceso de Node que estás generando
00:26:11aquí, salga pase lo que pase. Tuvimos un error con Notion donde el código de los usuarios se ejecutaba hasta completarse,
00:26:18pero por alguna razón, este proceso de Node no terminaba. Se quedaba colgado hasta que el ciclo de vida
00:26:25del espacio aislado expiraba, que era como de cinco minutos o algo así. Así que no fue catastrófico,
00:26:30pero estaba desperdiciando recursos. Y lo que descubrimos o recordamos —yo había olvidado por completo que
00:26:38esto es una propiedad del tiempo de ejecución de Node— es que los usuarios ejecutaban código que configuraba temporizadores con
00:26:44setInterval o setTimeout. Y especialmente con los intervalos, o creo que las promesas colgantes hacen lo mismo.
00:26:52Si el script se ejecuta y hay algún intervalo o algo similar funcionando, el proceso de Node
00:26:58nunca terminará. No va a retornar por sí solo hasta que todos esos temporizadores expiren. Así que si es un
00:27:03intervalo, simplemente se ejecutará para siempre hasta que el espacio aislado muera. Por lo tanto, siempre debes asegurarte de que
00:27:08cuando el código o la función que intentas ejecutar realmente se complete, le digas explícitamente
00:27:14al proceso que salga. Así se detiene el espacio aislado y el proceso retorna después de eso.
00:27:23Ese es prácticamente todo el flujo. Voy a ejecutarlo una vez más y voy a activar
00:27:29la depuración para que puedas ver lo que sucede.
00:27:42Así que primero desplegaremos nuestro trabajador de salidas. Explicaré esto en un segundo. Desplegamos nuestro
00:27:48trabajador de salidas subiendo ese paquete primero. Creamos un espacio aislado que usaremos para extraer
00:27:54información sobre ese trabajador. Extrajimos estas herramientas. Esto es lo que obtenemos al registrar el
00:28:01contenido de los módulos en la salida estándar. Obtenemos las claves de cada herramienta, la descripción y el esquema de entrada.
00:28:08Hacemos lo mismo para ese trabajador simple de saludos. Luego tomamos todo eso y se lo pasamos al agente para
00:28:13que pueda realizar llamadas a herramientas y responder al usuario. Eso es prácticamente todo. Es un ejemplo bastante sencillo
00:28:19de cómo puedes tomar código de usuario no confiable, almacenarlo en alguna parte, aprender sobre él de manera segura para
00:28:26poder colocarlo en almacenamiento duradero o enviarlo directamente a agentes y hacer que tus agentes ejecuten
00:28:31ese código de forma segura. Así que nos quedan unos 10 minutos. Si alguien tiene alguna pregunta, si quieren preguntar sobre
00:28:39Notion workers o sobre trabajar con los espacios aislados de Vercel en general o la ejecución de código no confiable,
00:28:46estaré encantado de hablar de ello. Gracias.
00:28:56Ah, sí. Y después, si quieren hablar sobre la plataforma de desarrolladores de Notion en general,
00:29:01pueden hablar conmigo o con mi colega MJ aquí presente. Ella es la gerente de producto de la plataforma de desarrolladores en Notion.
00:29:08Hola. Esta es una pregunta operativa, pero ¿cómo lo limitan, ya que parece que los usuarios pueden hacer cualquier cosa?
00:29:15Sí. Podrías tener a varios usuarios escribiendo una herramienta similar.
00:29:20¿Varios usuarios haciendo qué? Escribiendo una herramienta similar. Por ejemplo, planificar un viaje a la ciudad de Nueva York.
00:29:24Sí. Podrías tener a 10 usuarios diferentes con 10 códigos distintos. Sí. Que hagan lo mismo.
00:29:29¿Hay algo que estén haciendo para bloquear eso o simplemente se le deja al agente?
00:29:33No. Simplemente dejamos que lo hagan; si varios usuarios van a hacer lo mismo, dejamos que lo hagan.
00:29:37Es parcialmente una pregunta de producto. Como si son varios usuarios en la misma organización, ya sabes,
00:29:42quieres asegurarte de que... así que es una pregunta operativa y también de producto.
00:29:46Quieres asegurarte de tener buenas primitivas para compartir y cosas por el estilo. Así que puedo
00:29:50buscar y decir: ¿Ya existe un trabajador que haga este trabajo? Y de esa manera no están
00:29:55reescribiéndolo. Pero a nivel general de plataforma, no hacemos nada para... es muy poco probable que los usuarios
00:30:02implementen código idéntico. Y simplemente no vale la pena intentar desduplicar eso.
00:30:19De acuerdo. Sí, creo que tenemos un par más. No estoy seguro de quién tiene el micrófono.
00:30:24No lo escuché. Oh, oh, sí. Lo siento mucho.
00:30:29Pensé que se estaba capturando en los auriculares.
00:30:33Ah, sí. La pregunta que hicieron fue: si tienes a muchos usuarios implementando el mismo código,
00:30:40¿hacemos algo para poner operativa esa situación? Y no lo hacemos. Es más una pregunta de producto.
00:30:45Queremos asegurarnos de que los usuarios no repitan el mismo trabajo. Por eso queremos buenas primitivas para compartir
00:30:50en las que estamos trabajando ahora mismo para Notion workers. Pero operativamente a nivel de plataforma,
00:30:54si la gente implementa el mismo código 50 veces, no nos importa.
00:30:56¿Tienen problemas con los trabajadores que se quedan sin tiempo de espera porque el espacio aislado simplemente
00:31:04está haciendo lo suyo y muere demasiado rápido?
00:31:07¿Cuál fue exactamente la pregunta sobre los tiempos de espera?
00:31:10¿Tienen problemas con los tiempos de espera entre Vercel, por ejemplo, otros productos y flujos de trabajo,
00:31:15o funciones y su espacio aislado? ¿O simplemente todo marcha bien?
00:31:20Sí, no hemos tenido ningún problema con eso. La plataforma ha sido súper sólida para nosotros
00:31:25hasta ahora. No estoy haciendo publicidad encubierta. Lo digo en serio. Ha sido realmente bueno.
00:31:32Nos topamos con muchos más problemas simplemente porque los usuarios hacen lo incorrecto por accidente. Así que con el
00:31:36tiempo, se trata más de eliminar esos errores comunes y hacer que la plataforma sea cada vez más fácil de
00:31:42usar tanto para desarrolladores como para no desarrolladores. Genial. Eso estuvo muy bien. Quería preguntar sobre,
00:31:48supongo, cuando serializas el código inicial del usuario. Supongo que intentas asegurarte
00:31:52de que sea seguro o de que puedas ejecutarlo. Creo... supongo que quería preguntar un poco más.
00:31:57Dijiste que serializas el código y obtienes, supongo, las etiquetas de los trabajadores para conseguir las entradas deseadas
00:32:04para el código del usuario y luego una descripción de esa herramienta. ¿Es eso para Notion, supongo,
00:32:10para que tu agente ejecute el código? Porque noté que ya estás ejecutando el código de ejecución
00:32:15o la función ejecutora del usuario. Así que tenía curiosidad por saber por qué tú... no diría que te importa, pero estás realizando este
00:32:21paso adicional para obtener las entradas y la descripción de la herramienta en sí.
00:32:24Es una buena pregunta. Está un poco más claro en el producto real, pero aquí está simplificado.
00:32:29La pregunta es básicamente: ¿Por qué ejecuto el código del usuario una vez para obtener información sobre el
00:32:34trabajador y luego lo hago de nuevo cuando se llama al código de la herramienta? La razón es que antes de que el agente
00:32:39pueda llamar a la herramienta en primer lugar o conocer la existencia de la herramienta, tenemos que aprender sobre
00:32:45lo que hay en esa herramienta y luego exponerla al agente a través del SDK. Así que lo que sucede en Notion
00:32:50Workers, por ejemplo, cuando ejecutas algo como NTN Workers Deploy, pasamos por la canalización de compilación,
00:32:55el espacio aislado que ejecuta la compilación toma el archivo tarball, lo guarda en algún almacenamiento. Iniciamos,
00:33:02creo que hacemos esto en un nuevo trabajador donde extraemos el nombre de las herramientas, las descripciones,
00:33:09los esquemas y los almacenamos en algo como DynamoDB. De esa manera, ya sabes, después de eso nunca volvemos a ejecutar
00:33:14espacios aislados hasta que realmente se llama a la herramienta. Mencionaste el sistema de archivos tarball. Supongo que eso es lo que más curiosidad
00:33:21me da. En términos de distribución, ¿cómo funciona exactamente cuando se trata del archivo tarball?
00:33:26¿Tienen algún mercado abierto en este momento o cómo funciona para la distribución?
00:33:31Ah, ¿te refieres a qué hay dentro del tarball real? Sí, sí.
00:33:33No tenemos... bueno, en términos de lo que hacemos mecánicamente, lo que entra en ese tarball,
00:33:38es que usamos ES build en este momento; la forma en que funciona toda la tubería de despliegue real es que ejecutas
00:33:47NTN workers deploy, llamas a un endpoint de la API, tu computadora recibe una URL presignada, empaqueta todo
00:33:52tu código fuente, creamos un tarball con el código fuente, ejecutamos un proceso de compilación con ES build en
00:33:58ese espacio aislado, y luego la salida se devuelve al almacenamiento de blobs para que podamos ejecutar lo real
00:34:04desde ahí. No tenemos un mercado real de trabajadores todavía. Estamos trabajando en primitivas para compartir
00:34:11dentro de un espacio de trabajo de Notion primero, pero definitivamente hay planes para algún tipo de mercado de trabajadores en el futuro.
00:34:18Mientras tanto, puedes simplemente distribuir esto en GitHub y funciona muy bien. Eso es lo que hace la gente hoy en día.
00:34:23Sí, solo publica un repositorio y alguien puede clonarlo, ejecutar NTN workers deploy y usarlo por sí mismo.
00:34:29Sí.
00:34:34Creo que hay una pregunta por allá atrás.
00:34:37Sí, tengo una pregunta sobre la facturación.
00:34:40¿Sobre la facturación?
00:34:40Sí, acabo de mirar... bueno, sin revelar todos tus secretos, por supuesto,
00:34:44pero hice una búsqueda rápida en Google. Parece que los agentes personalizados funcionan con algún tipo de sistema de créditos. Supongo
00:34:48que eso está vinculado al consumo de recursos.
00:34:51Sí, sí.
00:34:51Supongo que, a grandes rasgos, ¿cómo funciona eso con la plataforma de Vercel?
00:34:54Bien, entonces la pregunta es: ¿Cómo funciona la facturación de estos con la plataforma de Vercel?
00:35:02Uno de los beneficios de los trabajadores es que puedes escribir... muchos equipos han podido pasar de
00:35:08un conjunto gigantesco de instrucciones usando servidores MCP que entregan a sus agentes. Y cada vez que llaman a una tarea,
00:35:16ese agente va a gastar montones y montones de tokens de razonamiento haciendo básicamente lo mismo una y otra vez.
00:35:21Y si puedes tomar esas tareas repetitivas que realiza el agente y desplegarlas como un trabajador,
00:35:33sigues pagando por el tiempo de ejecución de tus trabajadores, pero es mucho, mucho menos costoso que los tokens de computación de IA.
00:35:40Así que si estás ejecutando un agente personalizado, este hace algo de razonamiento, ejecuta una herramienta y luego hace más razonamiento.
00:35:48Se te cobra por el consumo de tokens en el agente personalizado que realiza antes de llamar a la herramienta.
00:35:53Por lo tanto, cuando la herramienta se ejecuta, se te cobra a una tarifa diferente que consume tus créditos, tus créditos de Notion AI a una tasa mucho, mucho, mucho más baja.
00:36:01Y luego se te facturan los créditos normales de IA de nuevo cuando el agente finalmente responde.
00:36:05Así que esta es una forma en la que, si usas agentes personalizados de Notion, puedes reducir considerablemente los costos si tienes tareas repetitivas.
00:36:16Eso también es cierto. Tampoco es obligatorio tener Notion AI; no estamos mostrando aquí únicamente llamadas a herramientas, lo cual se integra con Notion AI.
00:36:24Pero los trabajadores también realizan sincronizaciones de terceros en Notion. Así que eso no requiere ninguna función de IA en absoluto.
00:36:31Por lo tanto, no es solo un producto de IA. La gente está usando esto para sincronizar.
00:36:36Tengo trabajadores que sincronizan mi feed de Letterboxd en mi Notion. Ese es el más importante para mí personalmente.
00:36:46¿De acuerdo? ¿Alguna otra pregunta?
00:36:52¿Tienen una más?
00:37:06Oh, como exponerlos a través de tu propio usuario, o sea, exponer los agentes personalizados de Notion a través de tu propia interfaz.
00:37:13No es algo que tengamos hoy en día.
00:37:16Lo siento. Gracias, MJ. La pregunta era: si tienes tus propios trabajadores y agentes personalizados,
00:37:22¿hay alguna manera de exponerlos a través de tu propia aplicación para tus propios consumidores?
00:37:28Y hoy en día no hay una forma de hacer eso. Sí tenemos una versión alfa de una API de agentes personalizados con la que
00:37:35definitivamente podrías hacerlo. Si defines agentes personalizados y trabajadores o agentes personalizados en tu espacio de trabajo,
00:37:40podrías usar una API para llamar a esos agentes y luego obtener una respuesta en streaming. Así que es posible.
00:37:46No creo que un montón de gente lo esté haciendo todavía. Y sigue siendo una función muy en fase alfa, pública pero alfa.
00:37:57¿Otras preguntas?
00:38:00Muy bien. Genial. Bueno, gracias a todos por dedicar el tiempo
00:38:04a venir a escuchar esto hoy. Y sí, si tienen preguntas sobre Notion, Notion workers,
00:38:08o Vercel Sandbox, busquen a MJ y a mí. Gracias.

Key Takeaway

Notion Workers utiliza Vercel Sandbox para ejecutar código de usuario no confiable y desplegar integraciones personalizadas de forma segura mediante contenedores aislados y tarballs.

Highlights

  • Notion Workers es un SDK y entorno de ejecución integrado con Vercel Sandbox que permite a los desarrolladores extender Notion con código personalizado y llamadas a herramientas para agentes.

  • El uso de Vercel Sandbox elimina la infraestructura necesaria para administrar la infraestructura y previene problemas complejos como ataques de denegación de servicio o criptominería maliciosa.

  • El código del usuario se empaqueta en un archivo tarball, se despliega en el almacenamiento blob de Vercel y se ejecuta en sandboxes aislados para garantizar la seguridad.

  • El proceso de extracción de herramientas ejecuta un comando Node.js en el sandbox para importar el módulo de usuario, serializar sus metadatos en JSON y registrar el resultado en la salida estándar.

  • La ejecución segura de herramientas requiere la creación de un nuevo espacio aislado para cada invocación, lo que evita que fallos o estados residuales contaminen ejecuciones posteriores.

Timeline

Introducción a Notion Workers y Vercel Sandbox

  • Notion Workers es un SDK y entorno de ejecución para incorporar código personalizado en Notion.
  • La plataforma permite sincronizar datos externos y escribir llamadas a herramientas avanzadas para agentes sin necesidad de administrar infraestructura.
  • La seguridad ante código de usuario no confiable y la compartición equitativa de recursos motivaron el uso de una infraestructura externa.

El orador presenta Notion Workers como una solución para extender las capacidades de Notion mediante código personalizado. Se destacan casos de uso complejos en áreas de TI y seguridad, además de la simplificación para los desarrolladores al no tener que esperar integraciones nativas. Asimismo, se detallan los desafíos inherentes a la ejecución de código arbitrario, como la prevención de criptominería y ataques de denegación de servicio.

Demostración de Notion Workers con un agente de IA

  • Vercel Sandbox resolvió los problemas complejos de infraestructura de manera inmediata desde el inicio del proyecto.
  • Los agentes de chat en streaming pueden invocar herramientas definidas mediante código de usuario personalizado.
  • Un worker avanzado puede ejecutar algoritmos complejos como análisis de geolocalización y rutas de tránsito urbano en la Ciudad de Nueva York.

Se muestra una demostración práctica de un agente conectado a un worker para planificar salidas urbanas basadas en restricciones de tiempo y distancia. El sistema procesa solicitudes complejas mediante algoritmos avanzados que superan las limitaciones de los servidores Model Context Protocol tradicionales.

Arquitectura y despliegue con tarballs

  • Cada worker expone metadatos esenciales como nombre de la herramienta, descripción, esquema de entrada y una función de ejecución.
  • El código fuente compilado y sus dependencias se empaquetan en un archivo tarball y se almacenan en Vercel Blob.
  • El servicio de sandbox extrae automáticamente el tarball en la raíz del espacio de trabajo tras su creación.

Se detalla la estructura modular de un worker en TypeScript utilizando Zod para definir los esquemas de entrada validados por los agentes. Se explica el flujo de almacenamiento y despliegue utilizando tarballs y URLs prefirmadas que facilitan la inicialización del entorno en el servicio de sandbox.

Extracción y ejecución segura de herramientas

  • El sistema extrae la información de las herramientas ejecutando Node.js en el sandbox y leyendo la salida estándar en formato JSON.
  • Cada invocación de una herramienta utiliza un nuevo espacio aislado para evitar la contaminación de entornos.
  • El SDK de IA valida automáticamente las entradas enviadas por el modelo utilizando esquemas JSON convertidos.

Se examinan los mecanismos técnicos para extraer metadatos del código sin requerir archivos de manifiesto estáticos independientes. Se enfatiza la importancia de eliminar los sandboxes explícitamente tras su uso y de validar estrictamente el tamaño de los registros devueltos para prevenir problemas de memoria en los servidores.

Preguntas y respuestas sobre operaciones y costes

  • Los procesos de Node.js generados en los sandboxes deben configurarse para salir explícitamente y evitar bloqueos por temporizadores colgantes.
  • El uso de workers reduce significativamente los costes operativos en comparación con el consumo masivo de tokens de razonamiento de IA.
  • La plataforma carece actualmente de un mercado público abierto, limitando la distribución a repositorios de GitHub y recursos locales.

Durante la sesión de preguntas se abordan aspectos operativos como la gestión de tiempos de espera, la deduplicación de código repetido por múltiples usuarios y el impacto económico de delegar tareas repetitivas en workers frente al uso exclusivo de modelos de lenguaje.

Community Posts

View all posts