はじめに
この記事は ServiceNow アドベントカレンダー 2025(第2弾)の12月25日分として執筆しています。
免責事項
- この記事の内容は個人の趣味によるものであり、筆者が所属する企業、団体とは一切関係ありません。
- また、この記事は電気工作知識ゼロの人間がほぼChatGPTとのやり取りだけで実装したものです。参考にする場合はくれぐれも自己責任・自己判断でお願いします。
君は LiquorBot を知っているか?
いきなりですが、皆さんは "LiquorBot" って知ってますか?
おそらくタイトルに釣られてやってきた "ServiceNow is 何" な方々はもちろん、ServiceNow 界隈でも知っている人は(ServiceNow の社員を除くと)比較的少ないんじゃないでしょうか。
こんなのです。
LiquorBot は、ServiceNow を使ってカクテルを作ってくれる バーテンダーロボットです。
アプリケーション開発プラットフォームの側面を持つ ServiceNow の様々な機能をふんだんに利用しており、
- 注文管理、レシピ情報、在庫追跡などを管理する複数のテーブルで構成されており、
- UIビルダーで作られた画面からユーザーがカクテルを選ぶと、
- 内部のワークフローが外部マシン(ポンプを制御する装置)に REST API で命令を送って、
カクテルを作ってくれる……といった仕組みのようです。1
ともあれ、酒好きにとって一家に一台は欲しいこの装置を自分で作ってみよう! というのが今回の趣旨になります。
作ってみた
とりあえずポンプは最小構成の2台で、ハイボールを作る LiquorBot もどきを構築してみました。
まだまだ開発途上ですが、理屈の上ではポンプは最大4つまで増やせる拡張性を有しています。やったー!LiquorBotもどき出来たよー!\(^o^)/#ServiceNow #BuildAgent pic.twitter.com/Z4Y2Ep53NH
— キクチユウ (@daifuku085) December 23, 2025
用意するもの
冒頭で述べたとおり、電子工作なんて中学の授業以来でどこから手を付けたらいいやら状態でしたが、そこは ChatGPT に一つひとつ不明点を紐解いてもらいながら材料を揃えました。
いつも思うが、LLM は進捗率 0% をとにかく 5〜10% くらいまで上げなきゃいけないときに有能すぎる。
| 必要なもの | 備考 | 用意したもの |
|---|---|---|
| Raspberry Pi | - | 初代 B+(家に転がっていたものを流用) |
| ペリスタポンプ | 食品グレード / 毎分 200mL 前後の流量 | 蠕動性ポンプ 12 v DC ミニ実験室大流量 220 ml/分チューブポンプKamoer KHPP260 自吸循環ポンプ小さな液体投与ポンプ |
| シリコンチューブ | 食品グレード / 2~3m 程度 / 内径,外径はポンプに合わせて | タイガースポリマー シリコンチューブ カット品 4mm×6mm 1M |
| 12V DC アダプタ | ポンプに合わせて 12V / 3~4A | Powseed 12V 3A アダプター 電源 12V 互換用 ルーター CCTVカメラ スピーカー USBタブレットLEDストリップ ルーター液晶モニターに対応 dc适配器 充電器 3000mA 最大出力36W |
| DCジャック→ネジ止め端子変換アダプタ | - | (上記のACアダプタに付属) |
| リレーモジュール | 2ch以上 / 5V駆動 / Raspberry Pi 対応のもの | DC 5V 4チャンネル 継電器モジュール フォトカプラ付き Arduino対応 Raspberry Pi対応 PIC AVR DSP ARM |
| 配線用ケーブル | 12V側で使用 / 0.5~0.75sq 程度の撚り線 | エーモン(amon) ダブルコード自動車用(赤/黒) 0.50sq 6m (平行線 配線コード電線 延長ケーブル ハーネス) 4942 販売ルート限定品 |
| 端子台 or ワンタッチ式コネクタ | 12V | エーモン(amon) プラス・マイナス分岐ターミナル ワンタッチ接続 3360 黒 |
| ジャンパワイヤ | GPIO側で使用 / メス-メス | ブレッドボード・ジャンパーワイヤー(メス-メス)(20cm)40本 |
| 絶縁テープ、はんだごて、はんだ | - | (家にあったものを使用) |
気がついたら1万以上注ぎ込んでいた……。
その他、ソフトウェア側は以下のとおりです。
- Raspberry Pi OS
- Python 3
- Flask
- gpiozero
- Docker Desktop (M1 Mac)
- ServiceNowの個人開発用インスタンス(バージョンZurich)
全体構成
ServiceNow → MIDサーバー → Raspberry Piまで
Webブラウザから ServiceNow を操作すると、ServiceNow が自宅にある Raspberry Pi 上の API エンドポイントを叩く仕組みとなっているわけですが、実際には Docker上に構築したMIDサーバー(中継サーバー)が定期的に ServiceNow へポーリングをする ため、ポートの穴開け等は不要です。
Raspberry Pi → リレーモジュール → ポンプ周辺
Raspberry Pi から直接 12V のポンプを駆動することはできないため、リレーモジュールを介して制御します。
12V の電源は別途用意した AC アダプタから供給し、その電源ラインをリレーモジュールがスイッチのように ON/OFF します。
- 分岐ターミナルを 12V に接続
- 分岐ターミナルのプラス側をリレーモジュール #1, #2 の
COMに接続 - リレーモジュールの
NO端子とペリスタポンプをつなぐ(通常オープン → 必要なときだけRasPiでクローズ) - ペリスタポンプのもう一方を、分岐ターミナルのマイナス側につなぐ
RasPiはリレーモジュールの ON/OFF を制御します。
以下は実際に ChatGPT とやり取りした内容ですが、最近は具体的な配線まで教えてくれるんですね! 一応確認しました2が、回答内容に間違いありませんでした。
製作手順
Raspberry Pi 上にFlask で API サーバを立て、次に ServiceNow とのやり取りを中継する MID サーバーを Docker 上に構築します。
最後に ServiceNow 側のアプリ開発ですが、今回は折角バージョン Zurich を使用するので ServiceNow の開発AIエージェントである "Build Agent" を使用してみました。
Raspberry Pi 上に Flask サーバ構築
依存パッケージのインストール
RasPi に SSH でログインし、 venv で Python 仮想環境を構築します。
sudo apt upgrade
sudo apt-install -y python3-venv python3-pip
mkdir -p ~/liquor-bot
cd ~/liquor-bot
python3 -m venv venv
環境をアクティベートし、Flask および gpiozero(GPIO制御用)パッケージをインストールします。
source venv/bin/activate
pip isntall --upgrade pip
pip install flask gpiozero
app.py 作成
今回、スクリプトの作成は ChatGPT に丸投げしました。
ポンプにそれぞれ1, 2という番号をつけ、どちらのポンプを何秒回すかを{"operations": [{"pump_id":1,"seconds":10},{"pump_id":2,"seconds":30}]}のようなJSON形式で渡します。
app.py(クリックして詳細を表示)
#!/usr/bin/env python3
from flask import Flask, request, jsonify
from gpiozero import OutputDevice
from time import sleep
from threading import Thread
# pump_id -> GPIO(BCM) マップ
PUMP_PIN_MAP = {
1: 17,
2: 27,
}
# active_low=True: on()=LOW, off()=HIGH (リレーがアクティブロー前提)
PUMPS = {
pump_id: OutputDevice(pin, active_high=False, initial_value=False)
for pump_id, pin in PUMP_PIN_MAP.items()
}
MAX_SECONDS = 60.0 # 安全のための上限
app = Flask(__name__)
def run_pump(pump_id: int, seconds: float):
if pump_id not in PUMPS:
raise ValueError(f"Unknown pump_id: {pump_id}")
if seconds <= 0:
raise ValueError(f"seconds must be > 0 (got {seconds})")
if seconds > MAX_SECONDS:
seconds = MAX_SECONDS
dev = PUMPS[pump_id]
dev.on()
sleep(seconds)
dev.off()
def run_pump_operations(operations):
"""
別スレッドで呼び出されるワーカー。
operations 例:
[{"pump_id":1,"seconds":10},{"pump_id":2,"seconds":30}]
"""
try:
for op in operations:
pump_id = int(op["pump_id"])
seconds = float(op["seconds"])
run_pump(pump_id, seconds)
except Exception as e:
# 必要ならここでログに出したりファイルに書いたりする
print(f"[PumpWorker] Error: {e}")
@app.route('/drink/cocktail', methods=['POST'])
def drink_cocktail():
"""
期待する JSON:
{"operations": [{"pump_id":1,"seconds":10},{"pump_id":2,"seconds":30}]}
"""
try:
data = request.get_json(force=True)
except Exception as e:
return jsonify({"status": "error", "message": f"Invalid JSON: {e}"}), 400
if isinstance(data, dict) and "operations" in data:
ops = data["operations"]
else:
return jsonify({
"status": "error",
"message": "Payload must be a JSON array or an object with an 'operations' array."
}), 400
if not ops:
return jsonify({"status": "error", "message": "No operations found."}), 400
# バリデーションだけ軽くやっておく
norm_ops = []
for op in ops:
try:
pump_id = int(op.get("pump_id"))
seconds = float(op.get("seconds"))
except Exception:
return jsonify({
"status": "error",
"message": f"Invalid operation format: {op}"
}), 400
norm_ops.append({"pump_id": pump_id, "seconds": seconds})
# ここで別スレッドでポンプ動作開始(Flask のレスポンスは待たない)
t = Thread(target=run_pump_operations, args=(norm_ops,))
t.daemon = True
t.start()
# すぐレスポンスを返す(ServiceNow はこれを見て「成功」と判断する)
return jsonify({
"status": "accepted",
"message": "Pumps scheduled",
"operations": norm_ops
}), 200
@app.route('/health', methods=['GET'])
def health():
return jsonify({"status": "ok"}), 200
if __name__ == '__main__':
try:
app.run(host='0.0.0.0', port=5000, debug=False)
finally:
for dev in PUMPS.values():
dev.close()
後ほど MID サーバー構築後に ServieNow から疎通確認をするのですが、REST API 呼び出し後に30秒以上応答がないと ServiceNow のシステムログに
Error: Communication error: No response for ECC message request with sysid=... after waiting for 30 seconds in ECC Queue というエラーが出たので、暫定措置としてFlask 側ではHTTPレスポンスは即返し、別スレッドでポンプを動かす仕様にしてあります。
app.py を実行
python3 app.py
別の端末からテストを実施して、想定どおり動作することを確認します。
RasPiのIPアドレス部分は適宜書き換えてください。
curl -X POST http://<RasPIのIPアドレス>:5000/drink/cocktail \
-H "Content-Type: application/json" \
-d '{"operations": [{"pump_id":1,"seconds":3},{"pump_id":2,"seconds":3}]}'
Docker on M1 Mac に MID サーバー構築
PDI 側作業
MIDサーバーユーザーを作成し、MID Server > Downloads からLinux(ZIP)版をダウンロードします。
ここの手順はアドカレ4日目の記事を参考にさせていただきました。
M1 Mac側作業
あらかじめDocker Desktopはインストール&起動しておきます。
コンソールを開き、Mac 上に作業用ディレクトリを作成して、ダウンロードしたMIDのイメージを解凍します。
mkdir -p ~/midserver-docker
cd ~/midserver-docker
cp ~/Downloads/mid.*.linux.x86-64.zip ./mid.zip
unzip mid.zip
解凍したディレクトリの中にある agent/config.xml を編集します。
- url
- mid.instance.username
- mid.instance.password
- name
作業ディレクトリの直下に Dockerfile を作成
cd ~/midserver-docker
cat > Dockerfile << 'EOF'
FROM ubuntu:22.04
# 非対話モード & ロケール
ENV DEBIAN_FRONTEND=noninteractive \
LANG=ja_JP.UTF-8 \
LC_ALL=ja_JP.UTF-8
RUN apt-get update && \
apt-get install -y locales ca-certificates && \
locale-gen ja_JP.UTF-8 && \
rm -rf /var/lib/apt/lists/*
# MID Server ファイルをコピー
# (事前に ./mid 配下に agent ディレクトリなどが展開されている前提)
COPY mid /opt/mid
WORKDIR /opt/mid/agent
# ログだけホストに残したければ volume にしてもOK
VOLUME ["/opt/mid/agent/logs"]
# コンソールモードで MID を起動
CMD ["bin/mid.sh", "console"]
EOF
Dockerイメージをビルド
docker build \
--platform=linux/amd64 \
-t sn-mid-zurich .
コンテナを起動します
docker run \
--platform=linux/amd64 \
--name sn-mid \
-it --rm
sn-mid-zurich
しばらくするとServiceNowの MID Server > Servers に、config.xml 内で定義した name のMIDサーバーが登録される(途中MIDサーバーが再起動する)ので、レコードを開いて関連リンクより Validate を実行します。
ServiceNow→MIDサーバ→RasPiの疎通確認は、アドカレ6日目の記事を参考に Flow Action を作成し、Testボタンから確認するのが手っ取り早くて簡単でした。
(※事前にDeveloperサイトで"ServiceNow IntegrationHub Standard Pack Installer"を入れる必要がありました)
途中でMIDサーバーが強制終了したり、消えない警告が残ったりしましたが、原因分析は後回しにして次の工程に進みます。
ServiceNow上にカスタムアプリ開発
ServiceNowの開発環境 "ServiceNow IDE" からBuild Agentを使って、カクテルを注文するカスタムアプリケーション "CocktailBot" を作成します。
Build Agentの概要については下記の内容を参考にしました。
PDIでは30日あたり10プロンプトまでという制約があるので、プロンプトの段階で詳細設計しておく必要があります。商用の場合も バイブコーディングしてたらすぐに底を着きそうな課金体系だった気がするので 考え方は同じなんじゃないでしょうかね。
ServiceNow コミュニティの こちらの記事 に掲載されている "Be descriptive" 版のプロンプトテンプレートをベースに、以下のプロンプトを流し込みました。
ポイントとしては、
-
Cocktailテーブルに定義されたカクテルをUI上から選択すると、Orderテーブルにレコードが作成される -
Orderレコードが作成されたらサーバ側処理を実行、(Script Include から REST Message を呼び出して) API 連携 - "Flow" や "Flow Action" は Build Agent だと作れなさそう だったので、使わないよう明示
- JSON の Body 部分は
RecipeテーブルとIngredient(材料)テーブルのレコードを参照して使用する(Recipeには液体の種類や量、Ingredientには主に使用するポンプと回転時間を定義) - サンプルとしてハイボールのレシピを登録すること
にはconfig.xmlで定義したnameを、 の部分は適宜書き換えてください。
実際に使用したプロンプト(クリックして展開)
You are building the following application. If any detail is missing, choose sensible defaults and proceed, then document what you assumed.
## Goal
- Primary objective: Provide a self-service “CocktailBot” experience where users can order a cocktail with a single click from a custom UI component, and the system records orders and cocktail recipes in custom tables and can trigger a Raspberry Pi via a MID server.
- Success criteria:
1. A user can open a CocktailBot UI (for example, a Workspace or UI Builder page) that lists active cocktails.
2. From that UI, the user can click an “Order” style control on a cocktail card/row and an x_cb_order record is created and linked to that cocktail.
3. Orders are stored with a simple state (requested, in_progress, completed, error) and can be viewed in list and form UIs.
4. Admins can maintain cocktail recipes (ingredients and mL amounts) and ingredient pump settings (pump_id, ml_per_sec) without having to change any UI logic.
5. A server-side automation path exists that, when an order is created, can compute pump run-time and call a Raspberry Pi endpoint through a MID server using Business Rules, Script Includes, and a Scripted REST API (no Flow Designer or IntegrationHub actions).
6. Admins and users can view lists of orders, cocktails, ingredients, and recipes from the application menu.
- Non-goals:
- No physical device provisioning or GPIO-level control logic on the ServiceNow side; that lives on the Raspberry Pi.
- Do not use Flow Designer flows or IntegrationHub actions in this build; server-side scripting (Business Rule, Script Include, Scripted REST API, RESTMessageV2 with MID) is preferred instead.
- No Service Catalog items are required in this build.
- No complex reporting dashboards; simple list views are enough for v1.
## App Identity
- App name: CocktailBot
- Data classification: internal (demo/testing only).
- Themeing: Default platform theming is fine; use clear labels and simple forms over heavy styling.
## What the App Must Do
- Core capabilities:
1. Store cocktails in a custom x_cb_cocktail table, with a simple “active” flag to control which cocktails can be ordered.
2. Store each order in a custom x_cb_order table and link it to a specific cocktail.
3. Store ingredients (with pump_id and ml_per_sec) in x_cb_ingredient as reusable records that represent “physical liquid lines”.
4. Store cocktail recipes as reusable records in x_cb_recipe, linking cocktails to ingredients with an mL amount.
5. Provide a simple “one-click order” UI (for example, a Workspace / UI Builder page and/or list/form UI action) where the user can see active cocktails and click once to create a related x_cb_order.
6. Provide simple list UIs so admins can view and edit orders, cocktails, ingredients, and recipes.
7. Provide a server-side integration path so that when an order is created, business logic can calculate pump run-time per ingredient and call a Raspberry Pi HTTP endpoint via a MID server using Script Includes and RESTMessageV2.
- SLAs or service targets:
- For this demo, treat orders as auto-approved. No explicit SLA records are required.
- Reporting needs:
- Basic list filtering and sorting on x_cb_order (by state, cocktail, opened date).
- No advanced analytics required for this version.
## Data Model (Tables, fields, relationships)
Create the following tables. Use sensible dictionary attributes and reference integrity. Unless stated otherwise, you may omit standard task fields; keep the schema simple and focused on the use case.
[TABLE 1]
- Table label and name: Order, x_cb_order
- Extends: none
- Fields:
- number (type: string) [let the platform generate a standard Number field]
- cocktail (type: reference to x_cb_cocktail, required: yes, notes: the selected cocktail for this order)
- state (type: choice, required: yes, default: requested, notes: order status)
- result (type: string, required: no, max length: 4000, notes: store response or error details from the Raspberry Pi call)
- Choices:
- state: requested=Requested, in_progress=In Progress, completed=Completed, error=Error
[TABLE 2]
- Table label and name: Cocktail, x_cb_cocktail
- Extends: none
- Fields:
- name (type: string, required: yes, max length: 40, display value, notes: cocktail name such as Highball)
- active (type: boolean, required: yes, default: true)
- Relationships:
- One-to-many: x_cb_cocktail has many x_cb_recipe children (x_cb_recipe.cocktail reference).
- Sample records:
- Create exactly one sample cocktail record:
- name = "Highball", active = true.
[TABLE 3]
- Table label and name: Ingredient, x_cb_ingredient
- Extends: none
- Fields:
- name (type: string, required: yes, display value, notes: ingredient name such as Whiskey or Soda)
- pump_id (type: integer, required: yes, notes: pump channel number used by the Raspberry Pi)
- ml_per_sec (type: number, required: yes, notes: pump output rate in mL per second for this ingredient/line)
- Relationships:
- Ingredients will be linked to cocktails through x_cb_recipe (see TABLE 4).
- Sample records:
- Create sample ingredient records as follows:
- name = "Whiskey", pump_id = 1, ml_per_sec = 4
- name = "Soda", pump_id = 2, ml_per_sec = 4
[TABLE 4]
- Table label and name: Recipe, x_cb_recipe
- Extends: none
- Fields:
- cocktail (type: reference to x_cb_cocktail, required: yes, notes: which cocktail this recipe row belongs to)
- ingredient (type: reference to x_cb_ingredient, required: yes, notes: which ingredient is used)
- ml (type: integer, required: yes, notes: how many mL of this ingredient to dispense)
- Relationships:
- Many-to-one: multiple x_cb_recipe records may reference the same x_cb_cocktail.
- Many-to-one: multiple x_cb_recipe records may reference the same x_cb_ingredient (if reused across cocktails).
- Sample records:
- Create sample recipe records that automatically link to the sample Cocktail and Ingredient records created above:
- cocktail = "Highball", ingredient = "Whiskey", ml = 40
- cocktail = "Highball", ingredient = "Soda", ml = 120
## UI
- CocktailBot main UI:
- Create a simple UI component or page (for example, a Workspace or UI Builder page) under the CocktailBot application where:
- Active cocktails (x_cb_cocktail where active = true) are displayed in a list or card layout.
- Each cocktail has an “Order” style control (button or UI action).
- Clicking that control creates an x_cb_order record linked to that cocktail, sets state = requested, and shows a confirmation or navigates to the created order.
- This UI should be usable by normal requesters without needing access to table administration.
- Optional list/form actions:
- It is acceptable to implement the one-click behavior as:
- A UI action on the Cocktail form or list to “Create order”, or
- A button or component on a Workspace / UI Builder page that calls a server script to insert x_cb_order.
- Choose the simplest mechanism supported by this environment and document what you chose.
## Automation (server-side logic and MID integration)
- Do NOT use Flow Designer or IntegrationHub actions; instead, use classic server-side scripting.
- Implement the following:
- A Script Include (for example, `CocktailBotEngine`) with a public API such as `processOrder(orderSysId)` that:
- Looks up the x_cb_order record and its related x_cb_cocktail.
- Queries x_cb_recipe for that cocktail and, for each recipe row, looks up:
- the related x_cb_ingredient,
- ingredient.name,
- ingredient.pump_id,
- ingredient.ml_per_sec,
- and recipe.ml.
- For each recipe row, computes `seconds = ml / ml_per_sec`.
- Builds a JSON payload including:
- order number,
- cocktail name,
- an array of operations like `{ "pump_id": ingredient.pump_id, "seconds": computedSeconds, "ingredient": ingredient.name }`.
- Uses `RESTMessageV2` to POST the payload to the Raspberry Pi HTTP endpoint via a MID server:
- MID server name: `<MIDサーバ名>`
- Endpoint URL: `http://<RasPiのIPアドレス>:5000/drink/cocktail` (you may assume this for the sample).
- Method: POST.
- Interprets the HTTP response:
- On HTTP 200, update x_cb_order.state to `completed` and optionally store the response body in x_cb_order.result.
- On non-200 or technical error, update x_cb_order.state to `error` and write the error/response text into x_cb_order.result.
- A Business Rule on x_cb_order that:
- Runs on insert (and only when state = requested).
- Calls the Script Include method (for example, `new CocktailBotEngine().processOrder(current.sys_id);`).
- Scripted REST API:
- Create a simple Scripted REST API (for example, namespace `x_cb` and API name `cocktailbot`) with at least one resource that can accept status callbacks or test calls from the Raspberry Pi.
- For this version, it is sufficient if the Scripted REST API:
- Accepts a JSON payload with an order number and a status.
- Looks up the related x_cb_order and optionally updates its state and/or result.
- Document any assumptions you make about how this Scripted REST API might be used in future (for example, asynchronous callbacks).
## User Experience
- Requesters:
- Use the CocktailBot UI page/component.
- They should be able to:
- See a list of active cocktails.
- Click an “Order” button next to a cocktail to create an order immediately.
- After creation, the requester can view the order record on standard pages or be navigated there by the UI.
- Admins:
- Use an application menu “CocktailBot” under the CocktailBot scope.
- Provide list modules for:
- Orders (x_cb_order)
- Cocktails (x_cb_cocktail)
- Ingredients (x_cb_ingredient)
- Recipes (x_cb_recipe)
- Admins can:
- Add and edit cocktails.
- Add and edit ingredients (including pump_id and ml_per_sec).
- Add and edit recipes for each cocktail.
- Inspect orders and, in future versions, use state and result for troubleshooting.
## Output Required From You (the build agent)
Provide at the end:
1) A build log of everything created (apps, tables, fields, UI components/pages, UI actions, Business Rules, Script Includes, Scripted REST APIs, and their paths).
2) A quick start checklist describing how a CocktailBot admin and a requester should use the app on day one.
3) Any assumptions you made about field types, defaults, security, sample data, or the UI and automation mechanisms you chose.
## Constraints and Defaults
- Do not create Flow Designer flows, IntegrationHub actions, or Service Catalog items in this build; table data, UI, and classic server-side scripting are sufficient. Future automation changes can be made manually.
- Prefer simple, readable field names and labels over very long ones.
- If a design choice is not specified, choose the simplest option that will work for a demo on a PDI and clearly document that choice in your assumptions.
何度か試行しましたが、このくらいの規模だと当たりを引く確率は3〜4割くらいかなぁというのが個人的な所感です。特に義務教育でReactを未履修だった弱々エンジニアは、UI周りで致命的なエラーが出ると死ねます。
比較的当たりだった場合もモック未満のレベルそのままでは動作しないため、修正が必須となります。が、PDI 環境のためか Build Agent はまだまだポンコツ君なので、自力でデバッグするか他のコーディングに強いLLMとペアプログラミングすることになるでしょう。
ちなみに今回の例としては、実行後システムログに
Business Rule: Error processing order … : undefined is not a function.というエラーが発生→よくよく調べてみたら生成されたBusiness Rule内で呼び出している Script Include 内に誤記がありました。
Script Include の1行目で RESTMessageV2 を import しようとして失敗していたので、コメントアウト
スクリプト内で RESTMessageV2 となっている部分も sn_ws.RESTMessageV2 といういつもの表記に修正
それでもうまく連携できないので調べてみたらそもそも REST Message が作られてない! 仕方ないので自分で登録しました。Build Agent はまだ対応していないんでしょうか。
OUTPUT

注文画面。Recipe の情報はテーブルから読み込んでいると思っていたら、
まさかのハードコーディングでした。 後で直さないと。。。

こちらは履歴画面。特に指示しなくても必要そうな機能を用意してくれるのは嬉しい。
まとめ
去年のアドカレは「ほぼ ChatGPT4o との対話のみで ServiceNow 上にインタラクティブなビンゴアプリを開発する」という、いわゆるバイブコーディングの事例を投稿してみましたが、今回はそのノリをハードの領域に適用してみました。
思いのほか短期間で作成できました。ChatGPT は1年でめちゃめちゃ賢くなってますね。






