Hooksを深掘りする | Workflow SDK

VVercel
컴퓨터/소프트웨어창업/스타트업AI/미래기술

Transcript

00:00:00本日はご参加いただき、ありがとうございます。
00:00:02VercelのワークフローチームのPraneetです。
00:00:05同じくワークフローチームのNateです。
00:00:08Nate、僕たちは最初からワークフローチームにいますが、
00:00:12この半年間にリリースした数多くの機能の中でも、
00:00:15フックとウェブフックは特にお気に入りの一つです。
00:00:18今日はまさにその話をしてもらうために来てもらいました。
00:00:21フックとウェブフックは僕も一番好きな機能です。
00:00:23非常に強力な機能なので、いくつかデモをお見せして説明します。
00:00:28最初のデモは、皆さんもよくご存知の「マジックリンク」です。
00:00:33マジックリンクはログインフォームの一種です。メールアドレスを入力して、
00:00:40受信トレイに届いたリンクをクリックするとログインできる仕組みです。
00:00:44そういえば、Vercelという社名になる前、
00:00:48まだZEITと呼ばれていた頃は、マジックリンクが唯一の認証方法でしたね。
00:00:52当時はこのシステム全体を自分たちで構築していました。
00:00:56そうなんです。当時は本当に苦労しました。
00:01:01ワークフローを使わずにこれを実装するのは、見た目以上に複雑だからです。
00:01:08ロジックが複数のファイルに分散してしまい、
00:01:12状態を管理するためにデータベースも必要になり、すぐに収拾がつかなくなります。
00:01:19確かに、僕も以前これを作った時は、構成をどうするか、
00:01:24どのデータベースを使うべきか悩みました。これはよくある課題ですよね。
00:01:28ですので、どう変わるのか見るのが楽しみです。
00:01:30その苦労を具体的に示すために、まずワークフローを使わない
00:01:38「伝統的な」方法でマジックリンクのログインを実装してみました。
00:01:43これには3つのエンドポイントが必要になります。
00:01:471つ目は、ログインフォームが送信された時です。
00:01:50セッションを生成し、Redisなどのデータベースに保存する必要があります。
00:01:57また、TTL(有効期限)も実装し、データを永久に残さず期限切れにする必要があります。
00:02:06そしてメールの送信。これが失敗するとログインできず、ユーザーは不満を感じます。
00:02:14さらに、cronジョブやインターンを使ってデータベースを掃除する必要もありますね。
00:02:19当時は僕がそのインターン役だったかもしれません。
00:02:22次に2つ目のエンドポイント。これはユーザーがメールのリンクをクリックした時のものです。
00:02:28ここでデータベースに問い合わせて、最初のエンドポイントで作った状態を復元します。
00:02:36この時点ですでにスパゲッティコードになり始めています。
00:02:38どんなコードになるか想像していましたが、見覚えのある構造で、僕もこう書くと思います。
00:02:48単純なコンセプトなのに、あっという間に複雑化してしまいますよね。
00:02:54では、これをワークフローで実装するとどうなるか見てみましょう。
00:02:59Workflow SDKを使ったマジックリンクの実装は、このようになります。
00:03:05関数があり、「useWorkflow」ディレクティブが付いています。これがワークフロー関数です。
00:03:11最初に行うのは、Workflowパッケージの「createWebhook」関数の呼び出しです。
00:03:18ここでは「respondWithManual」オプションを使っています。これは、ウェブフックをトリガーしたリクエストへのレスポンスをワークフロー関数側で制御することを意味します。
00:03:36ログイン後にリダイレクトなどを行うためですか?
00:03:40ええ。どのようなレスポンスを返すべきか判断するために、ワークフロー関数内の情報が必要だからです。
00:03:51最初のエンドポイントと同様に、ログインメールを送信します。これは「useStep」関数です。
00:03:57これが失敗しても、Workflow SDKには自動リトライ機能が備わっています。
00:04:03この耐久性の面だけでも、従来のアプローチよりメリットがあります。
00:04:10メール送信がステップになっていて、失敗しても、生成済みのウェブフックURLを使ってリトライできるわけですね。
00:04:21ここで非常に興味深いパターンがあります。
00:04:26「promise.race」を「sleep 5分」と一緒に使っています。
00:04:30ウェブフック・オブジェクトがPromiseを実装しているからこそ、これが可能なんです。
00:04:35ウェブフックのリクエストを待機するには、単にそのオブジェクトをawaitするだけです。
00:04:40今回はraceを使っていますね。タイムアウト機能が関数の引数にあると思っていましたが、
00:04:50ウェブフックとsleepのレースとしてモデル化する方が、ずっとクリーンでいいですね。
00:04:58これならもっと色々なことができそうです。2つのウェブフックを競わせることもできますね。
00:05:02関数の引数だとできることは限られますが、
00:05:06単なるPromiseなので、sleepや他のステップとpromise.raceができる。
00:05:12このパターンは素晴らしいです。何を作ろうかワクワクしてきますね。
00:05:16そう、それがWorkflow SDKが提供するプリミティブの美しさです。
00:05:21すべてがPromiseとして公開されているので、
00:05:23awaitやpromise.raceといった標準的なJavaScriptのパターンがそのまま使えます。
00:05:28もう一つのポイントは、Redisなどのデータベースが一切登場しないことです。
00:05:33従来の例ではタイムアウトの実装にRedisのTTLを使っていましたが、
00:05:41ここではワークフローのsleepプリミティブを使っています。
00:05:44データベースを掃除するインターンも必要ありません。
00:05:50そこが最高ですね。
00:05:51このように、ワークフローはリクエストに対してログイン成功ページへのリダイレクトを返し、
00:05:59その後、ログインを開始したクライアントに返すユーザー情報を取得します。
00:06:07これでワークフロー全体が完了です。マジックリンクの実装はわずか50行ほどです。
00:06:12これは本当にすごい。実際に動いているところを見られますか?
00:06:17こちらがマジックリンクのデモです。メールアドレスを入力します。
00:06:24ワークフローが開始され、メールが送信されました。ウェブフックが待機状態になります。
00:06:31実際、ワークフローは現在「サスペンド(中断)」状態です。人間がリンクをクリックするのを待つ間、計算リソースの消費はゼロです。
00:06:41Vercel上ではどう見えますか?待機中の実行状態を確認できますか?
00:06:47はい、メールが届きました。クリックする前に、オブザーバビリティ(観測可能性)を見てみましょう。
00:06:52話が飛びますが、この画面を見るのが大好きなんです。
00:06:57OK、40秒前に開始された実行がここにありますね。
00:07:02中を見ると、ワークフローが提供する標準的な観測機能が確認できます。
00:07:08入力値として、フォームに入力した僕のメールアドレスが見えます。
00:07:13そして面白いことに、フックがここで待機しているのがわかります。
00:07:17コンピューティングは実行されていないと言いましたね。観測はできていますが、クリックを待つために何かが動いているわけではない。
00:07:25その通りです。フックは待機中で、sleepは眠っています。どちらも実際には計算リソースを使いません。
00:07:39覚えている通り、これら2つはpromise.raceで競い合っています。
00:07:46ワークフローが再開するには、どちらかが先に完了する必要があります。
00:07:50ではリンクをクリックします。はい、ログイン成功ページにリダイレクトされました。これはロジック通りのステップです。
00:07:59ログインフォームに戻ってみると…
00:08:01ダッシュボード側でも完了しているはずですね。
00:08:05その通り。ワークフローが完了しました。
00:08:08フックが勝利した時点で、タイマーも止まっています。
00:08:11ええ。マジックリンクを約50行のコードで実装できました。
00:08:17実に見事です。ホワイトボードでマジックリンクの仕組みを説明する時の図が、
00:08:27そのままコードのステップとして対応していますね。それが最終的なコードになっている。
00:08:34間にデータベースを挟んだり、複数のAPIルートを作ったりする必要がない。非常に読みやすいコードです。
00:08:41それこそがWorkflow SDKの最も強力な点です。インフラに合わせてロジックを捻じ曲げるのではなく、アプリケーションの論理的な流れのままに記述できるのです。
00:08:59なるほど。ウェブフックという名前の付け方も面白いですね。考え方が全く変わります。
00:09:07一時的なURLを作成して、そこで処理を中断できるわけですから。
00:09:10これがいい橋渡しになりそうですが、Vercelでは多くのエージェントを構築していますよね。
00:09:16SlackやGitHubのエージェントを作っていて、それらのウェブフックを購読することがよくあります。
00:09:23PRにコメントが付くたびにエージェントを起動したい場合、GitHubからのイベントをトリガーにしますよね?
00:09:31Workflowのウェブフックを使って、例えばGitHubからのイベントを購読することはできますか?
00:09:36SlackやGitHubから送られるウェブフックの場合、通常はダッシュボードで静的なコールバックURLを手動で設定する必要があります。
00:09:49そうですね。メールのように、その都度使い捨てのURLを渡すことはできません。
00:09:54はい。「createWebhook」機能はより抽象度が高く、特定のワークフロー実行に紐づく、
00:10:04ランダムに生成された一意のURLを提供します。
00:10:07一方、GitHubやSlackのウェブフックルートは、不特定多数のワークフロー実行に紐づく可能性があります。
00:10:14ええ。事前設定が必要ですが、プルリクエストが複数あっても、すべて同じエンドポイントに送られます。
00:10:20そのため、これをWorkflow SDKで実装するには、より低レイヤーな「hook」プリミティブを使用します。
00:10:28それをお見せするためのデモを用意しました。
00:10:31見てみましょう。
00:10:32これは「ストーリータイム・ボット」です。
00:10:35これは僕が1年以上前にWorkflow SDKを使って初めて書いたアプリケーションです。
00:10:40「/storytime」コマンドを入力すると、このようにスレッドが作成されます。
00:10:47それぞれのスレッドが、個別のワークフロー実行に対応しています。
00:10:52スレッドを開くと、LLMが物語を書き始めています。僕やあなた、このチャンネルにいる人なら誰でも続きを書けます。
00:11:05そして、LLMが物語を最終的な結末へと導いてくれます。
00:11:09ルナが魔法の種を持っています。次に何が起きますか? 彼女は種を植えました。
00:11:13何やら動きがありましたね。
00:11:17次は? 魔法のようなことが起きて…
00:11:20物語が完成しました。最後に画像も生成されます。
00:11:26それは後で見るとして、
00:11:28気になることがあります。ウェブフックのリクエストは1回だと思っていましたが、今のやり取りで少なくとも2回はありましたよね。
00:11:35これがコードでどうなっているのか非常に興味があります。
00:11:38これがストーリータイム・ボットのワークフロー関数です。
00:11:44チャンネルIDを受け取ります。これがボットのいるチャンネルですね。
00:11:50いくつかの設定オプションも渡せます。
00:11:53注目すべきは、この「messages」配列です。AI SDKを使ったことがあれば馴染みのある、AIの会話を保存する形式です。
00:12:04通常のSlackボットなら、これをデータベースに保存し、ウェブフックイベントのたびに、つまり新しいメッセージが投稿されるたびに、状態を復元してデータベースから会話を取得します。
00:12:23でも、ここではそうしていません。ただの関数のローカル配列です。
00:12:27いいですね。イントロのコメントに「お母さん見て、キューもKey-Valueストアもいらないよ」とあったのを見て笑ってしまいました。
00:12:34データベースのインポートもありませんね。インポートしているのはWorkflowだけです。
00:12:40「final story」という変数があり、そこにメッセージをプッシュしていくのでしょうが、
00:12:55どこにもデータベースに送る処理がない。つまり「let」変数がデータベースの代わりになっているんですね。
00:13:04「letがデータベース」というのはいい表現ですね。使わせてもらいます。
00:13:10あなたから盗んだ言葉かもしれませんが(笑)
00:13:14ここで重要なのは「hook」機能です。ここでフックを作成しています。マジックリンクのウェブフックと違うのは、ここで「トークン」を渡している点です。
00:13:28これは、このワークフロー実行を一意に識別するための情報を含む文字列です。
00:13:35「TS」はスレッドIDです。この文字列が、このワークフロー実行を特定するトークンになります。
00:13:44ウェブフックルートのコードを見れば分かりますが、Slackから送られるイベントペイロードには、この識別子を決定論的に再現するために必要な情報がすべて含まれています。
00:13:58それが、ウェブフックを個別のワークフロー実行に紐付ける魔法の仕組みです。
00:14:04なるほど。マジックリンクでは新しいURLを作っていましたが、Slackボットではスレッドごとに新しいURLを教えることはできませんからね。
00:14:17つまり、Slackに接続された一つのAPIエンドポイントがあり、メッセージを受け取るたびに、受信側で同じトークンを計算しているということですね。
00:14:29そうすれば、ワークフローはそのトークンを待機でき、メッセージペイロードから同じトークンを生成して実行を再開できる。
00:14:37その通りです。Slackボットは一度だけダッシュボードで設定され、静的なウェブフックURLが定義されています。
00:14:50だからこそ、トークンを動的に再生成できる、より低レイヤーな「hook」プリミティブが適しているのです。
00:14:59ウェブフックルートの方も見てみましょう。実は大したことはやっていません。
00:15:07主な処理は、Slackから渡されたデータを使ってトークンを再現することです。
00:15:13そして「resume」関数を呼び出します。これで特定のワークフロー実行が再開されます。
00:15:20面白いですね。結局のところ、ウェブフック(createWebhook)も裏では同じことをしているんでしょうか?
00:15:28ランダムなトークンを生成して、それを解決するHTTPエンドポイントがあるようなイメージですか?
00:15:35ええ、違いは「createWebhook」を使う場合、自分でAPIルートを定義する必要がないことです。
00:15:44Workflow SDKがデフォルトのルートを自動的に実装してくれます。
00:15:50それ以外は、特定の実行に紐づくランダムなトークンであるという点は同じです。
00:15:55今回のケースではトークン付きの「hook」を使っていますが、もう一つの特徴は、このフックはデータを複数回受け取れることです。
00:16:06一度きりのトリガーだったマジックリンクとは異なります。
00:16:11ここでは、Slackスレッドにメッセージが投稿されるたびにフックを発火させたい。
00:16:17そのために、JavaScriptの非同期イテレータでよく使われる「for await...of」構文を使います。
00:16:25フックを使って、Slackのウェブフックから複数のイベントペイロードを受け取っています。
00:16:33これは素晴らしい。非同期イテレータやジェネレータはずっと好きで、昔それについて講演したこともありますが、
00:16:42デモ以外で適切な使い道が見つかりませんでした。
00:16:46でもこれなら、単なるループとして読めますね。
00:16:50固定のアイテムセットやタイムスタンプでループする代わりに、「for await...of」をフックに対して使うことで、
00:17:01ループ内の処理をユーザーの各メッセージに完璧に対応させることができる。
00:17:05新しいメッセージが来るたびにループが1回まわり、キューに溜まって処理が進んでいく。非常に分かりやすい考え方です。
00:17:12美しいのは、ユーザーが次のメッセージを入力するのを待っている間、計算リソースは全く消費されないことです。
00:17:22ワークフローは完全にサスペンドされており、次のメッセージが数分後でも、数日後でも、あるいは二度と来なくても問題ありません。
00:17:33数週間誰も反応しなくても、実行がそのまま待機し続けているスレッドがあるかもしれないわけですね。
00:17:42それは本当にクールだ。
00:17:43先ほどの「messages」配列の話に戻ると、ここでは単にその配列を更新しています。
00:17:48新しいメッセージをプッシュする。配列はローカル変数なので、これが「データベースの更新」に相当します。
00:17:57すごいですね。さらに「promise.all」を使ってステップを並列化しているのも見えます。
00:18:03Slackの各メッセージに対する処理が、非常にクリーンに記述されていますね。
00:18:08ハッカソンなどでこれを作ろうとする時、まさにこうモデル化するだろうという書き方です。
00:18:12「メッセージごとに何が起きるか」をそのまま書き下したような。
00:18:16ええ。「promise.all」を使っているのは通常のステップ関数で、それらを並列実行するためです。
00:18:23例えば、Slackメッセージにリアクションを付けるのは、ユーザーに何かが起きていることを即座に伝えるためです。
00:18:32それと同時に、LLMを起動して物語の生成を進めています。
00:18:39機会があればオブザーバビリティも見てみたいです。スパンが同時に始まって、並列に動いているのが一目瞭然でしょうから。
00:18:49ストーリータイムの観測データもありますよ。
00:18:52すでに完了しているので、さっきの画像も確認しましょう。
00:18:56フックが見えますね。
00:18:58ここで面白いのは、2つの「hook received(フック受信)」イベントがあることです。
00:19:05これは僕がSlackスレッドに入力した2つのメッセージに対応しています。
00:19:10個別のイベントをクリックすれば、フックに渡された具体的なデータを確認できます。
00:19:14それは便利ですね。
00:19:16つまり、Slackのペイロードが特別なログ出力をしなくても、ダッシュボード上のイベントとして確認できるわけですね。
00:19:25その通りです。フックのペイロードを受け取るたびに実行が再開され、ステップが進んでいきます。
00:19:34そして最終的に、ストーリーボード画像を生成した結果がこちらです。
00:19:40これがストーリータイム・ボットです。
00:19:42本当に素晴らしい。
00:19:43マジックリンクでのウェブフック生成も、低レイヤーなフックをループで使って複数イベントを処理するのも見られて、
00:19:54とても勉強になりました。
00:19:55ウェブフックを使った人間とのやり取りのモデルが、自分の中でカチッと繋がった気がします。
00:20:02フックには他にも用途はありますか?
00:20:05もちろんです。
00:20:06最後に用意しているデモは、Slackのウェブフックへの応答と似たパターンですが、
00:20:17ウェブフックを実行の「ハンドオフ(受け渡し)」として使います。他の場所で計算が終わるのを、ワークフローをサスペンドして待ちます。
00:20:32そして、そのウェブフックURLをコールバックとして使ってもらい、戻ってきたところで残りのアプリケーションロジックを完了させます。
00:20:41この例ではVercel Sandboxを使い、FFmpegを使ってファイルの変換を行うような、実行時間の長い計算処理を行います。
00:20:51これがFFmpeg変換ワークフローです。
00:20:56まず最初に行うことの一つは、Vercel Sandboxの作成です。
00:21:00面白いのは、SandboxのNPMパッケージが提供する関数の内部で、「useStep」が使われていることです。
00:21:09つまり この操作自体が1つのステップ(Step)になります
00:21:12NPMパッケージとして公開することも可能です
00:21:15Sandboxは 基本的に関数内に"use Step"ディレクティブを
00:21:21含んだNPMパッケージを配布しているだけです
00:21:29ワークフローを介さずにインポートして使うと 自動的に
00:21:32Sandboxがステップになります コードを書く必要はありません
00:21:35ワークフロー外でSandboxを使っても 問題ないという意味ですか?
00:21:47ワークフローなしで呼び出すと どうなるのでしょうか?
00:21:49ディレクティブは単なる文字列なので ワークフローコンパイラなしで
00:21:55実行しても その文字列は何の影響も与えません
00:22:03なので そのまま動作します
00:22:07NPMパッケージに"use Step"を追加しても Workflow SDKなしで正常に動きます
00:22:11そして その関数をWorkflow SDK内で使用すれば
00:22:14すぐに耐久性(Durability)のメリットを享受できます
00:22:17なるほど Sandboxでは一般的な処理を行うのですね
00:22:29デフォルトでは入っていないので FFmpegをインストールし
00:22:36指定されたファイルのURLをダウンロードします
00:22:43これらの実行も 現時点ではそれぞれがステップなのですか?
00:22:53はい Sandbox内で個別のコマンドを実行しており これらがステップです
00:22:59これらはオブザーバビリティ画面で確認できます
00:23:04そして「create-webhook」の呼び出しに戻ります
00:23:11マジックリンクのデモで 覚えているかもしれませんね
00:23:17今回は そのWebhook URLをSandboxで実行する
00:23:20Bashスクリプトに渡すことになります
00:23:28ここで何が起きるかというと FFmpegを実行して
00:23:34UIでリクエストされた形式にファイルを変換します
00:23:40それが完了すると BashスクリプトがWebhookの
00:23:46コールバックURLに対してcURLを実行します
00:23:51そのcURLリクエストが発生すると ワークフローのロジックが再開します
00:23:56なるほど すごいですね 先読みして気づいたのですが
00:24:01この実行(run)には「AND」が含まれていますね
00:24:04FFmpegのようなステップは 非常に時間がかかる可能性があるため
00:24:05スクリプトを書いて バックグラウンドで実行させているのですね
00:24:07ステップがただ完了を待って 居座り続けるのは避けたいですから
00:24:12その通りです この行でFFmpegの変換スクリプトを
00:24:16バックグラウンドで開始させています
00:24:19そして ワークフロー関数はサスペンド(中断)し
00:24:26Webhookが再開されるのを待ちます
00:24:33また「promise race」と1時間のスリープを併用していますね 素晴らしいパターンです
00:24:37ええ FFmpegの変換プロセスには 長時間がかかる場合があります
00:24:42メディアファイルが非常に大きい可能性もあるので 今回は1時間のタイムアウトを設定しています
00:24:46ワークフローでは 実質的に無制限の時間をスリープさせることができるので 全く問題ありません
00:24:53繰り返しますが Webhookの再開を待っている間 計算リソース(compute)は一切消費されません
00:25:03これを実際に見ることはできますか? デモはありますか?
00:25:06ありますよ
00:25:17少し変わった例ではありますが
00:25:25ええ すぐに分かりました Blenderの「Big Buck Bunny」ですね
00:25:31昔 Blenderを勉強していた時に これらのビデオを見たのを覚えています
00:25:43おお それは羨ましいですね
00:25:48メディアファイルのURLを貼り付けました 今回は音声レイヤーのみを抽出します
00:25:57ボタンをクリックすると ワークフローが開始されます
00:26:04オブザーバビリティ画面を見てみましょう
00:26:13おっ 出ましたね Sandboxが作成されているのが分かります
00:26:21そして Sandboxのインスタンスが返ってきました かっこいいですね
00:26:27ワークフロー内のものはすべて シリアライズ可能である必要がありますが
00:26:34おっしゃる通り Sandboxはシリアライズを実装しているため
00:26:42シリアライズ可能な状態で ワークフローに表示されます
00:26:50その通りです Vercel SandboxのNPMパッケージにはSandboxクラスがあり
00:27:01そのクラスがワークフローの シリアライズ関数を実装しています
00:27:05そのため オブザーバビリティ画面でも正常に動作します
00:27:12Sandboxに限らず どのパッケージでも可能ですよね?
00:27:19ワークフロー内で動作させたいクラスなら 同じシンボルを実装して
00:27:31"use Step"ディレクティブを持たせればいいだけですから
00:27:42ええ その通りです 今回は20秒でフックがコールバックされました

Key Takeaway

Workflow SDKは、状態管理やデータベース構築などのインフラ制約を排除し、JavaScriptの標準的な非同期処理パターンのみで耐久性のある複雑な分散ロジックを記述可能にする。

Highlights

  • Workflow SDKは、マジックリンクのログイン機能をデータベース不要で約50行のコードに圧縮する。

  • JavaScriptの標準的なPromise.raceとsleepを使用し、5分間などのタイムアウトロジックを簡潔にモデル化できる。

  • フック(Hook)機能により、計算リソースを一切消費せずにユーザーの操作を数週間でもサスペンド状態で待機できる。

  • 低レイヤーのフックプリミティブは、固定された一意のトークンを使用して外部の静的ウェブフックを特定のワークフロー実行に紐付ける。

  • for await...of構文を使用し、Slackのスレッド投稿などの連続的な外部イベントを単一のループ内で処理できる。

  • Vercel Sandboxと連携し、FFmpegによる動画変換のような長時間実行されるタスクをワークフロー外へハンドオフして完了を待機する。

Timeline

従来のマジックリンク実装における複雑性と課題

  • マジックリンクの構築には、セッション管理のためにRedisなどの外部データベースとTTLの設定が必要である。
  • ロジックが複数のエンドポイントやファイルに分散し、コードの保守性が急速に低下する。
  • データの有効期限を管理するために、データベースを掃除するためのcronジョブや追加の運用コストが発生する。

ZEIT時代から続くマジックリンク認証の実装経験に基づき、従来の手法がいかに複雑であるかが示された。3つのエンドポイントが必要になり、メール送信の失敗への対応や、状態を復元するためのデータベース問い合わせがスパゲッティコードを誘発する。この複雑性は単純なログイン機能において大きな開発負荷となる。

Workflow SDKによるデータベースレスな実装

  • useWorkflowディレクティブを使用することで、ワークフロー関数を定義し状態を永続化できる。
  • createWebhook関数とrespondWithManualオプションの組み合わせにより、リダイレクト等のレスポンスを自在に制御できる。
  • 自動リトライ機能が備わっているため、メール送信などの不安定な外部ステップの耐久性が向上する。

約50行のコードでマジックリンクが完結する様子が提示された。Redisの代わりにsleepプリミティブを使い、Promise.raceでタイムアウトを記述することで、インフラではなくアプリケーションの論理的な流れに沿った実装が可能になる。これにより、インフラの制約に合わせてロジックを曲げる必要がなくなる。

計算リソースを消費しないサスペンド状態と観測可能性

  • ワークフローは人間がリンクをクリックするのを待つ間、完全にサスペンドされ計算リソースを消費しない。
  • ダッシュボード上で、待機中のフックの状態や入力されたペイロードなどの実行履歴を詳細に追跡できる。
  • ホワイトボードに描くような論理的な図解が、そのままコードのステップとして対応する。

デモを通じて、ワークフローが実際にサスペンドされている様子が示された。実行から時間が経過しても、Vercelのダッシュボードからは現在のステップや入力値が確認可能である。クリックが発生した瞬間にのみ処理が再開されるため、効率的なリソース運用と高い透明性が両立されている。

静的ウェブフックと動的トークンによる外部連携

  • GitHubやSlackのような静的URLを要求するサービスには、トークン付きのhookプリミティブが適している。
  • メッセージのペイロードから決定論的に生成されたトークンを使用し、不特定多数のイベントを特定の実行インスタンスに紐付ける。
  • 状態をローカル変数(let)として保持できるため、Key-Valueストアやキューのインポートが不要になる。

Slackボット「ストーリータイム・ボット」を例に、より低レイヤーなフックの活用法が紹介された。ウェブフックを受け取るエンドポイント側でトークンを再計算してresume関数を呼び出すことで、特定のワークフローを再開させる。データベースを使わずに、関数のローカル配列だけでAIとの会話履歴を管理する手法は、開発モデルを大幅に簡略化する。

非同期イテレータを用いた複数イベントのストリーム処理

  • for await...of構文により、フックが受け取る複数のイベントをループとして直感的に記述できる。
  • ユーザーが数日間反応しなくても実行状態は維持され、新しいメッセージが届くたびにループが1回実行される。
  • Promise.allを併用することで、リアクションの付与とLLMの起動といったステップを並列に処理できる。

JavaScriptの非同期イテレータをワークフローに応用することで、ユーザーとのやり取りを時系列に沿ったループとしてモデル化できる。メッセージごとに何が起きるかをそのまま書き下すだけで、複雑な状態遷移を伴うインタラクティブなボットが構築可能となる。実行が再開されるたびに、ステップが進む様子がダッシュボード上で確認できる。

Vercel Sandboxへのハンドオフと長時間計算の実行

  • 長時間かかるFFmpeg変換などの処理は、外部のSandboxへハンドオフして完了を待機する。
  • cURLを用いたコールバックURLへのリクエストにより、外部環境での処理完了をワークフローに通知する。
  • use Stepディレクティブを含むNPMパッケージは、ワークフロー内外でシームレスに動作し、シリアライズも可能である。

ウェブフックを実行の受け渡し(ハンドオフ)として利用する高度なパターンが示された。ワークフロー自体は1時間のタイムアウトを設定してスリープし、外部のBashスクリプトが完了後にcURLでフックを叩くことで処理を再開させる。Vercel Sandboxのクラスがシリアライズ関数を実装しているため、オブザーバビリティ画面でもインスタンスの状態が可視化される。

Community Posts

No posts yet. Be the first to write about this video!

Write about this video