AzureOpenAIとPowerAutomateでメール要約をスマートに自動化

## はじめに

この記事では、PowerAutomateとAzureOpenAIを組み合わせて、メールの自動翻訳＋要約と課題登録を行うワークフローの作成方法について解説します。

## やりたいこととその背景

### 今回改善したいこと

Azureを利用していると、マイクロソフトからメンテナンス周知や対応依頼などさまざまなメールが来ます。メールを受信してから具体的なアクションに落ちるまでにはいくつか課題があり、今回はその解消を目指します。

- メールを課題登録するのが大変
  - メールには特定のアクションを要求するものもあるため、課題として登録する必要があります。
- メールの内容把握が大変
  - メールは英語で書かれていて、我々が本当に知りたいこと以外のことも書いてあります。

メール確認はシステム運用中継続的に続く作業であり、作業者としてもうんざりしてしまう箇所かと思っています。

### 今回実装する内容

マイクロソフトからメールが来た時にAzureOpenAIを用いて内容を翻訳＆要約しその結果を自動でJira Softwareに課題登録するワークフローを作ります。

全体のフローは下記のようになります。

![PowerAutomateの全体構成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/4927b850-ee2d-d967-d724-2c718121e052.png "PowerAutomateの全体構成")

マイクロソフトからメールが送られてくると自動でワークフローが走り、AzureOpenAIを用いて内容を翻訳+要約したうえで課題管理用にJira Softwareに登録します。要約内容に誤りがある可能性を考慮して要約とともに元メールの情報もセットで登録します。

内容把握についてすべての課題が解消されるわけではありませんが、パッと見で内容を理解できるようにすることで時間短縮を図ります。

また、今回はPowerAutomateとAzureOpenAIが焦点ですので、Jiraへの連携に関しては参考程度といたします。

## 必要なリソース

本記事では下記リソースがすでに存在していることを前提としています。

- Microsoft PowerAutomateアカウント
- AzureOpenAIサービス

:::note warn
2023/12/19時点で、AzureOpenAIの利用には現状**マイクロソフトへの申請**が必要です。また申請時にはメールアドレスの登録が必要で**フリーアドレスの場合は申請が拒否**される仕様になっています。
:::

AzureOpenAIサービスではGPT4でチャットできるモデルをデプロイしておくことをおススメします。要約力に大きな違いがあります。私も「gpt-4-1106-Preview」を使っています。

## ワークフローの概要

1. PowerAutomateでメールをトリガーとするフローを作成
2. AzureOpenAIを使用してメール内容を要約
3. （参考）要約されたテキストを課題管理ツールに登録

フロー全体は下記のようになります。

![フロー全体図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/f1de23fb-35d2-f2b7-b510-6f2d8e71207d.png "フロー全体図")

## ① メールトリガーの設定

最初に下記部分を設定します。

![メールトリガーの設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/d2373458-a0f5-b29d-8ee5-fbcfd690401a.png "メールトリガーの設定")

「トリガーの追加」ボタンを押してトリガーを追加します。すでに他のトリガーが設定されているときは削除が必要です。

![トリガーの追加](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/260e11fc-dd26-c5e8-c8c1-1af49d75e8de.png "トリガーの追加")

「outlook」と検索し「新しいメールが届いたとき（v3）を設定

![outlookコネクタを選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/4fd6bd6d-5bc1-c444-5b44-236ef4f1c95a.png "outlookコネクタを選択")

ここにワークフローを走らせるメールの条件を指定します。今回は`azure-noreply@microsoft.com`から`sample@biprogy.com`に届いたメールに対してワークフローを走らせます。受信したメールのフォルダー設定も絞れるため、outlookのメール振り分け設定と合わせればかなり柔軟に設定できます。

![トリガ設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/a369c778-4c15-c502-5cda-9741e4e1e8c8.png "トリガ設定")

これでトリガーの設定は完了です。

## ② AzureOpenAIを用いてメール内容を要約

次にAzureOpenAIの部分のワークフローを作成します。下記部分を設定します。

![AzureOpenAIのAPI呼び出し](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/cc6e3c01-faec-f47d-d82d-98bbfd6e3f91.png "AzureOpenAIのAPI呼び出し")

### 変数設定

AzureOpenAIに渡すプロンプトを設定しておく変数と回答を保存する変数を作成します。

#### プロンプト用変数

プロンプトは雑ですが、いったん下記のように設定します。

![プロンプト内容](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/f6df9d99-416d-b5fe-cf89-b4cb92b8c140.png "プロンプト内容")

```markdown
# 命令文
Azureに関するマイクロソフトからのメールを要約してください。
発生している事象の内容と必要な対処の有無、その内容が最も重要です。

# メール内容
<メールの本文>

# 出力
日本語で300文字程度で出力してください。
```

「メール内容」にはトリガーで取得したメールの本文を記載しています。下記の稲妻ボタンから

![前の手順のデータ取得](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/97f9cd05-5ed8-a301-6393-ccc7fe45d4e4.png "前の手順のデータ取得")

「新しいメールが届いたとき（V3）」の「本文」を選択します。

![本文の取得](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/52bb8af2-d014-1cdb-d6d7-4defaea651df.png "本文の取得")

これでメールの本文がプロンプトの「# メール内容」の下に埋め込まれます。

#### 回答用変数

回答用変数は空で定義しておきます。後でAzureOpenAIが回答した内容をここに格納します。

![回答用変数](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/5fff0fb4-2e08-c911-5f95-f5c39cfa6d0e.png "回答用変数")

### AzureOpenAIのAPI呼び出し

いよいよAzureOpenAIの出番です。「HTTP」コネクタを呼び出し下記のように設定します。

![AOAIへのリクエスト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/98d3fec7-7952-9848-f152-18defb06487a.png "AOAIへのリクエスト")

```json
{
  "messages": [
    {
      "role": "system",
      "content": "あなたは私のAIアシスタントです。できるだけ簡潔に回答してください。"
    },
    {
      "role": "user",
      "content": <content変数の値>
    }
  ]
}
```

`URI`と`api-key`は環境によって異なります。
どちらもAzure AI Studioから「コードの表示」を押すことで取得できます。

![プレイグラウンド画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/1052c39d-a8a8-92f4-79f5-c3e40cac1da2.png "プレイグラウンド画面")

「サンプルコード」の下の「エンドポイント」「キー」に表示されています。

![「コードの表示」画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/3fc77372-1635-3ed9-ee5d-732ea5833f56.png "「コードの表示」画面")

:::note alert
api-keyは流出すると不正利用のリスクがあります。管理には最新の注意を払ってください。
:::

### JSONの解析

「HTTP（AOAIにチャット）」で得られた内容をこの後使うわけですが、レスポンスのJSON構造をPowerAutomateが認識していないため、このままだと後続の設定でレスポンス内容を呼び出すときに複雑な式を手動で書くことになります。

その回避のため、ここでレスポンスの形式を定義しておきます。ここまでの内容で1回PowerAutomateをテスト実行し、「HTTP（AOAIにチャット）」の出力結果をコピーします。

コピーした内容を下記「JSONの解析」コネクタの「サンプルのペイロードを使用してスキーマを生成する」を選択し、フォームに貼り付けます。

※Schema欄に直接入力はしません。「サンプルのペイロードを使用してスキーマを生成する」から入力すると自動で入力されます。

![JSONの解析](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/c0beb078-946f-27ce-b25a-da4c5f1c8c8f.png "JSONの解析")

こうすることで後のコネクタで「HTTP（AOAIにチャット）」の出力をプロンプトの変数設定の箇所でしたようにGUIで選択できるようになります。

参考までにこちらでSchemaに登録された内容を記載します。（※「サンプルのペイロードを使用してスキーマを生成する」から入力したあと、「Schema」に自動表示された内容です。）

<details><summary>Schemaの登録内容</summary>

```json
{
    "type": "object",
    "properties": {
        "choices": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "finish_reason": {
                        "type": "string"
                    },
                    "index": {
                        "type": "integer"
                    },
                    "message": {
                        "type": "object",
                        "properties": {
                            "content": {
                                "type": "string"
                            },
                            "role": {
                                "type": "string"
                            }
                        }
                    }
                },
                "required": [
                    "finish_reason",
                    "index",
                    "message"
                ]
            }
        },
        "created": {
            "type": "integer"
        },
        "id": {
            "type": "string"
        },
        "model": {
            "type": "string"
        },
        "object": {
            "type": "string"
        },
        "system_fingerprint": {
            "type": "string"
        },
        "usage": {
            "type": "object",
            "properties": {
                "completion_tokens": {
                    "type": "integer"
                },
                "prompt_tokens": {
                    "type": "integer"
                },
                "total_tokens": {
                    "type": "integer"
                }
            }
        }
    }
}
```

</details>

### 回答を変数に格納

AzureOpenAIからの回答を`answer`という変数に格納します。

「変数の設定」コネクタを選択してください。

見本では「For each」内に設定されていますが、これは変数の内部を設定した段階で自動で「For each」内に移動されるため気にしないで大丈夫です。

設定は下記のようにおこないます。

![変数の設定（回答内容抽出）](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/355d75b5-72cf-b930-d342-00bf3cb94ef5.png "変数の設定（回答内容抽出）")

Valueの部分は「プロンプト用変数」の箇所でおこなったように稲妻ボタンから設定します。「JSONの解析」が正常にできていれば「JSONの解析」の欄から下記のように`Body content`が選択できます。

![Body Content](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/36f38065-c586-2c18-3052-e92ca73583f4.png "Body Content")

## ③ （参考）課題管理ツールへの登録

最後に参考としてJiraへの登録部分を設定します。

![課題管理ツールへの登録](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/775e354b-a010-5768-d6a9-005bc4f7b96b.png "課題管理ツールへの登録")

PowerAutomateにはJira用のコネクタが用意されています。ただ、私の場合はなぜかエラーになって使えなかったため、Jiraの自動化機能で課題登録用のincoming webhookを別途作成し、そこをキックするようにしています。

### Jira側のIncoming Webhook設定

私は下記のように要約（紛らわしいですがここでは課題件名のことです）と説明の内容で新規課題登録する用のシンプルなIncoming Webhookを作成しています。

![Jiraのwebhook設定](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/daf5a23c-d3b7-b9ce-0927-d18e24266763.png "Jiraのwebhook設定")

「説明」には下記の内容を記載しています。

```markdown
h2. 要約 

*※Powered by AzureOpenAI (誤りが含まれていることがあります）*

{{webhookData.summary}}

h2. メール情報

- 受信日時: {{webhookData.date}}
- 差出人: {{webhookData.from}}
- メール件名: {{webhookData.title}}

h2. 本文

{{webhookData.preview}}
```

`{{webhookData.xxxxx}}`の部分に関してはwebhookへのリクエストのbody部分の変数を利用します。`{{webhookData.preview}}`であればbody内のpreviewの内容を表示します。

Jiraの自動化機能のwebhook機能に関しては下記を参照してください。

https://support.atlassian.com/cloud-automation/docs/jira-automation-triggers/#Automationtriggers-Incomingwebhook

### PowerAutomate側の設定

「HTTP」コネクタを使い下記のように設定します。AzureOpenAIからの回答だけであれば`answer`変数だけでよいですが、課題で元メールの情報も表示したいため、元メール情報も一緒に連携しています。

![Jiraへのリクエスト](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/e77028cd-b819-6c12-8b08-6746c387b6b5.png "Jiraへのリクエスト")

Body内の変数についてはJira側で使っている`{{webhookData.xxxxx}}`の内容と整合性をとる必要があります。

## 実行結果

実行結果としてJira側に作成された課題の画像を添付します。要約結果と元メール情報が表示されています。

![実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3640132/14e36bce-3895-a7e0-e30c-c18714c4da13.png "実行結果")

しかし、プロンプトで「300文字で要約してください」程度しか言っていないのもあり要約の内容としては工夫の余地としてはまだまだ、、、改善の余地がありそうです。

## コストに関して

AzureOpenAIのコストに関しては、私はAU Eastリージョンの「gpt-4-1106-Preview」モデルを用いてメール1通で約2円程度だったと思われます。（厳密に1回あたりの金額を求めるのが難しいですが）

メールの分量やプロンプトの定義によっても変わりますが、ひと月に来るマイクロソフトメールの数を考えてもそれほどコストはかからないので、気になる方はまずは評価してみるのがおススメです。

## さいごに

PowerAutomateとAzureOpenAIを組み合わせることで、日々のメール管理を効率化し、より重要な業務に集中できるようになります。

日本では生成AIへの期待度はかなり高いものの、活用という意味ではまだあまり進んでいないのが現状です。

一部jsonコードはあるものの、PowerAutomateを使えば多くの部分はノーコードで作成できますので、この投稿をきっかけに少しでも挑戦してみようという人が増えればと思っています。

## We Are Hiring

BIPROGYでは一緒に働く仲間を募集しています。ご興味ある方は下記をご参照ください。

https://www.biprogy.com/recruit/recruiting/