QnAMakerを使って画像付きで回答を返してみた。



## はじめに
QnAmakerでQAと紐づいた画像を返してみようというお話です。
※bot自体の実装は、AzureBotServiceV3.0 C# botFrameworkを利用しています。
※QnAmaker、AzureBotServiceの概要は省略しています。

## 仕組み
画像を表示させるには、QnAMakerのMetadataを使用します。*1Metadataの本来の使い方ではありませんが、Metadataに画像のURLを設定し、アプリケーションからアクセスして使うという手法です。

## QnAMakerにQAを設定する。
まずは、QnAMakerにQAを追加します。
右上歯車ボタンを押すと、QAにメタデータを付加することができます。
メタデータは{Key,value}のセットで登録できます。
今回は、{key:picurl} {value:画像のURL(ファイル名)}
という形で登録していきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/195221/626d6fdb-68d7-9742-9c9f-47f971366c40.png)


## 画像を保存
今回はAzureのblobストレージに画像を保存します。
コンテナを作成します。パブリックアクセスレベルは「コンテナー」とします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/195221/9646e1bf-d403-9ccf-46f6-879729a2853b.png)

先ほどQAに登録したファイル名の画像をBlobにアップロードします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/195221/47f7335e-36a0-56e2-a910-a335a850b244.png)




## 画像を取得
QnAmakerから取得した回答のmetadataを使用して、BLOBストレージから画像を取得します。

QnAmakerから取得した回答文とmetadataの値（画像のファイル名）を引数としています。

```C#:ReplayPicture.cs
private IMessageActivity ReplayPicture(IDialogContext context, string Answer, string picurl)
        {

            i = 0;

            StringBuilder replayanswer = new StringBuilder();
            // blob の情報を取得します
            CloudStorageAccount cloudStorageAccount = CloudStorageAccount.Parse(@"DefaultEndpointsProtocol=XXXXXXXXXXXXXXXXXXXXXXX");
            CloudBlobClient cloudBlobClient = cloudStorageAccount.CreateCloudBlobClient();
            CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference("コンテナ―名");
            CloudBlockBlob blob = cloudBlobContainer.GetBlockBlobReference(picurl + ".png");

            // SAS キーを取得します (キーの有効期間を 15 分、アクセス許可を読み取りに設定しています)
            var sasContraints = new SharedAccessBlobPolicy();
            sasContraints.SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5);
            sasContraints.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(15);
            sasContraints.Permissions = SharedAccessBlobPermissions.Read;

            var sasBlobToken = blob.GetSharedAccessSignature(sasContraints, null, null);

            string extension = ".png";

            string attachedUrl = BlobUrl + picurl + extension + sasBlobToken;
            
            ...

```

上記の```attachedUrl```に画像にアクセスするためのURLを取得することができました。

あとはこのURLを使って、画像を差し込んだメッセージを作成していきます。


## 画像付きメッセージを作成する
上記でBLOBのURLを取得すれば、あとは画像付きメッセージを作成していきます。

画像付きメッセージの作成にはHero Cardを使う方法もありますが、Cardの場合画像は文の下にしか入れれないため、今回は*2 markdownを使用します。

botframeworkで画像付き回答コンテンツを作成する場合は、テキストフォーマットをmarkdownに指定して、markdown方式で作成していきます。

```C#:
            //markdownを指定します
            replay.TextFormat = "markdown";

            //回答を作成      
            replay.Text = "回答の文章を入れる"![](" + 画像URL + ")"";
```

上記を使用すれば画像を入れることができますが、画像を回答文のどこの部分に差し込むかを決めなければなりません。

先ほどQnAmakerに登録したQAを編集します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/195221/09f168bd-774d-cfc0-ac61-973ff587ebe8.png)


上記画像では```attachedimg```を画像を差し込み場所の目印にしています。

プログラム側では以下のように実装します。(ReplayPicture.csの続きです)

```C#:ReplayPicture.cs
   
   ...

            var spanswer = Regex.Split(Answer, "attachedimg");

            foreach(var addanswer in spanswer)
            {
                replayanswer.Append(addanswer);

                if (i == 0)
                {
                    replayanswer.Append("![](" + attachedUrl + ")");
                }

                i++;
            }

            Activity replay = ((Activity)context.Activity).CreateReply();

            //markdownを指定します
            replay.TextFormat = "markdown";

            
            replay.Text = replayanswer.ToString();

            return replay;


```

処理内容は単純で回答の文章を```attachedimg```でセパレートして、画像のURLに置きかえて返答コンテンツを作成しています。

## 動作確認
botでは以下のように表示されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/195221/63a93bf8-532d-3e0f-f78c-f1285e3a656b.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/195221/9a8d63f7-a2af-a8b7-8b86-28f95a33fdd7.png)


## 参考doc

*1 QnA makerメタデータ
https://docs.microsoft.com/ja-jp/azure/cognitive-services/qnamaker/how-to/metadata-generateanswer-usage

https://qiita.com/gomwam/items/887900e2d996294ed3f3

*2 botframework　markdown： 
https://blogs.msdn.microsoft.com/jamiedalton/2016/08/22/bot-framework-markdown-support-by-channel/