Azure OpenAI Service をノーコード開発してみる(2) ~ Logic Apps でサーバーサイド開発(開発編) ~

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

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

今回は全6回の第2回~ Logic Apps でサーバーサイド開発(開発編)  ~になります。  

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

## はじめに
第2回では Logic Apps を用いてクライアントからの呼び出しを受ける API を開発します。  
実装する主な機能は下記の通りです。
* OpenAI Service を呼び出して回答を返却
* 呼び出し回数の制限を行うためのクレジット管理
* 一連の履歴を積み上げてプロンプトを生成

↓が全体フローです、次のセクションからポイントとなる実装をピックアップしてご紹介していきます。  
![bbb4271e333943e09061_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/fd3e1c26-e4b2-9bc9-d861-e27cc8b43575.png)  

## HTTP 要求の受信時
クライアントからの呼び出しの際のリクエストパラメータです。
* SessionId : プロンプトの積み上げで使用 
* UserId : クレジット管理で使用
* Message : チャット本文
```
{        
    "SessionId": "e692b99d-f7ac-42f8-b5f5-5eb261e80562",
    "UserId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    "Message": "NTTデータ先端技術株式会社について教えてください。",
    "DisplayName": "原田 幸太郎",
    "LastName": "幸太郎",
    "FirstName": "原田",
    "Email": "haradaktr@intellilink.co.jp",
    "UPN": "haradaktr@intellilink.co.jp"
}
```

## ユーザー情報チェック＆登録
Cosmos DB の User コンテナーを UserId をキーにチェックし、ユーザー情報が存在しない場合は登録を行います。 (ここでクレジットの初期化も行っています)  
![bbb4271e333943e09061_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/55743bbd-e5be-184d-76c6-375f057802da.png)  

## クレジットチェック
ユーザー情報が存在する場合はクレジットをチェックし、残っていない (Credit <= 0) 場合ステータスコードに 429 をセットして応答を返します。 
![bbb4271e333943e09061_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/1a9ac996-899a-f84f-80da-64334ac77573.png)  

## セッションチェック
Cosmos DB の Session コンテナーを SessionId をキーにチェックし、セッションが存在する場合は前回までの履歴を取得します。  
存在しない場合はチャットを呼び出す呪文で初期化。  
![bbb4271e333943e09061_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/1e4a0d0f-2f88-c4b7-d819-914dd3024cec.png)  

セッション情報はこんな感じ。
```
{
    "Messages": [
        {
            "content": "You are a helpful assistant.",
            "role": "system"
        },
        {
            "content": "NTTデータ先端技術株式会社について教えてください。",
            "role": "user"
        },
        {
            "role": "assistant",
            "content": "NTTデータ先端技術株式会社は、日本の情報技術（IT）企業であり、NTTグループの一員です。NTTデータグループの中でも主に先端技術の研究開発やサービス提供に力を入れており、AIやビッグデータ分析、IoT、ブロックチェーンなどの分野において、多数の実績と強みを誇っています。また、グローバルにビジネスを展開し、アジア、ヨーロッパ、北米などに拠点を設けています。"
        }
    ],
    "SessionId": "ef015b7f-5a15-4a20-aabb-be68ab331653",
    "UserId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    -----
}
```

## プロンプト生成
履歴もしくは呪文が格納された配列にチャット本文を追加し Json 形式に整えます。  
![bbb4271e333943e09061_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/bcf69cc9-c174-34f7-1db3-12a4216cba92.png)  

## OpenAI Service 呼び出し
OpenAI Service は HTTP アクションで呼び出します。(いずれ専用アクションがリリースされるんでしょうか)  
![bbb4271e333943e09061_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/7789da1d-25a4-7c1b-ee55-f306384e095a.png) 

## クレジット減算
クレジットを減算して User コンテナーを更新します。  
![bbb4271e333943e09061_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/34cb8adb-3b1e-a23b-2e97-e11bb54971c3.png)  

## セッション更新
プロンプトに応答を追加してセッションを更新します、実装はプロンプト生成とほぼ同じです。  
OpenAI Service は単一の応答でもアレイで返してきますので First 関数で引っ掛けてます、このあたりはもうちょい深堀りすべきかもしれません。  
![bbb4271e333943e09061_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/7a993eca-2d68-224f-52b7-adad83e4f107.png)  

## 応答
そして最後に応答を返して終了です。

## テスト
まだクライアントが出来ていませんので Postman でテストしてみます。  
![bbb4271e333943e09061_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3620633/2bed7ff6-f513-030b-a72f-5c4909c8d97e.png)
上手くいっていそうですね。 

## おわりに
今回難しかったところは…あんまりなかったです、強いてあげるなら応答がアレイになっていることに気づくところでしたでしょうか。
開発期間は 1.5～2.0 日くらいでした。

## 次回
次回はこの API を使用して Power Platform (Power Virtual Agent) でクライアントサイド開発を行っていきます。