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.