Microsoft Power Virtual Agentのプラクティス

# 背景

職場で問い合わせ対応を実施しているのですが、同じような質問が多い、かつ回答者が自分だけで属人化しているので、回答内容を整理しておきたい。またできればそれをチャットボット化して回答を自動化させたい。というところから調べた結果の共有になります。

# Microsoft Virtual Agentについて

会社ではTeamsを使用しており、またAzureのChatbot開発を使用できるほどのライセンスがないので、Microsoft Virtual Agent for Teamsを使用する。ちなみに、Web AppとTeams Appは完全に別物です。Teams Appで作成したBotはWeb App側では管理できません。

FAQのQ,A(トピック)はマニュアルで追加することもできますが、「トピックの提案」という機能で一括登録もできます。wordやテキスト、エクセル、ウェブページなどをソースにできますが、若干難があります。

## トピックは何かしらのクラウドから取得しないといけない

ローカルファイルのアップロードに対応していない…。これは正直意味わからないけど、そういう仕様なので仕方ないです。また完全にパブリックアクセスなファイルでないといけない(認証に対応していない)ため、その点も注意が必要です。

## 完全にQ-Aの構造になっていないといけない

エクセルであれば2列のみ、ワードであれば、見出し→本文→見出し→本文、のように完全にQ→Aになっている必要があり、カテゴリ分け等ができない。

## 重複したトピックを検出してくれない

以前使ったワードにFAQを追加して再度提案すると、既存ですでに取り込み済のものも一緒に提案される。デフォルトでは無効化されているので、必要分のみ有効化すればいいのだが、回数が増えてくるとちょっと大変そうです。

## 機械的な回答になる

これは作りこみの問題ですが、単純にトピックの提案をするだけでは、チャットからの問い合わせに対して、一番近しいものを選び、その回答部分をそのまま返すため、Q/Aがかみ合わない、もしくは間違った回答になる可能性が高いです。例えば、

Q: Aというサービスに、Cは含まれますか?
A: 含まれません。

というQ/Aが登録されていると、「(Bというサービスに)Cは含まれますか?」という質問に対して、「含まれません」と回答します。質問者が前提条件をすべてインプットするとは限らないので、注意が必要です。
また、Q側(トリガー)からしか拾いません。例えば、
Q: Aというサービスの対応時間帯を教えてください。
A: 夜間休日を含み対応が可能です。

というQ/Aが登録されていると、「夜間対応が可能なサービスはどれですか?」という質問には回答できません。「夜間」というキーワードがトリガー側にないためです。

## キーワードの重みづけはされない?

上記と似ていますが、
1. Q: Aというサービスで、Cは対応可能ですか?
2. Q: Aというサービスで、Dは対応可能ですか?
3. Q: Aというサービスで、Eは対応可能ですか?
4. Q: Bというサービスで、Cは対応可能ですか?
5. Q: Bというサービスで、Dは対応可能ですか?
6. Q: Bというサービスで、Eは対応可能ですか?
  
みたいなトリガーを設定すると、「サービス」や「対応」「可能」などもキーワードと認識するのか、「Bというサービスで、Fは対応可能ですか」に対して1-3を候補に出してしまう場合があります。候補は3つまでしか提示されず、またそこにないと回答するとそれで終わりになってしまい、追加で候補が提示されません。

## トピック数制約が意外と少ない

[公式]([Rate limits for chatbots - Power Virtual Agents | Microsoft Learn](https://learn.microsoft.com/en-us/power-virtual-agents/requirements-quotas))に記載がありますが、トピックがデフォルトでは1つのボットにつき250までになっています。自分の場合、3-4か月分で50件程度だったので、当面は大丈夫そうですが、場合によってはネックになりそうです。またトピックあたりのトリガーは200までとなっているので、こちらは余裕があります。

# 上手な運用を考える

## トピックのソースづくり

自分の場合は、Microsoft Listsを使って作成し、そこからエクスポートしたエクセルから余分な列を削除してソースにします。エクセルにしたのはカテゴリなどの情報を持たせても、列ごと削除が簡単だからという理由で、大本としてListを使った理由は、素のエクセルだと入力や検索がちょっと辛いこと、です。Listの入力UIは、エクセルのセルに直接入力するのに比べて非常にやりやすいです。

以下に気を付けて作成します。

- Answer部分は冗長になってもよいので、誤解が発生しないように前提条件含めて記載する
- できるだけトリガー側にキーワードを入れる
- トリガーを細分化しつつ、Answer側に包括的な情報を持たせて、トピックの数を節約する
- Answer部分に図表は入れられないため、テキストのみにする。必要な場合はリンクなどで対応する。
  

### エクセルをTopicとして取り込めなかった話(たぶんすごい特殊ケース)

当初どれだけシンプルな2列のエクセルを用意しても、「Getting suggestions took too long. Try links with less content.」といわれてはじかれました。
いろいろ試した結果、おそらく、自分の環境の問題のようです。自分の会社ではデータ管理のために各エクセルファイルに変なラベルを付与されるようになっていて、それが原因。アップロード可能なCSVを逆にxlsxとして保存しなおした直後なら、トピックとして取り込めたので。このラベルはエクセルファイルを閉じるときに強制付与されるので、以下のようにして無理やり回避しています。

1. Listからxlsxでエクスポート  
2. テーブルや余分な列を削除して名前を付けて保存(この時点ではラベルいらない)
3. ファイルを閉じずにクラウドにアップロードして取り込んでしまう
  

### エクセルの行数が多いと取り込めない

これは明確な基準があるのか不明ですが、44行のエクセルはNGで20行ならOKでした。ただその後、別の20行のエクセルはNGになったりするので、条件が不明です。
その後、当初取り込めた20行のエクセルで再度試したところNGだったので、アップロード先のS3のパフォーマンスや、Virtual Agent側のクォータの可能性もあります。

(2023/1/13 Update)
Q側の列がセル内で改行されているためでした。改行をなくしたところ無事取り込めました。[公式](https://learn.microsoft.com/en-us/power-virtual-agents/teams/advanced-create-topics-from-web-teams#microsoft-excel)では改行でトリガーを複数設定できると読み取ったのですが…。日本語だとだめなのかもしれません。

> You can enter more than one question (separated by new lines) in a first-column cell and they'll all be added as trigger phrases to the topic suggestion.

## トピックのソース置き場(MinIO)

結論を言うと、最終的にはAWSのS3に配置し、取り込み後すぐに削除するという運用にしています。

Public Accessにしないといけないため、当初は社内のLinuxサーバとMinIOを使ってオンプレで構築してみたのですが、オレオレ証明書によるHTTPS化ではだめなようです。公式にはそこまで書いてないのに…。

> - Web ページまたはサポートされているファイルの種類を指している
> - インターネット上の誰でもアクセス可能
> - ユーザーがログインする必要はない
> - HTTPS を使用している (`https://` で始まる)

[オンライン コンテンツからトピックの提案を取得する (ビデオを含む) - Power Virtual Agents | Microsoft Learn](https://learn.microsoft.com/ja-jp/power-virtual-agents/advanced-create-topics-from-web)

一応社内CAから発行してもらえればできるかもしれませんが、今回はそこまでしませんでした。後学のため、MinIOの手順はメモしておきます。

### インストール

基本的に[公式]([MinIO | Code and downloads to create high performance object storage](https://min.io/download#/linux))通りで問題なし。

```shell
# mkdir minio
# cd minio/
# wget https://dl.min.io/server/minio/release/linux-amd64/minio
# chmod +x minio
# MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001"
```

### HTTPS化

これも[公式]([Network Encryption (TLS) &#8212; MinIO Object Storage for Linux](https://min.io/docs/minio/linux/operations/network-encryption.html))にガイドがあるので、これ通りでよい。はじめにオレオレ証明書を作るツールを入れる。ご丁寧にMinIOが用意してくれているので、[こちら]([GitHub - minio/certgen: A dead simple tool to generate self signed certificates for MinIO TLS deployments](https://github.com/minio/certgen))を使う。

MinIOを構築したOSに合わせてダウンロードし、適当に配置する。

```
# ls
certgen-linux-arm64  minio
# chmod +x certgen-linux-amd64
# ./certgen-linux-amd64
2023/01/03 11:20:01 Missing required --host parameter
# hostname
xxxx.local
# ./certgen-linux-amd64 -host "xx.xx.xx.xx,xxxx.local"
Created a new certificate 'public.crt', 'private.key' valid for the following names 📜
 - "xx.xx.xx.xx"
 - "xxxx.local"

# ls
certgen-linux-amd64  minio  private.key  public.crt
# mv private.key ../.minio/certs/
# mv public.crt ../.minio/certs/
# ls -la ../.minio/cert
total 8
drwx------. 3 root root  54 Jan  3 11:26 .
drwx------. 3 root root  19 Jan  3 11:23 ..
drwx------. 2 root root   6 Jan  3 11:23 CAs
-rw-------. 1 root root 241 Jan  3 11:21 private.key
-rw-r--r--. 1 root root 769 Jan  3 11:21 public.crt
```

この状態で再度MinIOサーバを起動すればHTTPSアクセスになっている。

### VirtualAgentで使用可能か確認

MinIOのGUI(http://{miniohost}:9001)にアクセスし、適当にバケットを作成、トピックのソースになるファイルをアップロードする。バケットはデフォルトだとPrivateアクセスなのでPublicに変更する。GUIではわからない気がするが、オブジェクトのURLは`http://{miniohost}:9000/{bucketname}/{key}`という形式になるため、それを指定してVirtual Agentでの取り込みを実施したところ、NG。

> We couldn’t connect to this link, or it’s not secure. Make sure it’s secure (beginning with https://) and that it’s a working link.

どうやら、単にHTTPSにするだけではだめで、認証局による証明までできていないとだめなのかも。もしオレオレ証明書でもできたという方いれば教えてください…。

# 実際に少し使ってみて

自分の場合、過去の問い合わせと回答事例をそのまま取り込んで、いい感じにできないかなという期待があったのですが、結果的にそれだけではまともな運用が難しそうです。

- 各問い合わせのトリガー側に、重視したいキーワードを追加で登録する
- 似たようなトピックはできるだけまとめて、トリガーを複数パターン用意する
  
といったことをマニュアルで少しずつ整理していくと精度があがりそうなので、地道に続けることにします。