Azure OpenAI Service をノーコード開発してみる(4) ~ ServiceNow でクライアントサイド開発 ~

NTTデータ先端技術株式会社の原田です。

Azure OpenAI Service の学習にあたって試してみたフルノーコードによる開発のご紹介です。  
この記事は2023/11/15時点の情報で記載しています、アップデートにより変わってしまっている部分がありましたらご容赦ください。

今回は全6回の第4回~ ServiceNow でクライアントサイド開発 ~になります。  

## 記事一覧
[第1回. Logic Apps でサーバーサイド開発(構築編) ](https://qiita.com/koutaro_harada/items/ba5646e2d90a56f9ec3b)   
[第2回. Logic Apps でサーバーサイド開発(開発編) ](https://qiita.com/koutaro_harada/items/bbb4271e333943e09061)    
[第3回. Power Platform でクライアントサイド開発](https://qiita.com/koutaro_harada/items/ac074acb2264bcaed28d)    
**第4回. ServiceNow でクライアントサイド開発**  
第5回. Azure リソースのセキュリティ強化  
第6回. Azure OpenAI Service をノーコード開発してみるのまとめ  

## はじめに
クライアントサイド開発の2回目は ServiceNow の仮想エージェントを使用します。  
前回の終わりで少し触れましたが実は Power Virtual Agent はライセンスの関係で社内公開できないという状況に陥ってしまい、その代替方式として ServiceNow の仮想エージェントが浮上と言う経緯を辿っています。  
↓が仮想エージェントの全体フローです、次のセクションからポイントとなる実装をピックアップしてご紹介していきます。  
![c9ce99047dc1b80cceb3_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/6b91fec9-7fa8-83e3-3fd6-f2d274b9e663.png)

## REST メッセージの登録
まず前段の作業として API を REST メッセージとして登録します、Power Platform ですとカスタムコネクタが相当の機能でしょうか。  
![c9ce99047dc1b80cceb3_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/0a6eda45-7fac-f0c6-de9c-1ffda6b33429.png) 
POST メソッドの定義は以下の通り。  
![c9ce99047dc1b80cceb3_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/1337907f-c45d-761f-8031-a564834cfcd3.png)  
関連リンクのテストを押すと、値フィールドに設定されたパラメータで API を呼び出してくれます。(Postman 使わなくて良いので便利)  
![c9ce99047dc1b80cceb3_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/8cbcb375-7a21-8272-19b5-bdadf85b8d53.png)  
関連リンクのテストを押すとスクリプト使用のプレビューで、サンプルを取得可能です。(この時点でノーコードではなくなりました…)  
```
try { 
 var r = new sn_ws.RESTMessageV2('aoai-qiita-chat-create', 'POST');
 r.setStringParameterNoEscape('DisplayName', '原田 幸太郎');
 r.setStringParameterNoEscape('UserId', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
 r.setStringParameterNoEscape('Email', 'haradaktr@intellilink.co.jp');
 r.setStringParameterNoEscape('LastName', '原田');
 r.setStringParameterNoEscape('FirstName', '幸太郎');
 r.setStringParameterNoEscape('UPN', 'haradaktr@intellilink.co.jp');
 r.setStringParameterNoEscape('SessionId', '');
 r.setStringParameterNoEscape('Message', 'NTTデータ先端技術株式会社について教えてください。');

//override authentication profile 
//authentication type ='basic'/ 'oauth2'
//r.setAuthenticationProfile(authentication type, profile name);

//set a MID server name if one wants to run the message on MID
//r.setMIDServer('MY_MID_SERVER');

//if the message is configured to communicate through ECC queue, either
//by setting a MID server or calling executeAsync, one needs to set skip_sensor
//to true. Otherwise, one may get an intermittent error that the response body is null
//r.setEccParameter('skip_sensor', true);

 var response = r.execute();
 var responseBody = response.getBody();
 var httpStatus = response.getStatusCode();
}
catch(ex) {
 var message = ex.message;
}
```  

## テーブル定義の変更
REST メッセージの登録の際エンドポイントフィールドが200文字に制限されていて入力できないと言う事象に当たりましたので、テーブル定義を変更して255文字に拡張しています。  
ここは結構手間取りました。  
![c9ce99047dc1b80cceb3_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/2bebc1c4-0265-b29e-8a51-f656955b718f.png)  

## SessionId 生成
ここから仮想エージェントの中身です、まずは SessionId の生成。  
スクリプト応答コンポーネントを配置して、  
![c9ce99047dc1b80cceb3_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/90ba494f-9a35-7626-b5d8-3aaa9ec220d6.png)    
スクリプトはこんな感じ、Guid を生成して仮想エージェント変数に格納しています。  
```
(function execute() {
    vaVars.sessionid = gs.generateGUID();
})()
```

## API 呼び出し
その後テキスト入力のコンポーネントを配置し、API の呼び出し処理です。  
こちらもスクリプト応答コンポーネントを配置して、   
![c9ce99047dc1b80cceb3_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/67cc4387-fd70-8b17-0089-d6dc7d4bcff4.png)    
スクリプトはこんな感じ、呼び出し処理はサンプルそのままでパラメータには vaInput.user に格納されているユーザー情報を使用しています。  
```
(function execute() {
    try { 
        var r = new sn_ws.RESTMessageV2('aoai-qiita-chat-create', 'POST');
        r.setStringParameterNoEscape('DisplayName', vaInputs.user.first_name + ' ' + vaInputs.user.last_name);
        r.setStringParameterNoEscape('UserId', vaInputs.user);
        r.setStringParameterNoEscape('Email', vaInputs.user.email);
        r.setStringParameterNoEscape('LastName', vaInputs.user.last_name);
        r.setStringParameterNoEscape('FirstName', vaInputs.user.first_name);
        r.setStringParameterNoEscape('UPN', vaInputs.user.email);
        r.setStringParameterNoEscape('Message', vaInputs.input_message);
        r.setStringParameterNoEscape('SessionId', vaVars.sessionid);

        var response = r.execute();
        var httpStatus = response.getStatusCode();
        var responseBody = response.getBody();
        var parser = new JSONParser();
        var result = parser.parse(responseBody);

        var singleOutMsg = new sn_cs.SinglePartOutMsg();
        singleOutMsg.setTextPart(result.Content);
        return singleOutMsg
    }
    catch(ex) {
        var message = ex.message;
    }
})()

```  
responsebody を取得するところまではサンプルの通りですが、Json をパースするところでコーディングが発生しています。  

## 繰り返し制御
繰り返しの制御はブーリアン入力コンポーネントを配置し、意思決定ユーティリティで遷移先を振り分ける形です。   
![c9ce99047dc1b80cceb3_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/28a14c02-693e-251c-8579-cf3e3aa7e753.png)  

## テスト
チャットボットのテストを行います。  
![c9ce99047dc1b80cceb3_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/575ebd38-daa6-93b1-5090-ecbb1bd29e70.png)  
上手くいっていそうですね、画面は省略しますがセッション情報も格納されていました。 
(すいませんクレジット超過処理は未実装です)  

## おわりに
今回はエンドポイントフィールドの200文字制限突破に0.5日ほど手間取りました。  
開発期間はこちらも 1.5～2.0 日くらいで、未実装の部分があるのを習熟度で相殺すると生産性は Power Virtual Agent とあまり変わらない印象です。  
あと ServiceNow はノーコードと言うよりローコードだと言うのもわかりました。  

## 次回
ここまででサーバーサイド・クライアントサイドとも大枠まで開発することができました。  
次回は観点を変えて Azure リソースのセキュリティ強化を実施していきます。