まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:nodered

Node-RED 環境に標準で提供されている標準ノードの1つに template ノードがあります(下図赤枠):
2024091001


この template ノードは次のノードへ送る msg.payload (など)の値をテンプレートをベースに、一部を変数化して生成するノードです。HTML の一部を変数化して扱いたいとか、最近だと生成 AI のプロンプトを作成する際のテンプレートとしても便利に使えるので、今後活用の機会があるかもしれません。

ところで、この template ノードの標準記法として設定されているのが mastache (「マスタッシュ」)記法というものです:
2024091002


この mastache 記法でどんなことができるのか調べてみました。以下で3つの方法を紹介しますが、とりあえず以下のようなシンプルなフローを作っておきます。inject -> function -> template -> debug の各ノードを一直線につないだだけのシンプルなフローです:
2024091003


このうち inject ノードは単に処理をスタートさせるためのスイッチとしてだけ使うのであまり意味はありません(このノードの左にある部分をクリックするとフローが実行されます)。また最後の debug ノードはデフォルトで msg.payload の内容を画面右側のデバッグウィンドウに出力するのですが、これもデフォルトのままで大丈夫。つまりこの2つは特に設定変更しません。

そして2つ目の function ノードをダブルクリックして編集状態にして、以下の内容を記載します:
msg = {
  payload: {
    value0: "0",
    value1: 1,
    value2: "<b>2.0</b>"
  },
  value1: 1,
  value2: 2.2,
  array1: [ 2, 3, 5, 7, 11, 13, 17, 19 ],
  array2: [
    { name: "apple" },
    { name: "orange" },
    { name: "grape" }
  ]
}
return msg;
2024091004


この3つのノード(inject, function, debug)はこの後特に変更しません。 以下で mastache 記法にちうて3つの方法を紹介しますが、全て上述の内容の msg を受け取る仮定として、 template ノードのみ変更して動作を確認していきます。


【テンプレート内に1つの値を指定する場合】
まずはシンプルな例を紹介します。template ノードを開き、以下の内容を記入してください:
payload.value0 = {{payload.value0}}
payload.value1 = {{payload.value1}}
payload.value2 = {{payload.value2}}

value1 = {{value1}}
value2 = {{value2}}
2024091101


そしてこの状態でフローをデプロイし、inject ノードをクリックしてフローを実行します。すると画面右のデバッグペインには以下のように表示されます:
payload.value0 = 0
payload.value1 = 1
payload.value2 = &lt;b&gt;2.0&lt;&#x2F;b&gt;

value1 = 1
value2 = 2.2

2024091102


msg オブジェクトの一部が変数としてテンプレートの中を埋めて出力されていることがわかると思います。普通の変数であれば {{ と }} でメッセージ内の値を指定することで表示できそうですね。

ちょっと気になる点があるとすれば "payload.value2" の値でしょうか。そのまま HTML で使えるように一部文字がエスケープされた状態になっています。このエスケープが不要である場合は {{ と }} ではなく、 {{{ と }}} で囲うようにするだけです。試しに template の内容を以下のように書き換えてみます:
payload.value0 = {{payload.value0}}
payload.value1 = {{payload.value1}}
payload.value2 = {{{payload.value2}}}

value1 = {{value1}}
value2 = {{value2}}
2024091103


そしてフローを再デプロイし、inject ノードで処理を実行すると、今度は文字列がエスケープされずにそのまま表示されます:
2024091104


【テンプレート内に配列値を指定する場合】
テンプレート内に1つ1つの値を個別に直接指定する方法は上述のような方法でした。では「配列」を扱う場合、テンプレート内にどのように記述するべきでしょうか? 今回の場合だと msg.array1 が配列になっているので、これをどのように列挙するか? というケースを考えます。

その答がこれです。template の内容を以下のように変更します:
{{#array1}}
{{.}}
{{/array1}}

2024091105


この template でデプロイして inject ノードをクリックして処理が実行されると、デバッグ画面に下図のような結果(msg.array1 の配列の内容が1行ずつ表示)が出力されます:
2024091106


このようにテンプレート内で {{#配列名}} と {{/配列名}} で括り、その中で {{.}} と指定すると配列内の各要素が表示されました。


【テンプレート内に JSON 配列を指定する場合】
最後にテンプレート内で「 JSON オブジェクトの配列」を扱う場合を考えます。 今回の場合だと msg.array2 が JSON 配列になっているので、これをどのように列挙するか? というケースを考えます。

その答がこれです。template の内容を以下のように変更します:
{{#array2}}
{{name}}
{{/array2}}

2024091107



この template でデプロイして inject ノードをクリックして処理が実行されると、デバッグ画面に下図のような結果(msg.array2 の JSON 配列の name 要素が1行ずつ表示)が出力されます:

2024091108

このようにテンプレート内で {{#配列名}} と {{/配列名}} で括り、その中で {{要素名}} と指定すると JSON 配列内の特定の要素を表示できました。


【まとめ】
Node-RED のテンプレートとしての template ノードはあまり使う機会がなかったのですが、うまく使うとプロンプトエンジニアリングのプロンプト作り(サンプル作り)のテンプレートに使えそうだな、という印象を受けています。この方法でサンプルを列挙するようなプロンプトを生成して、その結果を拙作の watsonxai ノードhttps://flows.nodered.org/node/node-red-contrib-dotnsf-watsonxai)に渡して問い合わせする、といった使い方ができるのではないかと考えています:
2024091109


mastache 記法はここで紹介した内容以外にもいろいろな記法があります。詳しくは以下のページを参照ください。とてもわかりやすく、自分の理解にも使わせていただいたページです。


【参照】
mustache記法について簡単にまとめてみた

先日このブログでも紹介した IBM watsonx.ai のベータ版 REST API を使って、Node-RED のカスタムノードを作り、公開してみました。以下で使い方を紹介しますが、実際に利用するには前回 REST API を利用する紹介をした時のブログでも作成した Watson Studuio と Watson Machine Learning を使ったプロジェクトを登録しておく必要があります(そのプロジェクトの ID が必要です)。またこれも同様に前回紹介しましたが、実行するユーザーの IBM Cloud IAM API キーを生成/取得しておく必要があるのでこちらも準備の上で試してみてください。

なお以下で紹介する内容では Node-RED v2.2.3 を使って動作確認しています。また私が提供しているものは公式のものではなく、あくまで個人的に開発したものを個人的に公開しているだけであること、現時点で提供されている REST API 自体がベータ版のため、今後近い将来の API 変更などにより動かなくなる可能性があることをご了承ください(とはいえ、私も使うので、少なくとも公式ノードが出るまではなるべくタイムリーにメンテナンスするつもりです)。


【パレットに watsonx.ai ノードを追加】
Node-RED のパレット画面を開き、右上のメニューから「パレットの管理」を選択してパレットのカスタマイズ画面に移動します。ここで「ノードを追加」タブを選び、"node-red-contrib-dotnsf-watsonxai" を検索してください(2023年7月20日時点では "watsonx" と検索すると1つだけ(!)見つかるのがそれです)。検索できたら「ノードを追加」ボタンをクリックします:
2023072001


以下のような確認ダイアログが表示されるので「追加」をクリックします:
2023072002


正しくインストールされると「ノードをパレットへ追加しました」というメッセージが表示されます。パレット管理画面を閉じます:
2023072003


元のパレット画面に戻ると、"watsonx.ai" というノードが追加されているはずです:
2023072004


【watsonx.ai ノードを使う】
では追加された watsonx.ai ノードを使ってみます。watsonx.ai ノードの前後に適当なノードを追加します。以下ではシンプルで分かりやすい代表として、inject ノードと debug ノードで挟んでみました:
2023072005



watsonx.ai ノードにデータを渡すノード(上図では inject ノード)からは、msg.payload にいわゆる「プロンプト」のテキストを指定します。watsonx.ai ノードは自分に送られてきたデータの msg.payload にプロンプト指定がされている前提でそのテキスト内容に書かれた指示の返答テキストを生成します。

以下の例では msg.payload の内容を(前回のブログで紹介した内容に合わせて)以下のようにしてみました:
入力:\nAbout Watson Discovery\\nIBM Watson® Discovery is an intelligent document processing engine that helps you to gain insights from complex business documents.\n翻訳\n

2023072006


入力内容が英語で書かれていて、最後に「翻訳(してね)」と指示している内容です。watsonx.ai ノードの手前のノード(inject ノード)の設定内容は以上です。

次に watsonx.ai ノード自体にも設定が必要です。watsonx.ai ノードをダブルクリックしてプロパティ設定画面を開き、API Key と Project ID をそれぞれ入力します:
2023072007


このうち API Key は IBM Cloud の IAM API キーの値で、Project ID は Watson Studio と Watson Machine Learning で作成したプロジェクトの ID です。これらも前回のブログで紹介したものと同じ内容の問い合わせを行うサンプルなので、この作業について詳しくはこちらも参照してください。


全ての設定が完了したら作成したフローを「デプロイ」します:
2023072008


デプロイ後に inject ノード左のボタンをクリックしてメッセージを送信すると、watsonx.ai ノードが API キー、プロジェクト ID 、そしてプロンプトテキスト内容を元に問い合わせを行い、その結果が画面右のデバッグ画面内に表示されます:
2023072009


私が 2023 年7月20日に試した際の結果は "Watson Discoveryはビジネスドキュメントに関する意見を得るための知能型ドキュメント処理エンジンです。" と表示されました。上述の英語文章を翻訳してほしい、というプロンプトだったので、質問意図を正しく理解して実行できているようでした。


(いずれにせよベータ版 API を使うことになりますが)REST API を直接実行することでカスタムアプリケーションを作ることもできますが、今回紹介した Node-RED のカスタムノードを使うことで、より手軽に watsonx.ai のテキスト生成機能を体験できるようになったのではないかと思っています。


IBM Cloud から提供されている 30 日間無料 Kubernetes サービスIBM Kubernetes Service 、以下 "IKS")環境を使って利用することのできるコンテナイメージを1日に1個ずつ 30 日間連続で紹介していきます。

環境のセットアップや制約事項については Day0 のこちらの記事を参照してください。

本日 Day 19 からはプログラミング・開発系コンテナとその GUI ツールを中心に紹介します。シリーズ初回となる Day 19 はローコード開発の雄 Node-RED イメージをデプロイする例を紹介します。
Node-red-icon



【イメージの概要】
多くのノーコード/ローコード開発環境が世に出ていますが、私の理解の中では最も広く使われている環境が Node-RED であると思っています。「ノード」と呼ばれるブロックを組み合わせてデータのフローを定義するだけで、その定義通りに動きだす、という視覚的にも理解しやすい GUI プログラミングを実現しています。 HTTP だけでなく MQTT や WebSocket といった多くのプロトコルにも対応していたり、自分で独自のノートを作って活用できる特徴もあって、ローコードでありながら IoT や AI といった広い分野での応用ができる点も特徴といえます。

開発元は英 IBM の Hursley 研究所で、現在は OpenJS Foundations 管理下でオープンソース化されています。


【イメージのデプロイ】
まずはこちらのファイルを自分の PC にダウンロードしてください:
https://raw.githubusercontent.com/dotnsf/yamls_for_iks/main/nodered.yaml

今回の Node-RED も特にパラメータ指定不要で、そのままデプロイすることができます。以下のコマンドを実行する前に Day 0 の内容を参照して ibmcloud CLI ツールで IBM Cloud にログインし、クラスタに接続するまでを済ませておいてください。

そして以下のコマンドを実行します:
$ kubectl apply -f nodered.yaml

以下のコマンドで Node-RED 関連の Deployment, Service, Pod, Replicaset が1つずつ生成されたことと、サービスが 30880 番ポートで公開されていることを確認します:
$ kubectl get all

NAME                           READY   STATUS    RESTARTS   AGE
pod/nodered-5fb585f656-7bdgm   1/1     Running   0          32s

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/kubernetes   ClusterIP   172.21.0.1     <none>        443/TCP          27d
service/nodered      NodePort    172.21.11.49   <none>        1880:30880/TCP   34s

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nodered   1/1     1            1           35s

NAME                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/nodered-5fb585f656   1         1         1       36s

この後に実際にサービスを利用するため、以下のコマンドでワーカーノードのパブリック IP アドレスを確認します(以下の例であれば 161.51.204.190):
$ ibmcloud ks worker ls --cluster=mycluster-free
OK
ID                                                       パブリック IP    プライベート IP   フレーバー   状態     状況    ゾーン   バージョン
kube-c3biujbf074rs3rl76t0-myclusterfr-default-000000df   169.51.204.190   10.144.185.144    free         normal   Ready   mil01    1.20.7_1543*

つまりこの時点で(上述の結果であれば)アプリケーションは http://169.51.204.190:30880/ で稼働している、ということになります。ウェブブラウザを使って、アプリケーションの URL(上述の方法で確認した URL)にアクセスしてみます:
nodered1


上図のような画面が表示され、Node-RED の作業準備ができていることがわかります。画面左のパレットメニューからノードを配置して、実際にデプロイして動かすことができます:
nodered2



【YAML ファイルの解説】
YAML ファイルはこちらを使っています:
apiVersion: v1
kind: Service
metadata:
  name: nodered
spec:
  selector:
    app: nodered
  ports:
  - port: 1880
    protocol: TCP
    targetPort: 1880
    nodePort: 30880
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodered
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nodered
  template:
    metadata:
      labels:
        app: nodered
    spec:
      containers:
      - name: nodered
        image: nodered/node-red
        ports:
        - containerPort: 1880

Deployment 1つと、Service 1つ、環境変数の指定も不要で本シリーズで紹介する 30 個の中でも指折りにシンプルな YAML ファイルです。一応解説を加えておきます。アプリケーションそのものは 1880 番ポートで動作するように作られているため、NodePort 30880 番を指定して、外部からは 30880 番ポートでアクセスできるようにしています(NodePort として指定可能な番号の範囲は 30000 ~ 32767 です、指定しない場合は空いている番号がランダムに割り振られます)。また ReplicaSet は1つだけで作りました。


デプロイしたコンテナイメージを削除する場合はデプロイ時に使った YAML ファイルを再度使って、以下のコマンドを実行します。不要であれば削除しておきましょう:
$ kubectl delete -f nodered.yaml


【紹介したイメージ】
https://hub.docker.com/r/nodered/node-red


【紹介記録】
Dayカテゴリーデプロイ内容
0準備準備作業
1ウェブサーバーhostname
2Apache HTTP
3Nginx
4Tomcat
5Websphere Liberty
6データベースMySQL
7phpMyAdmin
8PostgreSQL
9pgAdmin4
10MongoDB
11Mongo-Express
12Redis
13RedisCommander
14ElasticSearch
15Kibana
16CouchDB
17CouchBase
18HATOYA
19プログラミングNode-RED
20Scratch
21Eclipse Orion
22Swagger Editor
23R Studio
24Jenkins
25アプリケーションFX
262048
27DOS Box
28VNC Server(Lubuntu)
29Drupal
30WordPress

こんなのを作ってみました。これ自体がそのまま直接役立つとは思ってませんが、cron などのスケジュールジョブと合わせて使うことで時系列データを簡単に入手できるので、「生の解析用サンプルデータを用意する」のが比較的容易にできちゃうと思っています。

文字通りの「為替取得 REST API」です。エンドポイント URL はこちらです:
http://fx.mybluemix.net/
https://dotnsf-fx.herokuapp.com/

(2022/04/23 追記 上記 URL を変更しました)


REST API なので、何らかの(機械的な)HTTP クライアントからアクセスされることを想定しています。とりあえずデータ・フォーマットを確認する目的で、ウェブブラウザでアクセスしてみると、このような JSON テキストが得られるはずです:
2021061501


JSON テキストを整形するとこんな感じの内容です:
{
 "status":true,
 "result":{
  "datetime":"2021-06-16 14:48:27+0",
  "rate":{
   "USDJPY":109.961,
   "EURJPY":133.339,
   "EURUSD":1.21255,
   "AUDJPY":84.707,
   "GBPJPY":155.129,
   "NZDJPY":78.49,
   "CADJPY":90.334,
   "CHFJPY":122.216,
   "HKDJPY":14.158,
   "GBPUSD":1.4107,
   "USDCHF":0.89958,
   "ZARJPY":7.99,
   "AUDUSD":0.77031,
   "NZDUSD":0.71376,
   "EURAUD":1.57405,
   "TRYJPY":12.872,
   "CNHJPY":17.186,
   "NOKJPY":13.116,
   "SEKJPY":13.137,
   "MXNJPY":5.465
  }
 }
}

status が API の実行結果(true/false)で、成功した場合は result がその結果です。result.datetime が取得したタイミングの GMT 日時です( API を実行した瞬間の日時になっているはずです)。そして result.rate 内にそのタイミングでの(リアルタイムの)各通貨ペアの為替情報が格納されています。例えば上の例では result.rate.USDJPY = 109.961 となっていますが、これはこのタイミングで「1米ドル(USD)=109.961日本円(JPY)」だったことを表しています。同様にして result.rate.EURJPY = 133.339 なので「1ユーロ(EUR)=133.339日本円(JPY)」です。他の通貨表記についてはこのあたりを参照してください:
https://www.gaitame.com/gaitame/gaika/gaika_index.html


このように上記 URL に GET リクエストを発行するだけでリアルタイムな為替情報 20 ペア分が取得できるものです。後はこれを1分おきとか1時間おきに取得して、その結果を RDB なり、JSON DB なりに格納する、という処理を1~10日くらい動かしっぱなしにしておけば、それなりにまとまったデータが取得できるはずです。一応数値は本物の為替情報で、深く考えなくてもただ GET リクエストを実行すれば結果が取得できるので、解析元となるデータを集める上では比較的便利かな、と思っています。

これはあくまで一例ですが、Node-RED のインジェクションノードを定期実行する設定にした上でこんな感じのフローを作るだけで定期的な為替情報を集めて DB に格納するまでが(簡単に)できちゃいます:
2021061502


後はここで集めたデータを使って解析学習時のデータにするもよし、予測機能を作るもよし、ご自由にお使いください。

このフローは以前に少し違う形で業務やイベントのネタ(の裏側)として使っていたことがあったのですが、埋もれてしまうのはもったいない気がしたので公開しちゃいます。

Node-REDFX (外国為替)情報を取得するフローを作りました。FX というのは例えば USDJPY だと USD と JPY 、つまり米ドルと日本円の関係です。「1ドル=107円23銭」みたいなやつですね。これの EURUSD (ユーロドル)やら EURJPY (ユーロ円)やら AUDJPY (豪ドル円)やら、、主に日本円が絡む通貨ペアを中心に 20 ペアの情報を1分おきにリアルタイムで取得するものです。

フローはこちらで公開しています:
https://flows.nodered.org/flow/9d045f691b6d7c5cb3259c197ad365d0

2020060105



このページ内のフロー定義を "Copy" して、Node-RED 環境に「クリップボードから読み込み」するだけでフローが再現できます。フロー1本だけの、それも標準ノードの組み合わせだけで構成されているシンプルな内容です。動く条件は「Node-RED 環境がインターネットに接続されていること」だけでいけると思います:
2020060101


↓ペースト後、こんなフローのタブが作られていれば成功:
2020060102


あとはこのまま「デプロイ」すれば1分おきに inject ノードが動き出し、取得した FX 情報を debug タブに出力し続けます:
2020060103


1回実行した時の debug タブの様子はこんな感じです。_id に実行時の日付時刻が入り、あとは通貨ペアとその瞬間の価格がまとめて出力されます:
2020060100



公開しているフローではこれだけ(debug タブに出力するだけ)ですが、IBM Cloud 内の Node-RED として動いている環境であれば、バインド済みの Cloudant out ノードをフローの最後に追加して、DB 名を指定するだけで出力される情報を1つのレコードとして DB に格納する所まで簡単に実現できます。他の環境でも各種データベースノードに渡すことで取得データの DB 格納ができます:
2020060104


中身は inject node が一分ごとに発火してオープンな API を使って FX 相場を取得し、(Cloudant DB に格納する前提での)JSON フォーマットに変換して debug ノードに渡す、というものです。FX は24時間相場が動くので、1日に 60 * 24 = 1440 データ集まります。(データ量に気をつけながら)1ヶ月程度動かしっぱなしにしておくとそこそこの為替情報データベースができあがります。シンプルですが API やフォーマットを変えることで応用範囲が広くなりそうだと思っています。

本来は集まったデータをグラフ表示したり、上下動の予測をしたり、、、といった使いみちになると思っています。サンプルではない実データを簡単に集めることができるので、説得力のあるデモアプリに応用しやすいと思っています。興味ある方は使ってみてください。


このページのトップヘ