面倒なことは、Azure OpenAI Service×Power Automateにやらせよう！

# 初めに
昨今、何かとMicrosoftと生成AIが話題ですね(^^♪
**この波に乗っていく＆生成AIを使って自分の仕事を楽にする**ために取り組んだ内容を記載します。

# 目次
- [生成AI×RPA（Robotic Process Automation）の組み合わせは最高！](#生成airparobotic-process-automationの組み合わせは最高)
- [自己紹介](#自己紹介)
- [記事作成の背景](#記事作成の背景)
- [解決したい課題](#解決したい課題)
- [OpenAIとAzure OpenAI Serviceの違い](#openaiとazure-openai-serviceの違い)
- [Power Automateとは](#power-automate-とは)
- [処理フロー](#処理フロー)
- [データフロー](#データフロー)
- [Power Automate全体のフロー](#power-automate全体のフロー)
- [アウトプット事例](#アウトプット事例)
- [工夫点](#工夫点)
- [特に苦労した点](#特に苦労した点)
- [Process Mining](#process-mining)
- [まとめと予定](#まとめと予定)


# 生成AI×RPA（Robotic Process Automation）の組み合わせは最高！
生成AIだけでも知能労働の支援はしてくれますが、更に単純労働まで自働化できたら最高じゃないですか？そう、**人間は考えることも、手を動かすことも面倒なんです！**


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/ade13595-200b-f47e-6dea-beb74277c677.png)


# 自己紹介
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/3c1a0abf-9312-9d3f-230b-10b3f5ac458b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/b63287cf-1488-1d32-da82-b1951cfce164.png)



# 記事作成の背景
前職では生成AI技術は全く使っていなかったため、**素早く技術にキャッチアップするために何か作ってみる**ことにしました。生成AI案件にアサインされるために、**何か作ってアピールしたかった**という理由もあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/3d8fbfd0-f74e-494d-5dc3-2aa05e1f722b.png)

# 解決したい課題
外資系企業に転職して、大量の英語メールが来るようになりました。
英語が不得意＆業務が立て込んできて全て読んでいられません。


そこで、**生成AIに日本語に翻訳させた上で、要約させることにしました。その上で、自分宛にメールを再送します。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/dd87b3f4-db0b-79c1-66c7-f1883bd281b6.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/cd4eb406-79b3-6b33-7259-0d04645f29f8.png)

# OpenAIとAzure OpenAI Serviceの違い
今回の生成AIによる処理にには、**Azure OpenAI Service**というクラウドサービスを使っています。このサービスを言語モデルにOpenAI社のGPTモデルを使っています。


それでは、 **本家OpenAIとAzure OpenAI Serviceで何が違うの？** との疑問が生まれます。実際、お客さんに必ずと言ってよいほど聞かれます。違いの概要は以下です。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/19a6819f-3090-05e3-95c7-93a9df85f96d.png)

Azure OpenAI Serviceを使う最大のメリットは、他のAzureインフラアーキに組み込み易い事です。


これにより、 **Azureの強固なセキリュティを享受しつつ、他のAIサービス（[Azure AI Document Intelligence](https://azure.microsoft.com/ja-jp/products/ai-services/ai-document-intelligence)や、[Azure AI Search](https://azure.microsoft.com/ja-jp/products/ai-services/ai-search) 等）の使用が可能になります。**



昨今、需要が増加しているRAG（Retrieval-Augmented Generation：拡張検索生成）システムを組む際、言語モデル単体では機能しません。Azure AI Search等の検索機能を組み合わせて使う必要があります。 


**Azure OpenAI Serviceを生成部に使っていると、統合が用意ですし、アーキ全体でのコスト管理もし易いです。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/094440e6-36b9-920c-0a21-05bc69f237dc.png)

# Power Automate とは
**Azure OpenAI Service（頭脳部）だけあっても、私の課題は解決しません。**


手足となって動くロボットが必要です。そこで、Azure OpenAI Serviceと相性が良いRPA（Robotic Process Automation）ツールがPower Automateです。同じくMicrosoft製品です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/6d2f0b7f-69ab-314a-6b3a-7a2e8048d2fd.png)

Power Automateには、上記2種類があります。今回は、**「Outlookに受信したメールをトリガーに処理を開始する」** ことがやり易そうなPower Automate Cloudを使うことにしました。


また、デバッグ機能があることもPower Automate Cloudの大きな利点です。


Power Automate CloudとPower Automate Desktopの違いは以下の記事を参考にさせていただきました。


https://www.cm-net.co.jp/blog/power-automate/

Power Automate Cloudは有料ですが、以下のページから30日間の無料トライアルに申し込めます。

https://www.microsoft.com/ja-jp/power-platform/products/power-automate#tabs-pill-bar-ocb9d4_tab3

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/680cf693-e1c5-8e1b-0063-fe41f252c53e.png)


# 処理フロー
めちゃくちゃ、シンプルです（笑）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/61db225e-a525-878d-301a-e1dab12074cd.png)

日本語メールを誤って処理すると、Azure OpenAI Serviceのコストが無駄になるので、件名で処理の可否を判断しています。

# データフロー

データフローは以下の様になります。


- インプット：Outlookで受信したメール
- アプトプット：日本語訳＆翻訳したメール

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/f1e1cd58-45df-a726-e5e9-b37b2345450e.png)

# Power Automate全体のフロー
7つのコンポーネントからなります。
各コンポーネント詳細設定について、以降で解説します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/54dae064-ccd1-029a-3412-90899080beb8.png)

コンポーネントの作成方法は以下の記事を参考にさせていただきました🙇‍♂️

https://qiita.com/t_yoshikawa/items/e698ecb751d1f2724e4f

## 「新しいメールが届いたとき」の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/15b6ba51-18b6-e5d3-7bdd-4d9255c789ea.png)

こちらは、**OutlookのInboxフォルダに新しいメールが届いた際にトリガーを立てる機能を担います。**

## 「変数を初期化する」の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/2b905e8e-97f2-f332-96c8-43d7882fa99e.png)

ここでは、**メールの件名を"Subject"という変数に格納しています。**

## 「変数を初期化する（ユーザープロンプト）」の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/a8af935f-70c4-79e7-deea-383d57d23ba1.png)


ここでは、**ユーザープロンプトを"User Prompt"という変数に格納しています**。変数の値に、メール本文を含めているのがポイントです。

## 「変数を初期化する（回答用変数）」の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/752d1de3-fb69-b02f-5816-4b17badb0031.png)

ここでは、**Azure OpenAI Serviceからの回答を格納する変数を定義しています。** この時点では空です。

## 「変数を初期化する（回答用変数）」の設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/fa91eb0e-7877-27c2-cb4b-1c1037146270.png)

ここでは、返信メールの件名に付与する注意書き（※後ほど説明）を"note"という変数に格納しています。

## 「言語の検出」の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/080a6753-1a95-5562-9eff-3f4ea811bd0f.png)

ここでは、**[Azure AI Language](https://azure.microsoft.com/ja-jp/products/ai-services/ai-language)のを使って、件名の言語名取得しています。**

言語検出については以下を参照ください。

https://learn.microsoft.com/ja-jp/azure/ai-services/language-service/language-detection/overview

言語検出でサポートされている言語は以下に記載あり。

https://learn.microsoft.com/en-us/azure/ai-services/language-service/concepts/language-support

## Apply to eachの設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/2e553904-ccd9-d354-2abe-72f30a71cbc2.png)

ここでは、　**初めにAzure AI Languageの出力（documents）をインプットし、その中の"name"属性がEnglishに等しいか判定します。はいの場合（メールの件名が英語である場合）左のフローが走ります。**　いいえの場合は、何もせず終了します。

## はいの場合の処理と設定
### 「HTTP（Azure OpenAI Serviceを呼び出し）」の設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/becf8ecc-23e6-8830-918d-7956e94befe2.png)


ここでは、　**本文の内容をAzure OpenAI ServiceにPOSTします。contentに前段で設定した"User Prompt"変数を格納しているのがポイントです。**

言語モデルのURIは以下の形式にしてください。

```
POST https://{your-resource-name}.openai.azure.com/openai/deployments/{deployment-id}/completions?api-version={api-version}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/eca7fabf-34c3-a053-845f-faaef4e77b07.png)
出典：Azure OpenAI Service の REST API リファレンス


APIのバージョンは以下のページを参照ください。

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/reference

### 「JSONの解析」の設定


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/a003c247-71f8-9168-4803-f2668c317311.png)

ここでは、 **Azure OpenAI Serviceからの回答内から、必要箇所（日本語要約文）だけを取り出すために回答のJSON形式を定義しておきます。** 


サンプルから生成 ⇒ サンプル JSON ペイロードの挿入に、以下のJSONを張り付けて、完了ボタンを押してください。


```
{
    "type": "object",
    "properties": {
        "choices": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "content_filter_results": {
                        "type": "object",
                        "properties": {
                            "hate": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "self_harm": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "sexual": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "violence": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    },
                    "finish_reason": {
                        "type": "string"
                    },
                    "index": {
                        "type": "integer"
                    },
                    "logprobs": {},
                    "message": {
                        "type": "object",
                        "properties": {
                            "content": {
                                "type": "string"
                            },
                            "role": {
                                "type": "string"
                            }
                        }
                    }
                },
                "required": [
                    "content_filter_results",
                    "finish_reason",
                    "index",
                    "logprobs",
                    "message"
                ]
            }
        },
        "created": {
            "type": "integer"
        },
        "id": {
            "type": "string"
        },
        "model": {
            "type": "string"
        },
        "object": {
            "type": "string"
        },
        "prompt_filter_results": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "prompt_index": {
                        "type": "integer"
                    },
                    "content_filter_results": {
                        "type": "object",
                        "properties": {
                            "hate": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "self_harm": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "sexual": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            },
                            "violence": {
                                "type": "object",
                                "properties": {
                                    "filtered": {
                                        "type": "boolean"
                                    },
                                    "severity": {
                                        "type": "string"
                                    }
                                }
                            }
                        }
                    }
                },
                "required": [
                    "prompt_index",
                    "content_filter_results"
                ]
            }
        },
        "system_fingerprint": {},
        "usage": {
            "type": "object",
            "properties": {
                "completion_tokens": {
                    "type": "integer"
                },
                "prompt_tokens": {
                    "type": "integer"
                },
                "total_tokens": {
                    "type": "integer"
                }
            }
        }
    }
}
```


このJSONは、一度右上のテストボタンから、テストを実行し、Azure OpenAI Serviceからのレスポンスをコピーしたものになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/54ac5186-70a0-02b5-9aaf-e177828bceda.png)

### 変数の設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/35985b71-1613-9fa4-9288-e03f8a9ca24b.png)

ここでは、**"response"という変数を定義して、そこにAzure OpenAI Serviceからの回答（JSONの解析済み）のbody部を格納しています。**

値欄のbodyをクリックして、以下の式を設定して更新してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/e0fcd368-6478-1c4a-f7d5-56406458879a.png)

```
設定式　：　body('JSON_の解析')?['choices'][0]?['message']['content']
```

### 「メールの送信（V2）」の設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/f2c0de1b-e38f-16c0-6e45-b7b79a9b205b.png)

最後に、件名に"note"変数、"Subject"変数設定、本文に"response"を設定します。宛先には、自分宛のメールアドレスを入れてください。

# アウトプット事例
上記フローを作成し、フローの表示画面から、当該フローをオンにします。
すると、Outlookにメールが届くたびに処理が走ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/6ccaf61c-b6ff-a432-458d-f8a909af2ec2.png)

以下は、元のメールと処理後に送信されたメールの例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/6c7f38c7-f7b8-a730-ca44-6f0549031b1c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/b16c6dfe-e675-e1d7-0f77-f09ec2cb70eb.png)

# 工夫点
とは言え、元のメールを全く読まないのは不安です。
また、要約メールを元もメールと混同して重要な情報を見落とすリスクがあります。


そこで、件名の前に、「**これはAOAIによる要約です！**」　という自分への警鐘（？）を付けて、内容を信じ過ぎないよう意識しました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/cd629169-8c24-d599-33f8-d34ec1e354a2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/9520b8d8-e055-72f6-61ee-5aa287ae0162.png)

# 特に苦労した点
Webの知識が乏しく、「JSONの解析」に特に苦労しました。
以下の図のように、不要なレスポンスも返信メールに含めてしまっていました。これをクリアするのに、結構時間が掛かりました(;^_^A

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/1da3139b-cd0b-0fc4-9e0e-4928e3cfd744.png)

# Process Mining
Power Automateには、プロセスを解析できる便利な機能があります。
以下は、処理平均時間を表示した図です。 **やはり、Azure OpenAI Serviceからのレスポンスに圧倒的に時間を要しています。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/bc1b1104-8018-73e3-53ce-3e792453f8d5.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/03819bbb-33b5-0039-83d2-18ef4b95c8ac.png)



# まとめと予定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318960/aeb7c995-b689-d5aa-811f-588e3b67c9a1.png)

ここまで、読んでくださりありがとうございました。
皆様の仕事が、 Azure OpenAI Service× Power Automateで少しでも楽になることを祈っております😊