9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RaspberryPi & ServiceNowでバーテンダーロボットを自作した話

Last updated at Posted at 2025-12-25

はじめに

この記事は ServiceNow アドベントカレンダー 2025(第2弾)の12月25日分として執筆しています。

免責事項

  • この記事の内容は個人の趣味によるものであり、筆者が所属する企業、団体とは一切関係ありません。
  • また、この記事は電気工作知識ゼロの人間がほぼChatGPTとのやり取りだけで実装したものです。参考にする場合はくれぐれも自己責任・自己判断でお願いします。

君は LiquorBot を知っているか?

いきなりですが、皆さんは "LiquorBot" って知ってますか?
おそらくタイトルに釣られてやってきた "ServiceNow is 何" な方々はもちろん、ServiceNow 界隈でも知っている人は(ServiceNow の社員を除くと)比較的少ないんじゃないでしょうか。

こんなのです。

Knowledge23@LasVegasにて撮影

LiquorBot は、ServiceNow を使ってカクテルを作ってくれるバーテンダーロボットです。
アプリケーション開発プラットフォームの側面を持つ ServiceNow の様々な機能をふんだんに利用しており、

  • 注文管理、レシピ情報、在庫追跡などを管理する複数のテーブルで構成されており、
  • UIビルダーで作られた画面からユーザーがカクテルを選ぶと、
  • 内部のワークフローが外部マシン(ポンプを制御する装置)に REST API で命令を送って、

カクテルを作ってくれる……といった仕組みのようです。1

ともあれ、酒好きにとって一家に一台は欲しいこの装置を自分で作ってみよう! というのが今回の趣旨になります。

作ってみた

とりあえずポンプは最小構成の2台で、ハイボールを作る LiquorBot もどきを構築してみました。

まだまだ開発途上ですが、理屈の上ではポンプは最大4つまで増やせる拡張性を有しています。

用意するもの

冒頭で述べたとおり、電子工作なんて中学の授業以来でどこから手を付けたらいいやら状態でしたが、そこは 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 します。

  • アダプタに分岐ターミナルを繋ぐ(途中にネジ止め端子変換アダプタを噛ませる)
  • 分岐ターミナルのプラス側をリレーモジュール #1, #2 の COM に接続
  • リレーモジュールの NO 端子とポンプを繋ぐ (通常は回路をオープン(Normally Open) → 必要なときだけRasPiがクローズ)
  • ポンプのもう一方を、分岐ターミナルのマイナス側に繋ぐ

ポンプと配線用ケーブルを繋ぐ箇所のみ、はんだごてを使用しました。

リレーモジュールとポンプの配線

RasPi はリレーモジュールの ON/OFF を制御します。
以下は実際に ChatGPT とやり取りした内容ですが、最近は具体的な配線まで教えてくれるんですね!

ChatGPTの回答内容

一応確認しました2が、回答内容に間違いありませんでした。

Raspberry Piとリレーモジュールの配線

製作手順

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 ~/cocktail-bot
cd ~/cocktail-bot

python3 -m venv venv

環境をアクティベートし、Flask および gpiozero(GPIO制御用)パッケージをインストールします。

source venv/bin/activate

pip install --upgrade pip
pip install flask gpiozero

Flask アプリの作成

今回、スクリプトの作成は ChatGPT に丸投げしました。

app.py(クリックして展開)
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) マップ
# GPIO17 → IN1, GPIO27 → IN2 に接続している前提
PUMP_PIN_MAP = {
    1: 17,
    2: 27,
}

# active_high=Trueの場合: on()=HIGH, off()=LOW
# 多くの中華リレーは「LOWでON」なので active_high=False にします
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()

ポンプにそれぞれ 1, 2 という番号をつけ、どのポンプを何秒回す必要があるかを
{"operations": [{"pump_id":1,"seconds":10},{"pump_id":2,"seconds":30}]}
のようなJSON形式で渡します。
今回使用したペリスタポンプは検証の結果1秒間に 4mL 注ぐことが確認できたので、上記の例だとポンプ#1 が 40mL, ポンプ#2 は 120mL という計算になります。

後ほど 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 レスポンスは即返し、ポンプは別スレッドで動かす仕組みにしてあります。

アプリの実行

作成した Flask アプリを実行します。

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 サーバー構築

ServiceNow 側の作業

ServiceNow 上に MID サーバー用ユーザーを作成し、アプリケーションナビゲーターの [MID Server] > [Downloads] から Linux(ZIP) 版の MID サーバーをダウンロードします。
ここの手順は アドカレ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 -d mid

解凍したディレクトリの中にある mid/agent/config.xml の下記4箇所を書き換えます。

  • url : 開発用インスタンス(PDI)の URL
  • mid.instance.username : PDI に作成した MID サーバー用ユーザー名
  • mid.instance.password : PDI に作成した MID サーバー用ユーザーのパスワード
  • name : 任意の MID サーバー名(←ServiceNow 側のカスタムアプリ開発時に使います)

作業ディレクトリの直下に Dockerfile を作成 (※以下をコンソールにコピペ)

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

sn-mid-zurich という名前で 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 を作成3し、Testボタンから確認するのが手っ取り早くて簡単でした。

途中でMIDサーバーが強制終了したり、警告が消えずにずっと残っていたりしたのですが、早く動かしたいので原因の特定は後回しにして次に進みます。

ServiceNow 上にカスタムアプリ開発

ServiceNow の開発環境 "ServiceNow IDE" から Build Agent を使って、カクテルを注文するカスタムアプリケーション「CocktailBot」を作成します。

Build Agent による開発

Build Agent の概要や使い方は以下の記事が参考になりました。

PDIでは30日あたり10プロンプトまでという制約があるので、プロンプトの段階で詳細設計しておく必要があります。商用の場合も バイブコーディングしてたらすぐに底を着きそうな課金体系だった気がするので 考え方は同じなんじゃないでしょうか。

詳細は後述のプロンプトを LLM に要約させてくださいなのですが、主に以下を意識的に盛り込むようにしました。

  1. データモデル(Order, Cocktail, Recipe, Ingredientの4テーブル構成。役割や関連性、フィールド名まで詳細に)
  2. カスタムアプリ内で完結させる(サービスカタログや要求管理は使用しない
  3. Flask サーバとの責任分界点(GPIO レベルのロジックは Flask 側に持たせる。CocktailBot は"どのポンプを何秒回すか"の提供まで)
  4. 実装方法(Flow ではなくScript Include と RESTMessageV2 を使うように明記)4
  5. ユーザーがカクテルをワンクリックで注文できるシンプルな UI の提供
  6. 例としてハイボールの作成に必要なレコードを自動で登録すること

使用したプロンプト

ServiceNow コミュニティの こちらの記事 に掲載されている "Be descriptive" 版のテンプレートをベースに、以下のプロンプトを作成しました。
<MIDサーバ名><RasPiのIPアドレス> の部分は適宜書き換えてください。

実際に使用したプロンプト(クリックして展開)
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 }`.
    - 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 周りで致命的なエラーが出た時点でほぼほぼ詰みます。

比較的当たりだった場合でも、基本的にそのままでは動作しないためバグ改修が必須となります。が、Build Agent 君は まだまだポンコツ デバッグはまだちょっと早いかなぁ〜? というスキルレベルなので、自力で直すか他の LLM とペアプロするかのどちらかになるでしょう。

ちなみに今回の例としては、実行後システムログに Business Rule: Error processing order … : undefined is not a function. というエラーが発生していたので、Business Rule → Script Include → …… と順に原因を追っていきました。

Script Include の1行目で RESTMessageV2 を import しようとして失敗していたので、コメントアウト

cocktail-bot-engine

スクリプト内で RESTMessageV2 となっている部分も、いつものsn_ws.RESTMessageV2という表記に修正

cocktail-bot-engine2

それでもうまく連携できないので調べてみたら、そもそも REST Message レコードが作られてない! 仕方ないので自分で作成しました。
Build Agent はまだ REST Message に対応していないんでしょうか。

image3.png

OUTPUTイメージ

スクリーンショット 2025-12-24 5.36.51.png
注文画面。
レシピ情報は Recipe テーブルから読み込んでくれていると思っていたら……。

CocktailCard.png

「難しいからハードコーディングしとくね」(超意訳) だそうです。後で直さないと。。。

スクリーンショット 2025-12-24 5.37.36.png
こちらは注文履歴画面。エラーが起きたときもこの画面上で見られます。
特に指示しなくても、必要そうな機能を提供してくれるのは嬉しい。

おわりに

去年のアドカレでは「ほぼ ChatGPT4o との対話のみで ServiceNow 上にインタラクティブなビンゴアプリを開発する」という、いわゆるバイブコーディングの事例を投稿してみましたが、今回はそのノリをハードの領域にまで拡張してみました。

アドカレに間に合わせるためにテストやらバグ改修やら様々な工程を端折ったところもありますが、2年越しの野望をわずか10日程度で実現することができました。 ChatGPT は1年でめちゃめちゃ賢くなってますね。

  1. https://www.servicenow.com/community/developer-advocate-blog/hackin-the-hackzone-liquorbot-software/ba-p/3131331

  2. https://www.raspberrypi-spy.co.uk/2012/06/simple-guide-to-the-rpi-gpio-header-and-pins/

  3. 私の環境の場合、REST Step を使用するためには事前に Developer サイトから "ServiceNow IntegrationHub Standard Pack Installer" をインストールする必要がありました

  4. Flow や Flow Action は Build Agent だと作れなさそうだったので

9
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?