Ship 26 뉴욕 - 워크숍 - 미니 워커스

VVercel
Computing/SoftwareSmall Business/StartupsInternet Technology

Transcript

00:00:00안녕하세요 여러분. 제 이름은 조나단 클레임이고요, 제이 클레임이라고 부르셔도 됩니다. 저는 소프트웨어 엔지니어인데
00:00:11노션에서 개발자 플랫폼 관련 업무를 맡고 있습니다. 특히 최근에 선보인 새로운 제품에
00:00:17대해 집중하고 있는데, 바로 노션 워커스(Notion Workers)라는 제품입니다. 오늘 워크숍에서는
00:00:23노션 워커스가 무엇이고, 우리가 왜 Vercel Sandbox를 이용해 이를 구축했는지
00:00:28간단히 개요를 설명해 드리겠습니다. 그런 다음 워커스의 미니 버전을 직접 살펴보면서
00:00:36Vercel Sandbox를 사용해 이와 같은 제품이 어떻게 만들어지는지 감을 잡으실 수 있도록 하겠습니다.
00:00:43워커스가 무엇인지 잘 모르시는 분들을 위해 설명하자면, 사용자가 커스텀 코드를 작성하고
00:00:50그 코드로 노션을 확장할 수 있는 SDK이자 런타임입니다. 따라서 서드파티 데이터를 노션에 동기화하거나,
00:00:57에이전트를 위한 커스텀 도구 호출 코드를 작성하는 등의 작업을 할 수 있습니다. 사람들이 노션 워커스로
00:01:04노션 워커로 식료품을 주문하거나 스마트 홈을 제어하는 등 재미있고 기발한 일들을 하는 것을 보았습니다. 또한 매우 복잡한 워크플로도 보았는데,
00:01:11특히 IT 및 보안 분야에서 그렇습니다. 워커의 좋은 점은 여러분이 직접 관리해야 할 인프라가
00:01:17없다는 것입니다. 코드를 직접 작성하거나 코딩 에이전트에게 작성하도록 시키기만 하면, 노션이
00:01:23항상 정상적으로 작동하고 이용 가능하며 실행되도록 보장해 줍니다. 이것은 노션 사용자들,
00:01:30특히 개발자들에게 엄청난 혜택이 되었습니다. 이들은 더 이상 노션이 자체적인
00:01:36기본 연동 기능을 만들어 주기를 기다릴 필요가 없습니다. 노션이 지원했으면 좋겠지만 지원하지 않는 기능이라면 무엇이든
00:01:42직접 코드를 작성해 구현할 수 있습니다. 그래서 우리가 처음 노션 워커를 개발하기 시작했을 때 가장 우려했던 점은
00:01:53사용자의 신뢰할 수 없는 코드를 실행하는 플랫폼을 구축해 본 경험이 조금 있거든요.
00:01:59예를 들어 오랫동안 GitHub Actions 개발에 참여했었습니다. 그래서 저는 이런 제품을 위한
00:02:04인프라를 구축하는 것의 어려움과, 특히 안전성에 대해 깊이 우려했습니다. 신경 써야 할 부분이
00:02:09정말 많습니다. 예를 들어, 사용자가 작성한 코드가 당연히 노션 데이터베이스에 접근하지 못하도록 해야 하고,
00:02:14평소에는 접근할 수 없어야 하는 노션 서비스들을 건드리지 못하게 확실히 통제해야 합니다.
00:02:20또한 한 사용자가 다른 사용자에게 영향을 주지 않도록 해야 합니다. 한 사용자의 코드가
00:02:26다른 사용자의 코드나 당연히 그들의 비밀 키(secrets) 같은 것에 접근할 수 있어서는 안 됩니다.
00:02:31이것은 단순히 보안만의 문제가 아닙니다. 공정성과 자원 공유의 문제이기도 합니다. 만약 어떤 사용자가
00:02:37CPU와 메모리를 엄청나게 소모하는 작업을 하고 있다면, 그것이 동시에 다른 작업을 하려는
00:02:42동시에 작업을 수행하려는 다른 사용자들에게 불공정한 영향을 미치지 않도록 해야 합니다.
00:02:48이야기를 하나 하겠습니다. 자원 공유라는 건 특히 정말 까다로운 문제입니다. 제 발표 시간을 좀 갉아먹겠지만,
00:02:54이 이야기를 좋아합니다. 우리가 GitHub Actions를 만들 때도 그랬고(이건 워커스를 만들 때도 생각했던 부분인데요),
00:02:58임의의 코드 실행 플랫폼이 생기자마자, 사람들은 즉시 그 위에서 암호, 코인 채굴을 시도합니다.
00:03:02임의의 코드 실행 플랫폼이 생기자마자, 사람들은 즉시 그 위에서 코인 채굴을 시도합니다.
00:03:07몇 주 전에 어떤 분과 이야기를 나누는데 그분이 그러더군요. “그런 건 어떻게 탐지하나요?
00:03:10CPU 사용률이 100퍼센트로 치솟으면 그냥 바로 알 수 있는 거 아닌가요?”라고요.
00:03:14글쎄요, 꼭 그렇지는 않습니다. 왜냐하면 플랫폼이 성공적으로 자리 잡자마자, 채굴자들은 즉시
00:03:20서로 스크립트를 공유하기 시작하거든요. CPU 명령어 실행 방식을 살짝 수정해서
00:03:26마치 완전히 무해한 활동처럼 보이게 만듭니다. 하지만 시스템에 탐지되지 않으면서도
00:03:31가능한 한 절대적인 최대치의 리소스를 끌어다 씁니다.
00:03:37따라서 이는 엄청나게 어렵고, 이를 제대로 해내려면 수많은 년수와 수많은 보안 및 관측 가능성 계층이 필요합니다.
00:03:42또 다른 걱정거리는 코드 실행 플랫폼이 있을 때 사람들이 이를 이용해 서비스 거부(DoS) 공격을 하거나,
00:03:48코드 실행 플랫폼이 있을 때 사람들이 이를 이용해 서비스 거부 공격을 하거나,
00:03:54봇넷 명령 및 제어 네트워크용으로 악용하는 것에 대해 걱정해야 한다는 점입니다. 우리는 노션 워커스를 시작하자마자
00:04:00이런 모든 문제를 Notion Workers에서 처음부터 전부 신경 쓰고 싶지 않았습니다. 저희가 좋아하는 일에 집중하고 싶었죠.
00:04:07즉, 사용자들이 즐겨 쓰는 제품을 제공하는 것입니다. 그래서 Vercel Sandbox로 구축하기로 결정한 것입니다.
00:04:14Vercel Sandbox는 처음부터 보았듯이 정말 탄탄한 인프라였고,
00:04:18이러한 많은 문제들을 기본적으로 해결해 주었습니다. 그럼 Notion Workers가 무엇인지 아주 간단한 데모로 보여드리겠습니다.
00:04:25여기에 커스텀 에이전트가 있는데, 이 커스텀 에이전트의 임무는 이 워크숍이 저주받았는지 아닌지를 저에게 알려주는 것입니다.
00:04:33저는 '수성 역행(Mercury Retrograde)'이라는 워커를 작성했습니다. 다들 익숙하실지 모르겠지만, 수성이 역행하도록 정렬되면 보통
00:04:40수성이 역행하는 위치로 정렬될 때는 보통 징조가 좋지 않다고 여겨집니다.
00:04:47따라서 이 워커는 수성이 역행 중인지 아닌지 알려주는 기능 외에는 아무것도 하지 않는, 제가 찾은 API를 사용하는 단일 커스텀 도구 호출을 갖고 있습니다.
00:04:54그래서 에이전트에게 “내 워크숍에 저주가 내렸나요?”라고 프롬프트를 입력하면,
00:05:01잠시 생각하다가 인터넷만 잘 작동한다면
00:05:14도구 호출을 수행하는 모습을 보게 될 것입니다. 즉, 제 수성 역행 워커를 사용해
00:05:22그 워커가 제공하는 단일 도구를 호출하고, 수성이 역행 중인지 확인한 뒤에
00:05:27에이전트가 우리에게 응답을 줄 것입니다. 이걸 GPT-54 나노 같은 모델에 연결해 둔 것 같은데, 그래서 보통은 훨씬 더 빠릅니다.
00:05:37불행히도 이건 인터넷 문제인 것 같네요. 혹시 수성이 역행 중인지 미리 확인해 보신 분이 있다면,
00:05:46실제로 역행 기간이라는 걸 제가 미리 알고 있습니다. 그래서 아마 이런 현상이 일어나나 봅니다. 그냥 넘어가겠습니다.
00:05:53잠시 후에 다시 돌아와서 응답이 결국 왔는지 확인해 보겠습니다. 하지만 이미 답은 알고 있는 것 같네요.
00:05:59좋습니다. 이제 노션 워커가 무엇인지 감을 잡으셨을 겁니다. 이제 사용자 코드를 가져와서,
00:06:05배포하고, 안전하게 실행하며, 에이전트가 해당 코드를 호출해 도구를 쓸 수 있도록 에이전트에 그 코드를 알려주는
00:06:13기본적인 작업들을 수행하는 제가 작성한 작은 스크립트를 보여드리겠습니다.
00:06:20이게 완료되었나요? 아, 네, 여기 있네요. 아, 아마도...
00:06:28수성 역행 API를 내려버렸나 봅니다. API가 응답하지 않는 것 같네요.
00:06:35어쨌든 나중에 어딘가에 티켓을 열어야겠군요. 좋습니다. 기본적인 스크립트가 있습니다. 여러분이 완전히 따라서
00:06:40코드를 작성하거나 할 거라 기대하지는 않습니다. 건너뛰면서 이런 제품을 만들 때 해결해야 하는
00:06:44몇 가지 문제들에 대한 아이디어를 드리는 방식으로 진행하겠습니다. 하지만 원하신다면 리포지토리가 있습니다.
00:06:49make notion slash for cell ship 2026 workers에
00:06:55제가 여기서 실행할 모든 코드가 들어 있습니다.
00:07:01좋습니다. 그럼 다른 슬라이드들을 열어보겠습니다.
00:07:08자, 무엇을 만들어 볼까요? 스트리밍 채팅 에이전트가 있고, 우리가 신뢰할 수 없거나 코딩 내용을 알 수 없는
00:07:16내용을 알 수 없는 커스텀 사용자 코드로 정의된 도구들을 호출할 수 있도록 허용할 것입니다.
00:07:22그리고 이를 Vercel sandbox와 Vercel의 blob 스토리지 서비스를 이용해 구축할 것입니다.
00:07:26그럼 여기서 간단한 데모를 실행해 보겠습니다. 실제로 저주받지 않았기를 바랍니다.
00:07:31기본적으로 메시지 하나만 전달하면 됩니다. “안녕”이라고 말해볼 거고, 응답이 돌아오기를 바랍니다.
00:07:36백그라운드에서 몇 가지 배포 작업을 수행하고 있기 때문에 조금 느릴 수 있습니다.
00:07:40이번에는 도구를 호출했네요. `say hello`라는 도구를 호출했고, 저에게 인사를 건네기로 결정했습니다.
00:07:47이번에는 일반적인 메시지를 보내서 1 더하기 1이 뭔지 물어보겠습니다. 그러면 일반적인 응답이
00:07:52스트리밍되는 것을 볼 수 있습니다.
00:07:58Vercel AI SDK를 사용해 보셨다면 이건 그냥 도구 에이전트 루프를 사용하는 것입니다. 좋습니다. 스트리밍 응답을 받았네요.
00:08:04훨씬 더 복잡한 워커도 호출할 수 있습니다. 보여드릴 다른 워커가 또 하나 있습니다.
00:08:14이 워커에서는 에이전트에게 제가 이 건물의 주소에 있다고 말해줍니다.
00:08:17저에게는 90분이 있고, 역사 유적지를 보고 싶으며, 15분 이상 걷고 싶지 않다고 알려줍니다.
00:08:24이것은 왜 이것들이 때때로 MCP 서버보다 유리한지를 보여줍니다. MCP 서버를 사용하면
00:08:29호출할 수 있는 여러 가지 이질적인 도구들이 있습니다. 복잡한 무언가를 하려고 할 때
00:08:33그 단계들을 에이전트에게 설명해 주어야 하고, 에이전트는 한 단계를 수행하고, 사고 토큰을 좀 쓰고, 또 한 단계를 수행하는 식입니다.
00:08:38따라서 이 방식은 매우 크고 복잡한 일종의 검색 알고리즘을 가진 워커를 호출하게 됩니다.
00:08:45이 알고리즘은 뉴욕시의 지리적 위치, 대중교통 소요 시간, 경로 계획 API들을 다수 활용합니다.
00:08:52이름은 `plan outing`입니다.
00:08:56그러면 그 워커가 우리가 방문할 수 있는 추천 역사 유적지와 함께 응답할 것입니다.
00:09:00시청 공원 근처에 있는 MIPOW 기념물인 '프리덤 트리 표지판(freedom tree marker)'을 찾은 것 같네요.
00:09:07좋습니다. 이것이 어떻게 작동하는지 살펴보겠습니다. 자, 워커란 무엇일까요?
00:09:14이 경우 파일 하나에 정의된 사용자 코드입니다. 보통은 여러분의 사용자에 의해 정의되거나
00:09:20GitHub 어딘가에 정의되어 CLI로 배포되겠지만, 여기서는 리포지토리에 체크인된 몇 가지 예제만 가지고 있습니다.
00:09:26그리고 이 각각의 워커들은 도구 이름, 에이전트가 올바른 도구를 찾아갈 수 있도록 돕는 도구 설명,
00:09:34에이전트가 해당 도구를 호출할 때마다 제공해야 하는 입력값이 무엇인지 알 수 있게 하는 입력 스키마를 노출해야 합니다.
00:09:38그리고 에이전트가 이 도구를 호출했을 때 실제로 실행될 코드가 무엇인지 정의하는 실행 함수를 노출해야 합니다.
00:09:44예시를 살펴보겠습니다. 아까 보셨던 그리터(greeter) 도구입니다. 아주 간단합니다.
00:09:51이 워커는 `index.ts` 파일에 있는 하나의 자바스크립트 모듈입니다. `say hello`라는 단일 워커를 내보냅니다.
00:09:59say hello라는 워커가 있습니다. 이 빌드 프로세스가 어떻게 작동하는지 보실 텐데요. 이 예시에서는,
00:10:04모든 모듈 내보내기 키가 도구 이름에 매핑됩니다. 따라서 이 도구의 이름은
00:10:09그리고 간단한 설명과 입력 스키마를 가집니다. 이 경우 스키마를 정의하기 위해 Zod를 사용하고 있습니다.
00:10:16그런 다음 이를 JSON 스키마로 변환합니다. 그것이 바로 이 에이전트들이 기대하는 형식입니다.
00:10:22그리고 간단한 실행 함수가 있습니다. 하지만 훨씬 더 복잡한 것들도 만들 수 있습니다. 예를 들어
00:10:27`plan outing` 워크플로를 열어보면, 이 도구가 어떻게 작동하는지, 언제 호출해야 하는지,
00:10:34무엇을 하는지 에이전트에게 알려주는 훨씬 더 긴 설명이 포함되어 있습니다. 그리고 이것을 위한 스크립트는 훨씬 더 길고 복잡합니다.
00:10:39이것을 전부 다 살펴보지는 않겠고, 일부 작은 부분만 읽어보았지만 아주 잘 작동합니다.
00:10:45이것이 우리가 지금 살고 있는 세상입니다. 자, 이제 이 워커들이 빌드되어 Vercel blob 스토리지에 배포될 것입니다.
00:10:54그다음 어떤 방식으로든 워커의 내용에 대해 학습한 뒤,
00:10:59에이전트에게 그것들을 노출할 것입니다. 그리고 그 도구들은 샌드박스 안에서 안전하게 실행될 것입니다.
00:11:03빌드 부분은 살짝 넘어갈게요. 노션 워커의 경우, 클라우드 기반의 배포 및 빌드 프로세스를 가지고 있습니다.
00:11:09여기서는 디스크상에 미리 이 워커들을 빌드해 두었습니다. 따라서 각 워커에는
00:11:15각 워커에는 컴파일된 TypeScript 코드 종속성 및 기타 항목이 포함된 tarball이 있습니다.
00:11:22아주 흥미로운 부분은 아니니 그냥 넘어가겠습니다.
00:11:26오늘의 핵심 질문은, 우리가 본 적 없고 신뢰할 수 없는 사용자 코드를 어떻게
00:11:34에이전트가 노출하고 안전하게 실행할 수 있는 도구로 전환하느냐 하는 것입니다. 여기엔 두 가지 측면이 있습니다.
00:11:39첫 번째 부분은, 예를 들어 사용자 코드가 블롭 스토리지에 있을 때
00:11:43그 코드의 내용을 어떻게 파악하느냐 하는 것입니다. 에이전트가 도구를 호출하기 전에
00:11:48도구의 이름이 무엇이고, 입력 스키마는 무엇이며, 도구에 대한 설명은 무엇인지 알려주어야 하기 때문입니다.
00:11:53그리고 두 번째 질문은, 에이전트가 해당 도구를 호출하기로 결정했을 때
00:11:58어떻게 실제로 안전하게 실행할 것인가 하는 점입니다. Notion Workers SDK에서
00:12:04구현한 방식 중 제가 정말 좋아하는 점이자 여기서 해낸 점은 코드가 스스로를 설명한다는 것입니다. 우리가 원치 않았던 점은
00:12:11Notion Workers를 설계할 때 사용자가 도구를 정의하는 TypeScript 코드를 작성한 다음
00:12:16정적 매니페스트 파일을 따로 만들어야 하고 기본적으로 똑같은 내용을 다시 작성해야 해”라고 말하게 만드는 것이었습니다.
00:12:21우리는 사용자가 스크립트를 실행하고, 분석을 거치고, 디스크에 컴파일한 다음 배포해야 하는 어색한 로컬 빌드 프로세스를 원하지 않았습니다.
00:12:27우리는 사용자가 스크립트를 실행하고, 분석을 거치고, 디스크에 컴파일한 다음 배포해야 하는 어색한 로컬 빌드 프로세스를 원하지 않았습니다.
00:12:34도구를 그냥 작성하기만 하면 거기서 정보를 뽑아내는 어려운 문제는 우리가 해결해야 할 몫인,
00:12:40최적의 개발자 경험처럼 느껴지는 방식으로 시작하고 싶었습니다.
00:12:46이것은 아주 간단한 다이어그램이지만, 코드로 들어가기 전에 이것을 어떻게 해결할 것인지
00:12:52설명해 드리겠습니다. 블롭 스토리지에 사용자의 컴파일된 코드가 있습니다.
00:12:59우리는 그 코드를 사용해 샌드박스를 만들 것입니다. 샌드박스 안에서 명령어를 실행할 때, 그 사용자의
00:13:04코드는 작업 공간 루트(workspace root)에 위치하게 됩니다. 사용자가 작성한 `index.js` 파일을 가져올 것입니다.
00:13:11그렇게 하면 모든 내보내기 항목의 이름, 설명, 입력 스키마들을 얻을 수 있습니다.
00:13:19여기서 약간 이상해지지만 아주 잘 작동하는 부분이 나옵니다. 해당 모듈에 대해
00:13:23`json.stringify`를 호출하고 표준 출력(standard out)으로 로그를 남길 것입니다. 이 모든 과정이
00:13:29샌드박스 내에서 일어납니다. 그런 다음 배포 스크립트가 그 표준 출력을 받아와 파싱한 뒤,
00:13:34“좋아, 이제 이 워커에 있는 모든 도구의 이름과 입력값, 설명을 알게 되었어”라고 판단합니다. 이 경우
00:13:40우리는 그것을 에이전트에 직접 전달합니다. 하지만 노션 워커의 경우 예를 들어,
00:13:44그것이 배포 파이프라인의 일부가 됩니다. 그래서 모든 정보를 가져와 데이터베이스에 저장하므로
00:13:49커스텀 에이전트를 실행할 때마다 데이터베이스에서 해당 도구 설명들을 가져오게 됩니다.
00:13:53작동 방식이 대충 이해되시나요? 좋습니다. 그럼 배포 스크립트를 살펴보겠습니다.
00:14:02첫 번째 커밋으로 돌아가야겠네요. 이 스크립트에는 배포 기능과 에이전트 호출 기능이 모두 내장되어 있습니다. 아주 간단합니다.
00:14:12여기서는 디렉토리와 워커들을 순회하기만 합니다. 각 하위 디렉토리에는 아까 보셨던 것 같은 워커 코드가 들어 있습니다.
00:14:18우리의 임무는 각각의 워커에서 모든 도구 정보를 추출해 내고
00:14:23이 tools 객체를 채울 방법을 알아내는 것입니다. 그것이 도구 루프 에이전트(tool loop agent)에 전달됩니다.
00:14:29이것은 Vercel이 만든 AI SDK의 일부일 뿐입니다. 그런 다음 사용자의 메시지를 그 에이전트에 보내고,
00:14:35출력을 스트리밍한 뒤, 스크립트에서 표준 출력으로 기록합니다. 따라서 우리가 가장 먼저 파악해야 할 것은
00:14:42소스 코드를 어떻게 업로드할 것인가 하는 점입니다. 그 부분은 꽤 쉽고 빠릅니다.
00:14:48라이브 코딩을 하는 대신, 타이핑하는 모습을 보시지 않도록 이 조각들 사이를 건너뛰겠습니다.
00:14:53코딩은 할 줄 안다고 장담합니다. 다만 아무도 그걸 지켜보고 싶어 하지 않을 것 같아서요.
00:14:59조금 앞으로 건너뛰어, 살펴보게 될 `upload source`라는 함수가 있습니다.
00:15:06보시다시피 Vercel blob SDK를 임포트했습니다. 여기서 이 부분은 꽤 간단합니다.
00:15:13이 `upload source` 함수를 보면, 기본적으로 이 `put` 함수를 호출하고 있습니다.
00:15:20그리고 사용자의 번들을 `worker name / bundle.tar.gzip` 경로 아래에 저장하겠다고 명시하고 있습니다.
00:15:29디스크에서 파일을 스트리밍하여 블롭 스토리지에 저장할 것입니다. 이 작업이 끝나면,
00:15:35해당 블롭으로부터 샌드박스를 생성할 수 있게 됩니다. 이것으로 업로드 파트는 해결되었습니다.
00:15:43그 다음 해야 할 일은 번들로 묶인 소스 코드를 가져와서
00:15:49그것으로부터 샌드박스를 생성하는 것입니다. 이를 위해 Vercel sandbox SDK를 임포트했습니다.
00:15:56아래쪽에 `create sandbox`라는 또 다른 헬퍼 함수가 있습니다. 이 함수의 몇 가지 부분은 건너뛰겠습니다. 하지만 기본적으로 우리가 하는 일은
00:16:03블롭 스토리지에 해당 객체가 있을 때, 샌드박스 서비스에 전달할 사전 서명된 URL(pre-signed URLs)을 사용하는 것입니다.
00:16:10샌드박스 서비스가 (이건 10분 만료로 설정되어 있는 것 같군요) 블롭 스토리지에서 해당 블롭을 끌어와
00:16:16샌드박스를 채우는 데 사용할 수 있도록 말이죠. 이것이 Vercel sandbox에서 제가 정말 마음에 드는 기능 중 하나입니다.
00:16:23이런 tarball과 잘 작동한다는 점입니다. 사용자 코드의 tarball을 빌드하기 쉽고,
00:16:28tarball로부터 샌드박스를 만들고 싶어 하는 샌드박스 서비스는 스토리지나 제공된 URL에서 그것을 가져와
00:16:35작업 공간 루트에 자동으로 그 tarball을 추출합니다.
00:16:40따라서 모든 파일이 바로 생성되어 작업할 준비가 됩니다. 이것은 단순히 `sandbox.create`를 호출하여 완료됩니다.
00:16:46아직 엄청나게 복잡한 부분에 도달하지는 않았지만, 곧 다다를 것입니다.
00:16:52또 한 가지는, 이 예제에서는 간결함을 위해 실행할 때마다 새로운 샌드박스를 매번 생성하고 있다는 점입니다.
00:16:57실제 상황에서는 도구가 호출될 때마다 매번 샌드박스를 재배포하거나 Vercel blob 스토리지
00:17:02또는 사용 중인 다른 클라우드 블롭 스토리지로부터 스트리밍해 오지 않도록 스냅샷 기능을 사용해야 합니다.
00:17:09플랫폼에 기본적으로 구축된 캐싱 메커니즘이 존재합니다. 단순함을 위해 여기서는 건너뛰었습니다.
00:17:15플랫폼에 기본적으로 구축된 캐싱 메커니즘이 존재합니다. 단순함을 위해 여기서는 건너뛰었습니다.
00:17:19이것으로 샌드박스 생성이 끝났고, 이제 흥미로워지는 부분입니다.
00:17:23다음으로 우리가 해야 할 일은 우리가 방금 생성한 샌드박스에서
00:17:31해당 워커에 정의된 도구들에 대한 정보를 추출하는 것입니다.
00:17:36여기서 아주 잠깐 멈추고, 제가 중간중간 흩어 놓은 몇 가지 팁을 말씀드리겠습니다.
00:17:40이와 같은 프로덕션 수준의 서비스를 구축하는 경우, 일반적으로 샌드박스를 중지하기 위해 최선의 노력을 다해야 합니다.
00:17:44따라서 `extract tools`가 무엇을 하는지 보시겠지만, 작업이 끝나면 샌드박스를 명시적으로 삭제하도록 합니다.
00:17:51따라서 `extract tools`가 무엇을 하는지 보시겠지만, 작업이 끝나면 샌드박스를 명시적으로 삭제하도록 합니다.
00:17:56실제 환경에서는 비동기 정리 작업을 큐에 넣는 등의 조치도 취해야 합니다.
00:18:00이러한 샌드박스들이 무기한 방치되도록 내버려 두어선 안 됩니다.
00:18:06자, `extract tools` 함수가 무엇을 하는지 살펴보겠습니다. 우리에게는 샌드박스가 있고, 저는 이 예제들이 정말 좋습니다.
00:18:15얼마나 단순한지 좀 우스꽝스러워 보이지만, 정말 너무나도 잘 작동하거든요.
00:18:23샌드박스 위에서 노드(node) 바이어너리를 사용해 노드 명령어를 실행하고, 스크립트 내부에서
00:18:30사용자가 작성한 모듈(`index.js`)을 임포트합니다. 해당 객체를 문자열화(stringify)한 뒤
00:18:36`console.log`를 호출합니다. 명심해야 할 점은 샌드박스는 요청을 보내고 응답을 받아올 수 있는
00:18:42웹 서버 같은 것이 아니라는 것입니다. 모든 입력과 출력은 명령어를 실행하는 방식으로 이루어집니다.
00:18:48그런 다음 수신하는 서비스에 응답을 보내게 하거나, 이 경우처럼 단순히
00:18:52표준 출력으로 로그를 남기게 할 수 있습니다. 몇 가지 팁을 드리자면, 보통은 그냥 `console.log`를 사용하고 샌드박스로부터 얻는
00:19:00모든 출력을 그대로 신뢰해서는 안 됩니다. 사용자가 설치한 다른 패키지들이 있을 수 있고,
00:19:07표준 출력 스트림에 동시에 다른 로그를 출력하는 실행 중인 다른 코드가 있을 수도 있습니다.
00:19:12따라서 파싱할 수 있는 어떤 종류의 태그로 출력을 감싸는 것이 좋습니다.
00:19:18이것은 여기서 우리가 하게 될 XML 유사 태그 같은 것입니다. 이 예제에서는 그냥 생략하고 있습니다.
00:19:23또한 소비하는 로그의 크기를 제한해야 합니다. 이 예제에서는 모든 출력을
00:19:29하나의 스트림으로 수집하고 있습니다. 프로덕션 수준의 애플리케이션에서는 그렇게 하면 안 됩니다.
00:19:34스트림 데이터가 1기가바이트에 달해 서버가 OOM(메모리 부족)에 빠질 수 있기 때문입니다.
00:19:39따라서 샌드박스 API에는 로그를 스트리밍하기 위한 명령어들도 존재합니다. 그것이 바로
00:19:45노션 워커에서 하는 방식입니다. 우리가 관심을 가지는 출력 토큰의 시작 지점을 볼 때까지 스트림을 소비합니다.
00:19:53그다음에 우리가 의도적으로 로그에 남긴 설명이나 내용들을 버퍼링하기 시작합니다.
00:19:58그리고 닫는 태그에 도달하자마자 출력 스트림 처리를 중단합니다. 이렇게 하면 샌드박스에서 무언가
00:20:04이상하고 잘못되어 엄청난 양의 데이터가 로깅되더라도
00:20:08그것들이 전부 메모리에 쌓이지 않습니다. 그냥 무시하고 우리가 신경 쓰는 데이터가 들어올 때까지 기다리면 됩니다.
00:20:14따라서 해당 명령어가 실행되도록 둔 후, 완료되기를 기다려 표준 출력을 가져옵니다.
00:20:22Zod 같은 라이브러리를 써보셨다면 꽤 익숙하실 겁니다. 문자열을 JSON으로 파싱하고 있죠.
00:20:27그리고 여기에 Zod 타입이 있어서
00:20:31원하는 형태의 데이터인지 검증합니다.
00:20:34이 데이터는 신뢰할 수 없는 사용자 코기 때문에 절대 그대로 믿으면 안 됩니다. 어떤 내용을
00:20:39로그로 남길지 알 수 없기 때문이죠. 따라서 반드시
00:20:43크기를 확인하고 전반적인 데이터 형태를 검증해야 합니다. 이 경우에 우리가 예상하는 것은
00:20:50문자열 키가 각 도구의 이름이 되는 레코드 형태입니다.
00:20:57그리고 객체는 설명과 입력 스키마를 담게 되는데, 바로 그
00:21:02JSON 스키마입니다. execute 함수는 여기에 없다는 걸 눈치채셨을 겁니다.
00:21:06애초에 반환되지 않기 때문이죠. 다행히도 JSON.stringify는 직렬화할 수 없는 항목을
00:21:11그냥 건너뜁니다. 그래서 무시되는 셈이죠. 결국 설명과 입력 스키마만 가져오게 됩니다.
00:21:18다시 위로 올라가서, 워커 도구들이 있습니다.
00:21:23해당 워커에서 노출된 모든 도구의 레코드인 셈이죠.
00:21:28다음으로 할 일은 해당 데이터를 AI SDK가 기대하는 형태로 맞추는 것입니다.
00:21:36그리고 각각의 도구에 실행 함수를 연결해야 합니다.
00:21:42표준 출력에 로그를 남길 때에는 실행 함수를 당연히 가져오지 않았으니까요.
00:21:46그럼 이제 이 도구에 대한 설명이 주어졌을 때, 어떻게 함수를 제공할 수 있을까요?
00:21:51SDK가 이 워커나 이 워커가 노출하는 도구를 호출하고 싶을 때마다 호출할 수 있는 함수 말입니다.
00:21:58그래서 execute tool이라는 작은 래퍼를 만들었습니다. 어떤 일을 하는지 살펴볼까요?
00:22:04이제 꽤 익숙해 보일 겁니다. 앞서 본 것과 동일한 create sandbox 함수를 호출하니까요.
00:22:10새로운 샌드박스를 만드는 거죠. 캐싱을 사용하더라도 Vercel 샌드박스에는
00:22:16영속성(persistence)이라는 기능이 있어서, 샌드박스가 일시 중지되거나 멈출 때마다 디스크에 있는 모든 상태를 캐시합니다.
00:22:23하지만 이 같은 기능에서는 실제로 그럴 필요가 없습니다. 모든 사용자 코드가 포함된 초기 상태를 스냅샷으로 찍고 싶겠지만,
00:22:28일반적으로는 그 이후에 도구가 실행될 때마다 매번 완전히 새로운 인스턴스를 보장하고 싶을 겁니다.
00:22:33그래야 한 번의 도구 실행에서 무언가 잘못되더라도, 다음에 실행되는 도구의 환경을 오염시키지 않으니까요.
00:22:38그래서 새로운 샌드박스를 만들고, 그 위에 다른 노드 스크립트를 실행합니다.
00:22:43이것도 꽤 익숙해 보이겠지만 약간의 차이가 있습니다. 사용자가 작성한 모듈을 가져오고 있죠.
00:22:49해당 모듈에서 도구를 가져오는데, 이는 execute tool 래퍼를 만들 때 얻었던 도구 이름입니다.
00:22:56거기에 있는 execute 함수를 호출하고요.
00:23:03그 execute 함수에 모델이 제공한 입력을 전달합니다.
00:23:09여기서는 타입을 unknown으로 지정하고 있습니다. 하지만 여기서는 꽤 안전한 편입니다.
00:23:19왜냐하면 우리가 제공했기 때문이죠. 여기서 안 했었나요? 이 예제에서는 건너뛰었을지도 모르겠네요. 하지만 평소에
00:23:27하는 일은... 아, 아니요, 한 것 같네요. 다시 위로 올라가 보겠습니다. 네. 도구를 조작해서
00:23:34에이전트에 보낼 때, JSON 스키마를 가져와서 다시
00:23:39Zod 타입으로 변환하고 있습니다. 그래서 우리가 직접 파싱할 필요가 없습니다. AI SDK와 도구 루프 에이전트 코드가
00:23:46해당 도구가 호출될 때마다 에이전트로부터 온 입력을 알아서 검증해 주니까요.
00:23:52따라서 이 값이 우리가 예상한 바와 같다고 어느 정도 신뢰할 수 있습니다. 그래서 이를
00:23:56여기 있는 execute 함수에 전달할 것입니다. 그런 다음 비동기 함수가 완료되면
00:24:03문자열화(stringify)하고, 표준 출력에 로그로 남깁니다. 그리고 나서—잠시 후에 이 부분에 대해 다루겠습니다.
00:24:10명령어가 완료되기를 기다립니다. 그리고 다시 샌드박스를
00:24:14삭제하고 마무리합니다. 그런 다음 사용자가 작성한 실행 함수의 반환값인 JSON을 파싱하고,
00:24:21이를 도구 루프 에이전트에 다시 보냅니다.
00:24:26결과적으로 흐름은 이렇습니다. 도구 루프 에이전트가 앞서 보았던 대중교통 API를 사용하는 일정 계획 도구를 호출하고 싶다고 말합니다.
00:24:35그러면 에이전트가 결정한 입력값들과 함께 이 함수가 호출됩니다.
00:24:40우리는 사용자의 컴파일된 코드에서 이전에 업로드한 블롭을 사용해 샌드박스를 생성합니다.
00:24:48그런 다음 사용자의 execute 함수를 호출하는 명령어를 해당 샌드박스에서 실행합니다. 함수가 반환될 때까지 기다린 후,
00:24:55그 반환값을 표준 출력에 기록하고, 파싱한 뒤 에이전트에게 다시 보냅니다.
00:25:00그러면 에이전트는 도구 루프를 계속 진행하여 추가 도구를 호출하거나 사용자에게 응답하게 됩니다.
00:25:05몇 가지 빠른 팁을 드리자면, 프로덕션 시스템에서도 동일한 출력 검증 규칙이 적용됩니다. 사용자가
00:25:13파싱하는 데 2GB나 걸리는 객체를 반환하지 못하도록 막아야 합니다. 또한
00:25:19아마 SDK를 제공하는 편이 좋을 것입니다. 이 예제에서는 다루지 않지만, Notion Workers SDK에서는
00:25:28해당 실행 함수의 반환값이 반드시 JSON 직렬화 가능하도록 타입이 설정되어 있습니다.
00:25:35이것은 사용자가 흔히 저지르기 쉬운 실수인데, 함수가 어떤 값이든 반환할 수 있게 만들면
00:25:41JSON으로 직렬화할 수 없는 값을 반환하게 되기 쉽습니다.
00:25:47그 상태로 표준 출력을 통해 전송되어 파싱되려고 하면
00:25:52사용자는 매우 혼란스러워하며 워커가 왜 작동하지 않는지 이해하지 못할 것입니다.
00:25:57그리고 이건 Notion Workers와 관련해서 발생했던 일화인데, 여기서 생성하는 노드 프로세스가
00:26:05무조건 종료되도록 항상 각별히 주의해야 합니다. Notion에서 버그가 있었는데, 사용자 코드가 실행되어 완료된 것처럼 보이다가도
00:26:11어떤 이유에서인지 이 노드 프로세스가 종료되지 않는 문제였습니다.
00:26:18샌드박스 수명이 다할 때까지, 대략 5분 정도 동안 그냥 멈춰 있었습니다. 치명적이진 않았지만
00:26:25자원을 낭비하고 있었죠. 그리고 알게 되었거나 기억해 낸 사실은—이건 노드 런타임의 특성이기도 한데—
00:26:30사용자들이 set interval이나 set timeout 같은 타이머를 설정하는 코드를 실행하고 있었다는 점입니다.
00:26:38특히 인터벌(interval)이나 대기 중인 프로미스(dangling promise) 같은 것들이 있으면 프로세스가 종료되지 않습니다.
00:26:44setInterval이나 setTimeout 같은 타이머를 설정하는 코드를 실행하고 있었습니다. 특히 인터벌이나, 제 생각엔 대기 중인 프로미스도 같은 문제를 일으킬 겁니다.
00:26:52스크립트가 실행될 때 인터벌이나 그와 유사한 무언가가 계속 실행 중이라면, Node 프로세스는
00:26:58절대 종료되지 않습니다. 그 타이머들이 전부 끝날 때까지 혼자서는 종료되지 않죠. 만약 이게
00:27:03인터벌이라면 샌드박스가 종료될 때까지 영원히 실행될 겁니다. 따라서 여러분이 항상 확인해야 할 것은
00:27:08실행하려는 코드나 함수가 실제로 완료되었을 때, 프로세스에 명시적으로 종료하라고 지시하는 것입니다.
00:27:14그래야 샌드박스가 멈추고, 그후에 프로세스가 반환됩니다.
00:27:23자, 대략 전체 워크플로는 이렇습니다. 이걸 한 번 더 실행해 볼 건데, 이번에는
00:27:29디버깅을 켜서 어떤 일이 일어나는지 직접 보실 수 있게 하겠습니다.
00:27:42먼저 departures 워커를 배포합니다. 이건 잠시 후에 자세히 살펴볼게요. 먼저 번들을 업로드하여
00:27:48departures 워커를 배포하죠. 그리고 그 워커에 대한 정보를 추출하는 데 사용할 샌드박스를 만듭니다.
00:27:54이 도구들을 추출했습니다. 모듈의 내용을 표준 출력으로
00:28:01로그에 남기면 이런 결과가 나옵니다. 각 도구의 키, 설명, 입력 스키마를 얻게 되죠.
00:28:08간단한 greeter 워커에 대해서도 똑같이 작업합니다. 그런 다음 이 모든 것을 가져와서 에이전트에 전달하므로
00:28:13에이전트가 툴 호출을 하고 사용자에게 응답할 수 있습니다. 대략 이런 식이죠. 이것이 바로 신뢰할 수 없는 사용자 코드를 가져와서
00:28:19어딘가에 저장하고, 안전한 방식으로 분석하여 영구 스토리지에 넣거나 에이전트에게 직접 보낸 다음,
00:28:26에이전트가 해당 코드를 안전하게 실행할 수 있도록 만드는 아주 간단한 예시입니다. 자, 시간이 10분 정도 남았네요.
00:28:31혹시 질문 있으신 분 계신가요? Notion 워커나 Vercel 샌드박스 활용법 전반에 대해,
00:28:39또는 신뢰할 수 없는 코드 실행에 대해 질문하고 싶으시다면
00:28:46기꺼이 답변해 드리겠습니다. 감사합니다.
00:28:56아 맞다. 그리고 나중에 Notion 개발자 플랫폼 전반에 대해 이야기 나누고 싶으시다면
00:29:01저나 저기 있는 제 동료 MJ에게 말씀해 주세요. Notion 개발자 플랫폼의 프로덕트 매니저입니다.
00:29:08안녕하세요. 운영 관련 질문인데요, 사용자가 무엇이든 할 수 있으니까, 이걸 어떻게 제한하시나요?
00:29:15네. 여러 사용자가 비슷한 도구를 각각 작성할 수도 있잖아요.
00:29:20여러 사용자가 뭐라고요? 비슷한 도구를 작성하는 거요. 예를 들어 뉴욕 여행 일정을 짜는 것 같은 거요.
00:29:24네. 10명의 서로 다른 사용자가 각각 다른 10개의 코드를 가질 수 있죠. 네. 똑같은 일을 하는 코드를요.
00:29:29네. 이걸 차단하기 위해 따로 조치를 취하고 계신가요, 아니면 그냥 에이전트에게 맡기시나요?
00:29:33아니요. 여러 사용자가 모두 똑같은 작업을 하려고 한다면, 그냥 그렇게 하도록 둡니다.
00:29:37부분적으로는 제품 관련 질문이네요. 예를 들어 동일한 조직 내의 여러 사용자가 있다면,
00:29:42운영적인 측면이기도 하고 제품적인 측면이기도 한 그런 부분을 확실히 하고 싶을 겁니다.
00:29:46좋은 공유 기본 요소 같은 기능들을 확실히 갖추고 싶어 하죠. 그래서 제가
00:29:50이 작업을 수행하는 워커가 이미 있는지 살펴보고 검색할 수 있도록 말입니다. 그렇게 하면
00:29:55코드를 다시 작성하지 않아도 됩니다. 하지만 플랫폼 전반적인 관점에서는, 사용자들이 동일한 코드를
00:30:02배포할 가능성은 매우 낮기 때문에 이를 굳이 중복 제거하려고 애쓰는 것은 가치가 없습니다.
00:30:19좋습니다. 질문이 몇 개 더 있는 것 같은데, 마이크를 누가 가지고 계신지 모르겠네요.
00:30:24못 들었어요. 아, 아, 네. 정말 죄송합니다.
00:30:29헤드폰으로 들어오는 줄 알았어요.
00:30:33아, 네. 하신 질문은 만약 수많은 사용자가 동일한 코드를 배포한다면
00:30:40이를 운영 관리에 반영하기 위해 무언가를 하느냐는 것이었는데, 따로 하지 않습니다. 제품 관련 질문에 더 가깝죠.
00:30:45사용자들이 같은 작업을 반복하지 않기를 바라기 때문에, 현재 노션 워커를 위해
00:30:50개발 중인 좋은 공유 기본 요소들을 제공하고자 합니다. 하지만 플랫폼 수준의 운영 관점에서는
00:30:54사람들이 동일한 코드를 50번 배포하더라도 신경 쓰지 않습니다.
00:30:56샌드박스 환경에서 워커가 작업을 수행하다가 너무 일찍 종료되어
00:31:04타임아웃 문제가 발생하는 경우는 없나요?
00:31:07타임아웃에 대한 질문이 정확히 어떤 내용이었죠?
00:31:10Vercel이나 다른 제품 및 워크플로우, 예를 들어 함수들과 샌드박스 간의
00:31:15타임아웃으로 인해 겪는 문제가 있으신가요? 아니면 그냥 모든 게 원활한가요?
00:31:20네, 그와 관련해서는 아무런 문제가 없었습니다. 저희에게는 플랫폼이 지금까지
00:31:25정말 견고했습니다. 빈말이 아니라 진심으로 아주 좋았습니다.
00:31:32오히려 사용자들이 실수로 잘못된 작업을 수행하는 문제들을 훨씬 더 많이 겪고 있습니다. 그래서 시간이 지날수록
00:31:36시간이 지날수록 그런 잠재적 위험 요소들을 없애고, 개발자와 비개발자 모두
00:31:42플랫폼을 훨씬 더 쉽게 사용할 수 있도록 만드는 게 핵심입니다. 좋습니다, 아주 명쾌하네요. 제가 여쭤보고 싶었던 건,
00:31:48초기 사용자 코드를 문자열화(stringify)할 때인데요. 아마도 코드가 안전한지 확인하거나
00:31:52실행 가능하게 하려는 목적이겠죠. 이 부분에 대해 조금 더 자세히 여쭤보고 싶었습니다.
00:31:57코드를 문자열화하고 워커 태그를 가져와서 사용자 코드에 필요한 입력값을 얻고,
00:32:04해당 도구에 대한 설명을 얻는다고 하셨는데요. 그게 노션, 그러니까 예를 들어서
00:32:10에이전트가 코드를 실행할 수 있게 하기 위한 건가요? 사용자 실행 코드나
00:32:15실행자 함수는 이미 실행하고 계신 걸로 알고 있어서요. 입력값과 도구 자체의 설명을 얻기 위해
00:32:21왜 이런 추가 단계를 거치시는지 궁금했습니다.
00:32:24좋은 질문이네요. 실제 제품에서는 조금 더 명확하지만, 여기서는 간소화되어 있습니다.
00:32:29질문의 요지는 '왜 워커에 대한 정보를 얻기 위해 사용자 코드를 한 번 실행하고,
00:32:34도구 코드가 호출될 때 또다시 실행하느냐'는 것일 텐데요. 그 이유는 에이전트가
00:32:39애초에 도구를 호출하거나 그 도구의 존재를 알기 전에, 우리는 먼저 그 도구의 내용을 파악한 뒤에
00:32:45SDK를 통해 에이전트에 노출해야 하기 때문입니다. 예를 들어 노션 워커에서
00:32:50NTN Workers Deploy 같은 명령을 실행하면, 빌드 파이프라인을 거치게 되는데요.
00:32:55빌드를 실행하는 샌드박스가 tarball(압축 파일)을 가져와 어딘가의 스토리지에 저장합니다. 그리고 나서,
00:33:02새로운 워커 안에서 도구의 이름과 설명, 스키마 등을 추출한 다음
00:33:09DynamoDB 같은 곳에 저장하는 방식으로 처리합니다. 그렇게 하면 그 이후에는
00:33:14실제로 도구가 호출될 때까지는 샌드박스를 다시 실행할 일이 전혀 없게 되죠. 방금 tarball 시스템을 언급하셨는데요.
00:33:21제가 가장 궁금한 부분이 바로 그 점입니다. 배포 관점에서 볼 때, tarball은 정확히 어떤 방식으로 작동하나요?
00:33:26예를 들어 현재 오픈 마켓플레이스 같은 게 따로 있는 건지, 배포는 어떻게 이루어지는지 궁금합니다.
00:33:31아, 실제 tarball 안에는 무엇이 들어있냐고요? 네, 맞습니다.
00:33:33우리가 실제로 구현하는 메커니즘이나 tarball에 포함되는 내용 측면에서 말씀드리면,
00:33:38현재는 ES build를 사용하고 있습니다. 전체적인 실제 배포 파이프라인의 작동 방식을 설명하자면,
00:33:47NTN workers deploy를 실행하면 API 엔드포인트를 호출하고, 컴퓨터가 사전 서명된 URL을 받아
00:33:52모든 소스 코드를 번들링합니다. 그런 다음 소스 코드가 포함된 tarball을 생성하고, 해당 샌드박스에서
00:33:58ES build로 빌드 프로세스를 실행한 뒤, 그 결과물을 다시 블롭 스토리지에 저장하여
00:34:04거기서 실제 작업을 실행할 수 있게 합니다. 아직 워커를 위한 본격적인 마켓플레이스는 없습니다. 먼저 노션 워크스페이스
00:34:11내에서 공유할 수 있는 프리미티브 기능을 개발 중이지만, 향후에는 일종의 워커 마켓플레이스를 만들 계획도 확실히 있습니다.
00:34:18하지만 그 전까지는 GitHub에 배포하는 것만으로도 아주 잘 작동합니다. 요즘 사람들도 그렇게 하고요.
00:34:23네, 그냥 리포지토리를 공개하면 누구나 클론해서 NTN workers deploy를 실행하고 직접 사용할 수 있습니다.
00:34:29네.
00:34:34저기 뒤쪽에 한 분 계신 것 같네요.
00:34:37네, 과금에 관해서 질문이 있습니다.
00:34:40과금이요?
00:34:40네, 방금 살펴보았는데요—물론 모든 비밀을 다 알려주실 필요는 없지만—
00:34:44간단히 구글 검색을 해보니 커스텀 에이전트는 일종의 크레딧 시스템으로 작동하는 것 같더라고요.
00:34:48리소스 소비량과 연동되어 있는 것 같은데요.
00:34:51맞습니다, 맞아요.
00:34:51그게 Vercel 플랫폼과는 전체적으로 어떻게 연동되는지 궁금합니다.
00:34:54좋습니다. 질문은 이것이 Vercel 플랫폼에서 어떻게 과금되느냐는 것이죠.
00:35:02워커의 장점 중 하나는 이런 코드를 작성할 수 있다는 점입니다. 많은 팀들이 이 방식을 도입해
00:35:08에이전트에 제공하던 MCP 서버 기반의 거대한 지시 사항 세트에서 벗어날 수 있게 되었습니다. 에이전트는 작업을 호출할 때마다
00:35:16기본적으로 똑같은 일을 반복하면서 엄청난 양의 사고 토큰을 소비하게 됩니다.
00:35:21따라서 에이전트가 수행하는 이러한 반복 작업을 가져와 워커로 배포할 수 있다면,
00:35:33워커의 실행 시간에 대해서는 여전히 비용이 청구되지만, AI 연산 토큰보다는 비용이 훨씬, 훨씬 적게 듭니다.
00:35:40그러므로 커스텀 에이전트를 실행할 때, 에이전트가 생각을 좀 하다가 툴을 실행하고, 다시 생각을 더 하게 되는데요.
00:35:48툴을 호출하기 전까지 커스텀 에이전트가 수행하는 토큰 소비에 대해서는 비용이 청구됩니다.
00:35:53따라서 툴이 실행될 때는 다른 요율로 크레딧이 차감되며, 노션 AI 크레딧이 훨씬, 훨씬, 훨씬 더 낮은 비율로 소모됩니다.
00:36:01그리고 에이전트가 최종적으로 응답을 반환할 때 일반 AI 크레딧이 다시 청구되는 식이죠.
00:36:05따라서 반복적인 작업이 있는 경우, 노션 커스텀 에이전트를 사용하면 비용을 크게 절감할 수 있는 방법입니다.
00:36:16그것도 맞는 말씀입니다. 꼭 노션 AI를 써야 하는 것도 아닙니다. 여기서는 노션 AI와 연동되는 툴 호출 기능을 시연해 보였지만요.
00:36:24하지만 워커는 노션으로의 서드파티 동기화 같은 작업도 수행할 수 있습니다. AI 기능은 전혀 필요하지 않죠.
00:36:31그러니까 단순히 AI 제품만은 아닙니다. 사람들은 동기화 용도로도 이걸 사용하고 있어요.
00:36:36저 같은 경우는 제 레터박스 피드를 노션에 동기화해 주는 워커를 사용하고 있습니다. 개인적으로 가장 중요한 워커죠.
00:36:46좋습니다. 다른 질문 있으신가요?
00:36:52한 분 더 있으신가요?
00:37:06아, 노션 커스텀 에이전트를 자신의 사용자나 인터페이스를 통해 노출하는 것 말인가요?
00:37:13현재 저희가 지원하는 기능은 아닙니다.
00:37:16죄송합니다. MJ님, 질문은 자신만의 워커와 커스텀 에이전트가 있을 때,
00:37:22이를 자신의 소비자들을 위한 자체 애플리케이션을 통해 노출할 수 있는 방법이 있느냐는 것이었죠.
00:37:28현재로서는 그렇게 할 수 있는 방법은 없습니다. 다만 커스텀 에이전트 API의 알파 버전이 있어서
00:37:35그걸 활용하면 충분히 가능할 수 있습니다. 워크스페이스에 커스텀 에이전트와 워커,
00:37:40혹은 커스텀 에이전트를 정의해 둔다면 API를 이용해 해당 에이전트를 호출하고 스트리밍 응답을 받을 수 있습니다. 그러니 가능은 합니다.
00:37:46아직 많은 사람들이 쓰고 있는 것 같지는 않고요. 여전히 알파 단계이며, 공개되긴 했지만 알파 단계인 기능입니다.
00:37:57다른 질문 있으신가요?
00:38:00알겠습니다. 좋습니다. 오늘 시간을 내어
00:38:04이 자리에 참석해 주셔서 모두 감사드립니다. 그리고 노션, 노션 워커,
00:38:08또는 Vercel Sandbox에 대해 궁금한 점이 있으시면 NJRI를 찾아주세요. 감사합니다.

Key Takeaway

노션 워커스는 Vercel Sandbox와 블롭 스토리지를 결합하여 개발자가 관리할 인프라 없이도 신뢰할 수 없는 사용자 코드를 안전하게 파싱하고 실행할 수 있는 SDK 및 런타임 환경을 제공한다.

Highlights

  • 노션 워커스(Notion Workers)는 사용자가 커스텀 코드를 작성하여 노션을 확장할 수 있는 SDK이자 런타임 환경이다.

  • 노션 워커스는 개발자가 관리해야 할 별도의 인프라가 없으며, Vercel Sandbox를 활용해 신뢰할 수 없는 사용자 코드를 안전하게 실행한다.

  • 사용자가 컴파일된 코드를 Vercel 블롭 스토리지에 tarball 형태로 업로드하면, 서비스가 이를 가져와 작업 공간 루트에 자동 추출한다.

  • 샌드박스 환경에서 노드 명령어와 json.stringify를 통해 사용자 코드의 도구 이름, 설명, Zod 기반 입력 스키마를 동적으로 추출한다.

  • 도구 실행 시 매번 새로운 샌드박스를 생성하여 이전 실행 환경의 오염을 방지하고 보안을 유지한다.

  • Node.js 프로세스 실행 시 타이머나 dangling promise로 인해 프로세스가 종료되지 않는 문제를 방지하기 위해 작업 완료 후 명시적인 종료 처리가 필수적이다.

Timeline

노션 워커스 개요와 Vercel Sandbox 도입 배경

  • 노션 워커스는 서드파티 데이터 동기화와 커스텀 도구 호출을 지원하는 런타임이다.
  • 인프라 관리 부담이 없고 노션이 직접 작동 및 가용성을 보장한다.
  • 임의의 코드 실행 플랫폼에서 발생하는 코인 채굴, DoS 공격, 자원 독점 등의 보안 문제를 방지하기 위해 Vercel Sandbox를 채택했다.

노션 워커스는 사용자가 직접 코드를 작성해 노션을 확장할 수 있는 도구다. 식료품 주문이나 스마트 홈 제어뿐만 아니라 복잡한 IT 및 보안 워크플로에도 활용된다. 과거 GitHub Actions 개발 경험을 통해 임의의 코드 실행 플랫폼이 직면하는 리소스 남용과 보안 위협의 심각성이 고려되었으며, 이러한 복잡한 인프라 문제를 처음부터 직접 구축하는 대신 Vercel Sandbox를 활용해 해결했다.

노션 워커 데모와 작동 방식

  • 수성 역행 확인 및 뉴욕 역사 유적지 일정 계획 등 복잡한 알고리즘을 수행하는 워커를 에이전트 도구로 호출할 수 있다.
  • MCP 서버와 달리 복잡한 다단계 지시 사항 없이 단일 워커 내에서 지리적 위치, 대중교통, 경로 계획 API를 통합 처리한다.
  • 빌드된 워커는 Vercel 블롭 스토리지와 샌드박스를 통해 에이전트에 노출된다.

커스텀 에이전트는 사용자가 작성한 워커를 도구로 인식하여 호출한다. 단순한 인사이더 도구부터 뉴욕 시의 대중교통 및 지리 정보를 활용하는 복잡한 일정 계획 워커까지 구현할 수 있다. 이는 여러 이질적인 도구를 조합해야 하는 MCP 서버 방식에 비해 에이전트의 사고 토큰 소비를 줄이고 복잡한 연산을 효율적으로 처리하는 장점이 있다.

소스 코드 업로드와 샌드박스 생성 프로세스

  • 사용자의 컴파일된 소스 코드는 tarball 형태로 Vercel 블롭 스토리지에 저장된다.
  • 샌드박스 서비스는 사전 서명된 URL을 이용해 블롭 스토리지에서 tarball을 가져와 작업 공간 루트에 자동 추출한다.
  • 개발자는 수동으로 정적 매니페스트 파일을 별도로 작성할 필요 없이 코드가 스스로 정보를 설명하는 구조를 갖는다.

배포 파이프라인은 사용자의 소스 코드를 번들링하여 tarball로 생성하고 블롭 스토리지에 업로드한다. Vercel Sandbox SDK는 이 tarball을 자동으로 추출하여 즉시 작업 가능한 환경을 구성한다. 별도의 매니페스트 파일을 중복 작성하지 않고도 개발자 경험을 최적화하는 방식으로 설계되었다.

도구 정보 추출과 안전한 실행 함수 래퍼 구현

  • 샌드박스 내에서 노드 명령어로 사용자 모듈을 임포트하고 문자열화하여 표준 출력으로 도구 이름, 설명, 입력 스키마를 추출한다.
  • 추출된 JSON 스키마는 Zod 타입으로 변환되어 AI SDK와 도구 루프 에이전트의 입력 검증에 사용된다.
  • 도구가 호출될 때마다 완전히 새로운 샌드박스 인스턴스를 생성하여 환경 오염을 방지한다.

샌드박스 환경에서 노드 스크립트를 실행해 모듈의 내보내기 항목 정보를 표준 출력으로 로깅하고, 이를 파싱하여 에이전트가 인식할 수 있는 도구 메타데이터로 변환한다. 에이전트가 도구 호출을 결정하면 다시 새로운 샌드박스를 생성해 사용자 코드의 execute 함수를 안전하게 실행하고 결과를 반환한다. 프로덕션 환경에서는 로그 크기 제한과 샌드박스 명시적 삭제 처리가 필요하다.

질의응답 및 플랫폼 운영 논의

  • 동일한 코드를 여러 사용자가 배포하는 중복 문제는 플랫폼 차원에서 별도로 차단하지 않으며 공유 기본 요소를 제공하는 방향으로 해결한다.
  • 에이전트가 워커를 호출할 경우 AI 연산 토큰 비용을 크게 절감할 수 있다.
  • Node.js 프로세스 내의 타이머나 dangling promise로 인한 타임아웃을 방지하려면 작업 완료 시 명시적인 프로세스 종료 지시가 필수적이다.

질의응답 세션에서는 다수의 사용자가 유사한 도구를 배포할 때의 운영 방식, Vercel 플랫폼 과금 연동, 그리고 Node.js 런타임 특성상 발생할 수 있는 프로세스 미종료 이슈 등이 다루어졌다. 워커를 활용하면 AI 에이전트의 반복적인 사고 토큰 소비를 줄여 비용 효율성을 높일 수 있으며, 비동기 작업 완료 후 프로세스를 강제 종료하는 예외 처리가 안정적인 운영에 핵심적인 역할을 한다.

Community Posts

View all posts