【UiPath Studio】UiPathとChatGPTを使ってチャットボットを作ってみる。（UiPath.Form.Activities v23.4系）


# はじめに
タイトルのままです。チャットボット風のワークフローを作ってみました。

正直なところ、ChatGPTのサイトはシンプルで優秀なUIだと感じています。
幅広いユーザーに使われるからこそ、使い方が限定される点が良いです。

あえてチャットボットとして作成し直す意味があるのかな…、という考えもあったのですが、学習も兼ね作ってみました。
**UiPathでこんなことできるんだ…、こんなことできるかも…** といった発想時の参考情報として捉えていただけるとうれしいです。

当記事では実装の肝や、手間取った点など、概要の説明にとどめています。

:::note
当記事の内容は **UiPath Studio Community v23.4.0** と **UiPath.Form.Activities v23.4.3**、**UiPath.WebAPI.Activities v1.16.2** で実行しています。
:::

# 作ってみたサンプルがこちらです。
:::note warn
Javascriptコードをたずねた後に **10秒以上の待ち** が発生していますが、**カット** しています。
:::
改善の余地がありますが、短期間でチャットボット風のアプリを作ることができました。
![チャットのサンプル3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/8c93e69c-cbab-c64b-abac-9c599f6388b5.gif)

最後の回答に画像が大きく表示されるのは意図した挙動ではありません。
自動レイアウト調整で画像が大きく描画されているようです…。

### チャットボット観点の工夫について
- 常時起動してチャットを待ち構えるようなユースケースを想定しました。
- チャットボット感を出すために感情移入できる人格の設定が重要な要素だと考え、手書きのアイコン画像を用意しました。（さらにキャッチーなネーミングがあると尚良いかも…）
-  API実行中の待ち時間は **正常処理中＆待ち時間** であることが伝わるようにしました。（難しいチャットだと応答に数十秒かかります…）


UiPathの実装に着目していきます。
チャットの中身以外はUiPathのFormアクティビティによる制御です。


# ChatGPTのAPIを利用する準備
学習を目的とした利用として、APIを実行する方法を選択しました。
以下の記事をご参照ください。

https://qiita.com/Kajiita/items/d9aeeaf7d1ab722732f6

以下の章の手順を実行しています。
1. **「API キーを取得してみる」**
2. **「【前半】API を使ってみよう ~ HTTP要求アクティビティによる実装例 ~」** 

:::note
 [公式サイトのAPI reference](https://platform.openai.com/docs/api-reference/chat)も確認しておきましょう。 
:::

### また、無料トライアルの対象ではない場合、支払情報（クレジットカード）の登録が必要です。
:::note warn
アカウントを早めに作成された場合、＄18の無料トライアル枠が割り当てられているようです。
**Manage account > Usage** で確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/56f91ced-440c-f63e-ba04-0c623271cb1e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/4ae429e0-a616-ad25-0ce4-99a5847c6aeb.png)

新規アカウントを作成したところ、APIを利用するには **支払方法（クレジットカード情報）の登録** が必要でした。
:::

#### 支払方法の登録の手順です。
事前にOpenAIのサイトのアカウントを作成します。

https://platform.openai.com/ へアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/e68a154a-8159-0d33-ec08-5fc5a2c17166.png)

右上のアイコンをクリックし、 **Manage account** を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/58c1c070-79db-97a9-b0f1-2d812c3006e7.png)

**Billing > Payment methods** の **Add payment method** を選択し、クレジットカード情報を登録します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/761f08e3-bfb0-44eb-c5dc-bd9c3d6cd821.png)

**Billing > Usage limits** の **Hard limit** (API利用停止の上限) と **Soft limit** (メール通知)に閾値となる金額を入力しておきましょう。 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/d05e4a1e-5b26-6484-1a88-69ff464c6e7c.png)

以降、それ以外の実装についてご説明します。

# UiPathメイン処理の実装サンプルはこちらです。
今回は **ローカルトリガー** という仕組みを使っています。
ボタン押下などのイベントをトリガーに、後続の処理が動く仕組みです。

ボタン押下時の処理に主なチャット時の実装があるため、
メインxamlの実装だけみるとシンプルです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/729854fe-bbaf-0b6c-3fab-64386e8f341e.png)

# チャットボタン押下後の処理はこんな感じです。

### 処理前半。
「**ローカルトリガー**」で始まっているのが特徴です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/798a1295-7486-0c35-c743-a5d3128f9641.png)


### 処理中盤。
肝となる実装です。並列処理でAPIと画面側の処理を分離しています。
左側がAPIを実行する流れで、赤枠内でAPIを実行しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/f83ea10d-8c8f-4da8-d772-b8e81d96f0b1.png)
右側の画面側の処理ではループ処理を実行していて、
ループ条件は左側のAPI実行後にFalseになるようにしました。

### 処理後半。
特筆する処理はありません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/14eb5da1-4a7f-e881-036c-559de546dcea.png)

# Closeボタン押下後の処理はこんな感じです。
こちらは閉じるためだけの処理なのでシンプルです。
「**ローカルトリガー**」で始まっていることと、
最後に「**ローカルトリガーを停止**」を配置しているのが特徴です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/80b6afec-21f5-69f4-98f4-477c6f239ada.png)

# フォームの定義はこんな感じです。
左ペインにある「 **レイアウト** 」内の定義を使い、各コンポーネントの配置を整理しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/5050a010-4bfc-cce9-39b5-34960a72c373.png)
（チャット応答後に画像が大きく表示されるのは、レイアウトにもう少し工夫が必要ということかな…と考えています）

#### 補足：　画像の追加方法は以下です。
左ペインにある **詳細 > コンテンツ** をドラッグで任意の場所に配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/d0ef8542-136e-0d99-5308-890bac22deb4.png)
**Insert image**を選択し、差し込む画像を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/2162edea-73c4-44d9-3922-cf0565347a83.png)
追加した画像をお好みで調整して **保存** ボタンを押下して定義を保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/0228f2ca-1627-e3b9-4618-1bfd42fbc036.png)


# ローカルトリガーの説明　（一例として、ボタン押下で後続処理が動く仕組み）

ボタンの **設定** メニューの左端にカミナリのマークの「**CREATE TRIGGER**」の選択肢が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/105c84a6-60d1-a353-512d-df566d2b45f8.png)

「**CREATE TRIGGER**」を選択すると、トリガー専用のxamlの雛形が自動生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/370ec77f-e262-4310-fc76-d18fc8f923c6.png)

**トリガーとして定義されたxaml** は以下のようにカミナリのマークがついているのが特徴です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/a6a6f797-9a1d-3603-c6f4-76862130d3bc.png)

### ★「ローカルトリガーを実行」アクティビティ（Start triggers）
トリガーが動作するためにはこのアクティビティを実行する必要があります。
今回はメインで呼び出すxamlの最後に配置しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/607ae6c1-1c34-0ed3-337f-c2aa281e1290.png)
これにより、ボタン押下のイベントが検知されるようになります。

### ★「ローカルトリガーを停止」アクティビティ（Stop triggers）
**「ローカルトリガーを停止」** が実行されると **トリガーとして定義されたxaml** に関連するイベントの監視が停止します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/37a23e62-f0e2-8fc1-fd7c-2e9e4f681a65.png)

:::note warn
手元の環境では **ローカルトリガーを実行（Start Trigger）** や **ローカルトリガーを停止（Stop Trigger）** は検索しないと見つかりませんでした。探すのに手間取ります、要注意です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/b6946859-af9f-7a12-1435-e3dc4695862e.png)
:::


# 実は 画像を動的に差し替える方法に意外と苦労しています。
フォーム画面の定義の中には、２つの画像を定義してあるのですが１つしか使えてません。
### 意外と苦労した点
- 「**フォームに値を設定**」で画像の差し替えも実現可能であろう、という仮説のもと着手したのですが、この仮説は実現できませんでした。
- 試行の一例は以下です。こちらは　**初期状態の非表示の設定** で、**期待通り** の挙動をしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/008267bd-b8a2-6f6e-ffac-2a12fba42e93.png)
（補足：**thinking** という **文字列パラメーター** で表示を制御しよう、というのが当初の目論見…）
- CSSを使って表示/非表示を使って制御しようとしてもうまくいかず諦めました…


:::note
**事象の切り分けの参考になったこと → HTMLを確認する！**
-  描画されるフォーム画面は通常の **右クリックメニュー** が効き、**名前を付けて保存** するとHTMLファイルであることが分かります。内容を確認した上でimgのsrcの書き換えを選択しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/1a12a094-678d-e280-f618-49f29eacdca4.png)
-  このHTML内に２つの画像が入っていないので、動的に切り替えるのは難しいだろう…という考えに至った次第です。
:::

## 補足：  画像の差し替えの方法
### ★「**フォームのスクリプトを実行**」 アクティビティ
「**フォームのスクリプトを実行**」 アクティビティで、Javascriptでimgタグのsrcを書き換える方法を使いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/27abf476-dda2-2223-81b2-4226feed8d7c.png)
画像差し替えのJavascriptはこんな感じ。
~~~
"var elem = document.getElementsByTagName('img');" +
"elem[0].src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...（略）"
~~~

画像は **Base64形式** の文字列として扱われています。
この内容はフォームの定義内の「**JSONを編集**」メニューからも確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/956bf604-12b1-87f5-cc14-d0edb26a1d3a.png)

下図にて、**"html"** の値としてBase64で記載された **imgタグ** の内容が確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/a64b7219-8bad-7102-325c-e43032755ad4.png)

**～ の画像に差し替え** の部分が「**フォームのスクリプトを実行**」 アクティビティで、これらを応答待ちの間、**並列処理でループ実行** しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3302589/2344d03b-bb1d-27c5-d17c-2109b795d5f9.png)

:::note 
**実装の補足：**
- フォーム描画の応答性が不明だったので処理に余裕をもたせる目的と、**HTTP要求（API実行）** 実行と並列で処理しているために制御がバランス良く実行される目的として 0.5秒の **Delay（sleep）** を入れました。 
- 少し冗長なのですが、チカチカと画像が変わった方が可愛いかな、と考え １ループ内で１往復の画像切替 をしています。
:::



# まとめ
いかがでしょうか。ChatGPTが気軽に利用できることで、UiPathと組み合わせて、手軽にチャットボットのような仕組みを作ることができました。

応用するともっと良い感じのチャットボットが実装できそうです。
ただし **UIにこだわりすぎると沼にはまってしまう** ので注意が必要です。

APIの応答待ち部分の画像制御は完全に趣味の世界ですが、プロセスの実行中に例外が発生していないことも知ることができます。ChatGPTの応答に時間が掛かることがあるので、意外と重要なサインとなりました。

Formアクティビティ、ChatGPTともに面白いです。
個人的にこの実装は使い込んで改善していきたいと思いました。