LED への RGB 値を JSON データで返答する ChatGPT API の仕組みを Node-RED でブラッシュアップしたメモです。
使っている API やバージョン
- OpenAI 社の ChatGPT API の 2023/3/17 時点のものを使用
- API Reference – OpenAI API の REST API の情報を参考
- Chat completion – OpenAI API のサンプルを使って gpt-3.5-turbo のモデルを利用
元となる質問例
LED へ設定する RGB 値を JSON データで返答し HTTP API 的に ChatGPT が答えてくれる質問例のメモ
こちらの記事の、LED へ設定する RGB 値を JSON データで返答し HTTP API 的に ChatGPT が答えてくれる質問例を元にして、Node-RED でフローを組んで ChatGPT で返してくれるようにして、さらにブラッシュアップをかけます。
Node-RED のフロー
フローはこちらです。
API Reference の REST API の情報を元に地道に http request を軸にアクセスしてます。
inject ノードから茶色や黄色(ひらがな)といった文言を出力し、content と書かれている template ノードで質問文を作成しています。また、ほげほげという文言で例外を発生させて検証しやすくします。
以下の仕様に従って答えてください。 # 返答するJSON データ {"result":true,"type":"led","r":255,"g":0,"b":0,"message":"説明"} # ルール - 色名がRGB値に認識されたときはresult値はtrueを返します。 - type 値はledで固定です。 - r値は色名がRGB値に認識されたときのR値です。 - g値は色名がRGB値に認識されたときのG値です。 - b値は色名がRGB値に認識されたときのB値です。 - 色名がRGB値に認識されたときの追加の説明はmessage値に入れてください。 - たとえば「赤」というメッセージの場合、RGB値はRが255、G値が0、B値が0なので、JSONデータは {"result":true,"type":"led","r":255,"g":0,"b":0,"message":"説明"} になります。 - 色名がRGB値に認識されなかったときはresult値はfalseを返します。 - 色名がRGB値に認識されなかったときの説明はmessage値に入れてください。 - ここまでのルールにおいての例外は {"result":false,"type":"led","message":"色名が認識されない例外処理です"} と返してください。 # 返答前にチェック! - 返答データはJSONデータのみです。 仕様は以上です。 今回は「{{payload}}」というメッセージについて返答ください。
いろいろ試行錯誤をして、このような質問例が良い感じに JSON を返してくれるようになりました。{{payload}}
のところに、色名が入ります。
API 設定と書かれている change ノードで http request ノードで API にアクセスするための設定をしています。API Reference – OpenAI API を参考に実装しました。
OpenAITaken は OpenAI で発行したトークンです。そして、url は https://api.openai.com/v1/chat/completions
で今回使う API です。headers.Authrization は OpenAITaken を利用して作る実際の HTTP ヘッダー情報です。
paylload は、
{ "model":"gpt-3.5-turbo", "messages":[ { "role":"user", "content":content } ], "temperature":0.7 }
となっていて JSONata で conent と書かれているところに先ほどの質問は入ります。
Node-RED の JSON データ
今回のフローの JSON データは以下の通りです。
[{"id":"f8eafae56f3d45a6","type":"inject","z":"fe9abfe75128af39","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"きいろ","payloadType":"str","x":210,"y":1460,"wires":[["8e86fa7263193a59"]]},{"id":"dec536cc47e82ed8","type":"change","z":"fe9abfe75128af39","name":"API 設定","rules":[{"t":"set","p":"OpenAIToken","pt":"msg","to":"<OpenAIToken>","tot":"str"},{"t":"set","p":"url","pt":"msg","to":"https://api.openai.com/v1/chat/completions","tot":"str"},{"t":"set","p":"headers.Authorization","pt":"msg","to":"\"Bearer \" & OpenAIToken","tot":"jsonata"},{"t":"set","p":"payload","pt":"msg","to":"{\t \"model\":\"gpt-3.5-turbo\",\t \"messages\":[\t {\t \"role\":\"user\",\t \"content\":content\t }\t ],\t \"temperature\":0.7\t}","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":540,"y":1460,"wires":[["79461771d50d7005"]]},{"id":"79461771d50d7005","type":"http request","z":"fe9abfe75128af39","name":"","method":"POST","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"","senderr":false,"headers":[],"x":750,"y":1460,"wires":[["f7ceee61855ed135","2cdbd942aed60b7c"]]},{"id":"fac0089ca9f0eaba","type":"inject","z":"fe9abfe75128af39","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"茶色","payloadType":"str","x":218,"y":1396,"wires":[["8e86fa7263193a59"]]},{"id":"8e86fa7263193a59","type":"template","z":"fe9abfe75128af39","name":"content","field":"content","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"以下の仕様に従って答えてください。\n\n# 返答するJSON データ\n{\"result\":true,\"type\":\"led\",\"r\":255,\"g\":0,\"b\":0,\"message\":\"説明\"}\n\n# ルール\n- 色名がRGB値に認識されたときはresult値はtrueを返します。\n- type 値はledで固定です。\n- r値は色名がRGB値に認識されたときのR値です。\n- g値は色名がRGB値に認識されたときのG値です。\n- b値は色名がRGB値に認識されたときのB値です。\n- 色名がRGB値に認識されたときの追加の説明はmessage値に入れてください。\n- たとえば「赤」というメッセージの場合、RGB値はRが255、G値が0、B値が0なので、JSONデータは {\"result\":true,\"type\":\"led\",\"r\":255,\"g\":0,\"b\":0,\"message\":\"説明\"} になります。\n- 色名がRGB値に認識されなかったときはresult値はfalseを返します。\n- 色名がRGB値に認識されなかったときの説明はmessage値に入れてください。\n- ここまでのルールにおいての例外は {\"result\":false,\"type\":\"led\",\"message\":\"色名が認識されない例外処理です\"} と返してください。\n\n# 返答前にチェック!\n- 返答データはJSONデータのみです。\n\n仕様は以上です。\n\n今回は「{{payload}}」というメッセージについて返答ください。","output":"str","x":380,"y":1460,"wires":[["dec536cc47e82ed8"]]},{"id":"f7ceee61855ed135","type":"debug","z":"fe9abfe75128af39","name":"debug 20","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload.choices[0].message.content","targetType":"msg","statusVal":"","statusType":"auto","x":900,"y":1460,"wires":[]},{"id":"cce37f09ebd0fb32","type":"json","z":"fe9abfe75128af39","name":"","property":"payload","action":"","pretty":false,"x":890,"y":1340,"wires":[["98d30e69f5b0121c"]]},{"id":"2cdbd942aed60b7c","type":"change","z":"fe9abfe75128af39","name":"payload.choices[0].message.content","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.choices[0].message.content","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":650,"y":1340,"wires":[["cce37f09ebd0fb32"]]},{"id":"98d30e69f5b0121c","type":"debug","z":"fe9abfe75128af39","name":"debug 21","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1020,"y":1340,"wires":[]},{"id":"43b7cb69828b4c95","type":"inject","z":"fe9abfe75128af39","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"ほげほげ","payloadType":"str","x":200,"y":1520,"wires":[["8e86fa7263193a59"]]}]
実行してみる
茶色の文言を出力する inject ノードをクリックしてみます。
{"result":true,"type":"led","r":165,"g":42,"b":42,"message":"茶色はR:165, G:42, B:42です。"}
きいろの文言を出力する inject ノードをクリックしてみます。
{"result":true,"type":"led","r":255,"g":255,"b":0,"message":"きいろはR:255, G:255, B:0の色です。"}
きいろの文言を出力する inject ノードをクリックしてみます。
{"result":false,"type":"led","message":"色名が認識されない例外処理です"}
うまく JSON データで返してくれています!
以下の仕様に従って答えてください。 # 返答するJSON データ {"result":true,"type":"led","r":255,"g":0,"b":0,"message":"説明"} # ルール - 色名がRGB値に認識されたときはresult値はtrueを返します。 - type 値はledで固定です。 - r値は色名がRGB値に認識されたときのR値です。 - g値は色名がRGB値に認識されたときのG値です。 - b値は色名がRGB値に認識されたときのB値です。 - 色名がRGB値に認識されたときの追加の説明はmessage値に入れてください。 - たとえば「赤」というメッセージの場合、RGB値はRが255、G値が0、B値が0なので、JSONデータは {"result":true,"type":"led","r":255,"g":0,"b":0,"message":"説明"} になります。 - 色名がRGB値に認識されなかったときはresult値はfalseを返します。 - 色名がRGB値に認識されなかったときの説明はmessage値に入れてください。 - ここまでのルールにおいての例外は {"result":false,"type":"led","message":"色名が認識されない例外処理です"} と返してください。 # 返答前にチェック! - 返答データはJSONデータのみです。 仕様は以上です。 今回は「{{payload}}」というメッセージについて返答ください。
もう一度文言を見てみると、いろいろブラッシュアップできました。
- ルール・返答する JSON データ・返答前にチェック!という見出しでお願いを分かりやすくした
- ルールは長々とした文章で書かずに箇条書きにして伝わりやすく
- 認識されなかったとき・認識されたときのルールを細かく指示。特に結果を result に出させた。
- ついつい JSON データに説明文を加えがちなので「追加の説明はmessage値に入れて」と説明を入れる値を入れるお願いを明確にすることで説明文を加えないように誘導(めちゃくちゃ効いた)
- 「ここまでのルールにおいての例外は {“result”:false,”type”:”led”,”message”:”色名が認識されない例外処理です”} と返してください。」という例外処理を明確にお願いすることで説明文を加えないように誘導(これもめちゃくちゃ効いた)
途中 role system 値も少し効いた気がする、でもちょっとわからない
この設定ナレッジには入れませんでしたが、Chat completion – OpenAI API で書かれている role system も効いた気がします。友人に前回のナレッジを伝えたところ「これ system って値でもっと調整できるかもしれませんよ」と教えてもらったものです。
これは、今回のようなルール宣言を role user の前に仕込める仕組みで、ルール指示を system のほうへ置いて、質問を user に置いて分離できます。ただ、今回のように user 中にルールも出題も全部入れても質問が良ければちゃんと返してくれるような気配もあって、もっと使って調べてみたほうが良さそうです。ともあれ、こういったユースケースを知れてよかったです。
また一つ、ChatGPT への良い指示の仕方が見えてきたので、引き続き色々やってみますー。