Script

服屋のショッピングモールを運営していると考えてみてください。商品を登録するたびに、魅力的な詳細説明を一つひとつ書くのは手間のかかる作業です。そこで、商品名とキーワードを入れるだけで、AI が詳細説明を代わりに書いてくれるようにしたいと思います。ところが、その AI 文章作成サービスを呼ぶには、秘密の鍵(access token、外部のサービスが「料金を払ったユーザーかどうか」を確認するための鍵)が必要です。この鍵を、お客さんが見るウェブサイト(ブラウザ)に入れておくと、誰でも取り出して見られるので漏れてしまいます。漏れた鍵で他人がこのサービスを勝手に使い、その料金がこちらに請求されることもあります。

そこで、鍵をお客さんの目に触れない場所に隠したまま、ウェブサイトの代わりに AI を呼んで、その結果を商品に埋めてくれる何かが必要です。Script がそれです。Script は「この鍵で AI を呼び、受け取った文章をこの商品の詳細説明に入れて」のように、やることを順番に書いておくものです。コードではなく、決められた様式(JSON、項目と値を波かっこで書くデータの表記方法)で書きます。ウェブサイトは、この Script をインターネットから呼ぶだけでよく、鍵は Script の中に隠されていて、お客さんには見えません。

あらかじめ書いておいたレシピを厨房に貼っておくことにたとえられます。お客さんがそのメニューを注文すると(ウェブサイトが Script を呼ぶと)、厨房(WEEGLOO)がレシピに書かれた順番どおりに作り、出来上がった料理を出してくれます。店主はレシピを書いて貼っておいただけで、注文が入るたびに自分で料理をするわけではありません。このページでは、Script が何であり、どんな形をしているのか、呼ぶと何を返すのかを見たうえで、服屋の「商品説明の入力」Script を例に、その形を確認します。最後に、この Script を、商品が登録されるときにひとりでに実行されるようつなぐ方法も見ます。

Script が代わりにしてくれること

商品説明を一つ埋めるという作業にも、裏側でやるべきことはいくつもあります。呼ぶ側に権限があるかを確認し、送られた値が正しいかを検査し、隠しておいた鍵で外部の AI サービスを呼び、受け取った結果を望みの場所(商品の詳細説明)に入れ、応答を返します。以前は、こうした作業をこなす中間のプログラムを自分で作り、サーバーに載せて管理しなければなりませんでした。Script は、これらの作業をコードなしで一か所に書いておいて代わりに行わせることを目指しています。

  • 一つの Script は一つの呼び出し窓口です。 ウェブサイトがインターネットから呼べる窓口一つが Script 一つです。呼ぶときに使う方式(method)で、どの Script を実行するかを見分けます。
  • やることは上から下へ並べます。 Script の中には、実行する動作を順番に書きます。上から順に実行され、前の動作の結果を次の動作が受け継ぎます。
  • 決められた動作を選んで組み合わせます。 どんなコードでも入れられるわけではなく、あらかじめ用意された動作(リソースの作成・読み取り・修正・削除、外部サービスの呼び出し、値の保持、条件チェック、繰り返しなど)を選んで並べます。

何をするかを書いておく定義

Script 一つは、次の項目を定めた「定義」で成り立ちます。

  • 呼ぶ方式(method): この Script を呼ぶときに使う方式です。Get・Post・Put・Patch・Delete のいずれかで、呼ぶときにこの値でどの Script かを見分けます。
  • やること(statements): 上から下へ実行する動作の一覧です。少なくとも一つは必要です。
  • 入力チェック(payloadSchema、任意): 呼ぶときに一緒に送る入力を、実行前に確認するための形式です。定めておくと、形式に合わない入力は実行せずに差し戻します。
  • シークレット値(secrets、任意): 外部のサービスの鍵のように、やることの中にそのまま書いてはいけない値を別にまとめておく一覧です。値ごとに名前(key)を付けておき、やることの中では { /secrets/<key> } で呼び出して使います。入れておいた値は、WEEGLOO が暗号化して安全に保存します。

服屋の「商品説明の入力」Script を例に見ていきます。この Script が扱うのは、商品名とキーワードが入った商品一つです。ウェブサイトから渡される入力(後で見る自動実行のときは、登録された商品がそのまま渡されます)は、このような形です。

{
  "sys": { "id": "3trmXRMKq7bd0Prbef1... (商品番号)" },
  "fields": {
    "productName": { "ja-JP": "ステンレスタンブラー500ml" },
    "keywords":    { "ja-JP": "保温、軽量、キャンプ" }
  }
}

この商品を受け取り、外部の AI で詳細説明を作り、その商品の詳細説明(body)を埋める Script の定義です。

{
  "method": "Post",
  "statements": [
    { "type": "Http", "method": "POST",
      "url": "https://api.ai-writer.example.com/v1/generate",
      "headers": [
        { "key": "Authorization", "value": "Bearer { /secrets/AI_WRITER_TOKEN }" }
      ],
      "body": {
        "product":  "{ /payload/fields/productName/ja-JP }",
        "keywords": "{ /payload/fields/keywords/ja-JP }"
      },
      "name": "gen" },
 
    { "type": "ResourcePatch", "resource": "Content",
      "target": { "sys": { "id": "{ /payload/sys/id }" } },
      "fields": { "body": { "ja-JP": "{ /gen/body/text }" } },
      "publish": true },
 
    { "type": "Return", "value": { "id": "{ /payload/sys/id }" }, "statusCode": 200 }
  ],
  "secrets": [
    { "key": "AI_WRITER_TOKEN", "value": "<秘密の access token>", "description": "AI 文章作成サービスの鍵" }
  ]
}
  • 最初の動作(Http)が、隠しておいた鍵で外部の AI サービスを呼びます。鍵はやることの中には書かず、シークレット値(secrets)に AI_WRITER_TOKEN という名前で入れておきました。リクエストと一緒に送る付加情報(ヘッダー)では、この鍵を { /secrets/AI_WRITER_TOKEN } で呼び出して使います。Script がこの値を応答として返さない限り、お客さんが見るウェブサイトに鍵が現れることはありません。受け取った結果は gen という名前に入れておきます。
  • 二つ目の動作(ResourcePatch)が、先ほど受け取った文章({ /gen/body/text })で、その商品の詳細説明(body)だけを埋めます。商品のほかの値には手を付けません。
  • 値を次の段階へ流していくプレースホルダー { /… } を使います。{ /payload/fields/productName/ja-JP } は渡された商品の名前を、{ /payload/sys/id } はその商品の番号を、{ /gen/body/text } は AI が返した文章を、{ /secrets/AI_WRITER_TOKEN } はシークレット値に入れておいた鍵を指します。
  • 最後の動作(Return)が、説明を埋めた商品の番号を返します。
  • Content の値を { "ja-JP": … } のように言語別に書く理由、statements に入れられる動作の全種類、プレースホルダーと条件・計算の文法は、値式 と Statement カタログ で扱います。

呼ぶと何を返すか

Script は、最後に Return 動作の値を呼んだ側に返します。返される応答には、次のものが入ります。

  • requestId: 今回の実行を指す識別番号です。
  • durationMs: 実行にかかった時間(ミリ秒)です。
  • statusCode: 到達した Return の状態コードです(別途定めなければ 200)。
  • return または error: Return が返した値です。ふつうは return に入り、その値をエラーとして示しておくと error に入ります。二つが一緒に出ることはありません。

「商品説明の入力」も、呼ぶとその場で実行され、上の応答がそのまま返ってきます。ただし外部の AI を呼ぶ動作が入っているので、応答が来るまでに数秒かかることがあります。一度の実行に使える時間は、下の 実行に与えられる時間 で扱います。返ってきた応答は、このような形です。

{
  "requestId": "3trmXRMZ8kqLb2Prdf1eYc0axWnKv",
  "durationMs": 1840,
  "statusCode": 200,
  "return": { "id": "3trmXRMKq7bd0Prbef1... (商品番号)" }
}

ウェブサイトは、この return の id で、たった今説明が埋められた商品を指し示し、新しい詳細説明をお客さんに見せることができます。

Return に届かないまま Script が終わると、return も error もなく、statusCode だけが 200 で返ります。Return で応答本文と状態コードを定める詳しい規則は、Statement カタログの Return で扱います。

実行に与えられる時間

Script は、呼んだその場で実行されます。呼んだ側は、その実行の結果を応答としてすぐに受け取ります。結果を後から問い合わせ直して取得する流れはありません。

一度の実行に使える時間には予算があります。既定は30秒です。外部のサービスを呼ぶ動作が入っていると、その動作に決めておいた待ち時間の分だけ予算が増えます。このように増えても、最大180秒までです。

何かを繰り返す動作は、予算を大きく使います。繰り返しの中の動作に決めておいた待ち時間に、繰り返しの回数を掛けた分だけ見込まれるからです。繰り返しの回数を大きく決めておくと、予算もその分だけ大きく計算されます。

決められた時間を超えると、その実行はそこで中断されます。「商品説明の入力」は外部の AI を一度呼ぶので、既定の30秒にその一度の待ち時間を足した分が、この Script の予算です。

動作ごとに予算がどれだけ見込まれるのか、そのほか実行にかかる制約は、実行セマンティクス、制約、セキュリティ で扱います。

Script は誰が作るのか

複雑な動作を人が手で一つひとつ書くよりも、Script は AI エージェントやプログラムが作るように設計されています。AI エージェントに「商品説明を埋めてくれる窓口を作って」と言葉で頼むと、エージェントが上で見たような定義を代わりに作ってくれます。ひとことで、ウェブサイトの裏で働く窓口が一つできるわけです。

AI エージェントで Script を作る詳しい流れは、一言でバックエンドを作る で扱います。

作られた Script は、管理画面(コンテンツスタジオ)で人が見て管理します。名前と定義を確認し、必要なら直したり消したりします。実際に Script を呼ぶ側は、お客さんが見るウェブサイトやアプリ(フロントエンド)です。製品に登録した会員(ServiceUser)の身元では、Script の実行だけができ、作成や修正はできません。

Webhook とどう違うのか

Script と Webhook はどちらも外部とつなぐ仕組みですが、呼ぶ方向が反対です。

  • Webhook は、決めておいた変化が起きると(商品が登録されるように)、ひとりでに反応します。人が呼ばなくても、出来事が起きればひとりでに動きます。ただし、呼んだ側に結果を返すことはありません。
  • Script は、ウェブサイトが必要なときに自分で呼ぶ窓口です。呼んではじめて実行され、その実行の結果をすぐに受け取ります。

「店主が『説明の入力』を押すと AI を呼んで詳細説明を受け取り、埋める」は、呼ぶ側が結果を待つ作業なので Script が、「商品が登録されると自動で何かが起こる」は、出来事に反応する作業なので Webhook が向いています。そして、この二つは一緒に使えます。すぐ次で見ていきます。

登録するだけで説明がひとりでに埋まるように

ここまでは、店主が「説明の入力」ボタンを押して Script を 自分で 呼んでいました。一歩進んで、ボタンを押さなくても、商品を登録した瞬間に Script が ひとりでに 実行されるようにできます。Webhook がその出来事をとらえて、こちらの Script を代わりに呼んでくれるからです。

流れは、次のとおりです。

  1. 店主が商品を登録します。このとき、商品名とキーワードだけを埋め、詳細説明は空けておきます。
  2. 商品が新しく登録される出来事を Webhook が察知します。
  3. Webhook が、たった今登録された商品をそのまま、こちらの「商品説明の入力」Script に渡して実行します。
  4. Script が外部の AI で詳細説明を作り、その商品の詳細説明(body)を埋めます。
  5. 少し後には、商品の詳細説明がひとりでに埋まっています。

ここで使う Script は、先ほどとまったく同じものです。変わるのは、呼ぶきっかけだけです。ボタンの代わりに「商品が登録された」という出来事が呼ぶのです。登録された商品がそのまま Script の入力になるので、Script は { /payload/sys/id } でその商品をつかみ、詳細説明を埋めます。

Webhook の側で定めることは、三つです。どの出来事に反応するか(商品が新しく登録されるとき)、どの商品にだけ反応するか(商品の種類で限定)、そして何をするか(外部のアドレスへ知らせる代わりに、こちらの Script を呼ぶこと)です。

Script が埋めた詳細説明が、また「商品が変わった」という出来事を引き起こして、いつまでも繰り返されるのではないかと心配になるかもしれません。そうはなりません。Script の書き込みは、別途オンにしない限り新しい出来事を引き起こさず、プラットフォームも果てしない繰り返しを防ぎます。

ある Script は、このように Webhook を通してのみ実行されるようにし、外部からアドレスで直接呼ぶことは止めておくことができます。そうすると、その Script は決めておいた出来事にだけ反応し、直接呼び出すと拒否されます。設定の方法は、Script リソースとエンドポイント で扱います。

このようにつなぐ詳しい設定は、Webhook で扱います。

Script が特に役立つ場合

外部に「こういうことが起きた」と知らせるだけで済む作業なら、Webhook 一つで十分です。しかし、外部のサービスを呼んだあと、その結果を見て続けて判断し、処理しなければならないなら、その流れ全体を一か所にまとめる Script が必要です。

有料で AI 画像を作ってくれる機能を例に挙げてみます。お客さんが画像の生成を依頼すると、次のことが順番に起こらなければなりません。

  1. お客さんのクレジットが足りているかを確認します。足りなければここで止まり、「クレジットが足りません」と知らせます。
  2. 足りていれば、費用の分だけクレジットを先に差し引きます。
  3. 外部の AI サービスを呼んで画像を作ります。
  4. 作られた画像を Content として保存します。
  5. 3番か4番で問題が起きたら、先ほど差し引いたクレジットをまた元に戻します。

Webhook は「依頼が入ってきた」と外部に知らせることはできても、このように結果を見てクレジットを差し引いたり、失敗を元に戻したりすることはできません。いくつもの段階を条件に応じてつなぎ、失敗すれば前の段階を元に戻す仕事は Script が担います。Script が特に力を発揮する場合は、次のとおりです。

  • 結果を見て続けて処理する必要があるとき: 外部のサービスが返した応答に応じて、保存するか、差し引くか、元に戻すかをその場で判断します。
  • 同時に入ってきてもずれてはいけないとき: 同じお客さんが短い間に二度依頼しても、クレジットが二度差し引かれてはいけません。Script は、値を読んだあと保存する直前に「その間にほかの依頼がこの値を変えていないか」をバージョンで確認し、ずれていれば止まります。
  • 呼ぶ側にない権限が必要なとき: お客さんには、クレジットの残高を自分で直接直す権限がありません。それでも差し引きが安全に行われるのは、Script がそれを作った人の権限を委任されて実行されるからです。呼ぶ側には、Script を実行する権限だけを与えればよいのです。この委任は、下の 実行と管理の権限 で詳しく扱います。

この例を実際の Script の定義としてどう書くのかは、クックブック のクレジットの確認・差し引き・戻しの例で扱います。

実行と管理の権限

Script を実行したり管理したりするには、ロール(SpaceRole)にそれに応じた権限が必要です。

  • 実行: Script を呼ぶには、ロールに Script の実行権限(Execute)が必要です。なければ実行がふさがれます。
  • 管理: Script を照会し、作り、直し、消すには、それぞれ照会・作成・修正・削除の権限が必要です。

Script を実行するときに確認されるのは、呼ぶ側に実行権限(Execute)があるか、この一つだけです。Script の中に並べた個々の動作は、実行するその瞬間に別途権限を確認されることはありません。実行を許されたプログラムを呼ぶとき、そのプログラムを実行する権限だけを見て、その中でする一つひとつの作業まで毎回許可を得るわけではないのと同じです。

その代わり、個々の動作の権限は、実行するときではなく Script を 保存するとき に前もって確認されます。作った人が、その Script の中の動作が扱う Content・Media の操作権限を実際に持っていてはじめて保存されます。たとえば「商品説明の入力」Script は商品 Content の詳細説明を直すので、作った人に商品を直す権限がなければ、保存が差し戻されます。権限のない動作が入った Script は、そもそも保存されないということです。

こう見ると、Script の実行は、それを 作った人の権限を委任されて 代わりに行うのと同じです。呼ぶ側が自分では行えない作業でも、作った人ができる作業であれば、Script を通してそのまま起こります。ですから、Script を作るときは、その中にどんな動作を入れるかを慎重に決めなければなりません。作った人の権限が、そのまま、その Script にできることの範囲になります。

ロールに権限を入れる方法は、ロールと権限 で扱います。

知っておくこと

  • 発行がありません。 Script は、発行して訪問者へ配信される種類のリソースではなく、管理画面で作っておき、ウェブサイトが呼んで使う窓口です。ですから Content・Media と違って発行・発行取り消しの状態がなく、作ればすぐに使えます。直すたびにバージョンが一つずつ上がるだけで、消すときも発行取り消しのような事前の段階なく、そのまま消えます。
  • シークレット値は、定義を照会できる人には見えます。 シークレット値は WEEGLOO が暗号化して保存しますが、Script を照会できる人は元の値を見ることができます。そのため、Script を照会する権限は、本当に必要な人にだけ与えます。製品に登録した会員(ServiceUser)は、Script の定義を照会できません。
  • プログラムで定義を直すときは、シークレット値も一緒に送ります。 定義は直すたびに全体が置き換えられるので、シークレット値を外して保存すると、入れておいたシークレット値が消えます。やることの中でその値をまだ呼び出して使っている場合は、保存そのものが拒否されます。
  • 数に上限があります。 Script は課金の対象なので、一つの Organization が持てる数が料金プランごとに決まっています(Free 10個、Basic 30個、Pro 100個、Enterprise 無制限)。上限に達すると新しい Script を作れなくなり、使っていない Script を消すと、一枠がまた空きます。

コンテンツスタジオで管理する

作られた Script は、コンテンツスタジオの Script 画面で見て管理します。左側のメニューで Script を押すと、これまでに作った Script が一覧で表示されます。一覧の各行には、名前、Endpoint(呼ぶ方式とアドレスが一緒に表示されます)、匿名呼び出し の許可の有無、更新日時、更新者が表示されます。

Script 一覧画面。名前・Endpoint・匿名呼び出し・更新日時・更新者の列と一緒に、「商品説明の入力」が一覧に表示されている状態

定義はふつう AI エージェントが代わりに作ってくれますが、この画面で自分で作ることもできます。新しい Script は、一覧の右上の 作成 ボタンで作ります。

  1. 一覧の右上の 作成 ボタンを押してください。

  2. 名前 欄に 商品説明の入力 を入力してください。

  3. HTTP メソッド を、この Script を呼ぶ方式(ここでは POST)に選んでください。

  4. Statement 欄に、やることの一覧を入れてください。上の「商品説明の入力」の例の定義から、statements の値(角かっこ [ で始まる一覧)を入れれば大丈夫です。例の url は説明のために作った架空のアドレスなので、実際に呼ぶサービスのアドレスに置き換えてください。

  5. シークレット値(secrets)を入れる Secrets セクションで、Secret を追加 ボタンを押してください。

  6. 新しくできた行の Key に AI_WRITER_TOKEN、値 に実際の鍵の値、説明 に AI 文章作成サービスの鍵 を入力してください。

    新しい Script 作成画面の Secrets セクション。Key に AI_WRITER_TOKEN、値は点で隠された状態、説明に「AI 文章作成サービスの鍵」を入れた一行と、その下の「Secret を追加」ボタンが表示されている状態

作成画面にはこのほかに、直接呼び出し チェックボックス(チェックを入れないと呼び出し URL からは呼べず、Webhook や Scheduler を通してのみ実行されます。既定でチェックが入っています)、匿名呼び出し チェックボックス(チェックを入れると、Weegloo のログインなしでも第三者が匿名呼び出しのアドレスから呼べます。既定ではチェックが入っていません)、そして 呼び出し URL(標準呼び出し・匿名呼び出し の二行で、保存してはじめて決まるので今は空です)の欄があります。

新しい Script 作成画面。名前「商品説明の入力」、直接呼び出しのチェックボックスにチェックあり、匿名呼び出しのチェックボックスにチェックなし、HTTP メソッド POST、呼び出し URL の二行は空、Payload Schema のチェックボックスにチェックなし、Statement 欄にやることの一覧(statements)を入れた状態

呼ぶときに一緒に送る入力を、実行前に検査したい場合は、Payload Schema のチェックボックスにチェックを入れて、検査する形式を書いておきます。すべて埋めたら、右上の 作成 ボタンを押してください。

一覧から Script を一つ押すと、詳細画面が開きます。詳細画面は 実行ログ と 設定 の二つのタブに分かれ、最初に開くと 実行ログ が表示されます。設定 タブで名前と定義を確認し、右側でこの Script の番号(ID)と バージョン、変更履歴(作成日時・作成者・更新日時・更新者)を一緒に見ます。定義を直したあと 保存 すると、バージョンが一つ上がります。もう使わない Script は 削除 で消します。

商品説明の入力 Script 詳細の設定タブ画面。名前と Statement 定義、埋まった呼び出し URL、右側に ID・バージョン・変更履歴が見える状態

実行ログ タブには、この Script が実際に実行された記録が一行ずつ積み重なります。一行ごとに 実行時刻、何がその実行を始めたのか(トリガー)、結果、所要時間、そしてその実行を指す リクエスト ID が表示されます。上の 結果 の欄で、成功したものだけ、または失敗したものだけを選んで見ることができ、ログを更新 を押すと、たった今実行された記録まで読み直します。

記録は長く残りません。成功した実行は1時間、失敗した実行は3日後にひとりでに消えます。成功したほうが先に消えるので、一覧に失敗ばかりが残って見えるときがあります。

商品説明の入力 Script 詳細の実行ログタブ画面。保存期間の案内と結果のフィルター、実行時刻・トリガー・結果・所要時間・リクエスト ID の列があり、残った記録がなく一覧が空の状態

次にすること

  • Script 概要: Script を成す定義の最上位の構造と実行規則、文法のドキュメント一式を扱います。
  • Statement カタログ: statements に入れられる動作(リソースの作成・読み取り・修正・削除、外部サービスの呼び出し、条件・繰り返しなど)の種類とフィールドを扱います。
  • Webhook: 商品が登録されると Script がひとりでに実行されるようつなぐように、決めておいた変化が起きると自動で反応させる方法を扱います。