ささみ学習帳 - sasami's study book

ささみ学習帳

Microsoft365 や Power Platform について学んだこと・アイデアのメモ

Microsoft365 管理センターのサービス正常性のメール通知を翻訳してTeamsチャネルに投稿する PowerAutomate クラウドフロー (3/3) 💎Azure OpenAI版

Microsoft365 管理センターのサービス正常性のメール通知を翻訳してTeamsチャネルに投稿するこちらのクラウドフローの続きです。

(1/3)

sasami-axis.hatenablog.com

(2/3) OpenAI API

sasami-axis.hatenablog.com

 

 

このフローの概要

Microsoft365 管理センターのサービス正常性のメール通知を翻訳してTeamsチャネルに投稿するこちらのクラウドフローの続きです。Azure OpenAI  Service (以降AOAIと表記)で実施してみたバージョンです。

 

前回のOpenAI API版フローとの違い

  • OpenAI APIからAuzre OpenAI Service に変更
  • "gpt-3.5-turbo-16k"から"gpt-35-turbo-instruct" に変更
    • それに伴いChat API → Completions APIに変更
    • プロンプトの見直し
  • アダプティブカードのデザインを変更 
    • Tableを使ってみた
    • 画像表示を削除

 

完成イメージ

Microsoft365管理センターからサービス正常性メール通知を受信すると、指定したTeamsのチャネルに下図のように投稿します。

 

必要なもの

 

フロー全体図

赤枠部分が前回フローからの変更点です。

アダプティブカードに画像を表示しなくなったため関連アクションを削除しています。

 

フロー解説

1.[Office 365 Outlook] 新しいメールが届いたとき (V3)トリガー

  • フォルダー
    • 受信トレイ
  • 差出人

サービス正常性通知メールのFromアドレスを指定しています。私の知る限りこのメールアドレスから他のメールは来ていないのでこれで問題ないと考えています。

 

2.[Content Conversion]Htmlからテキスト

  • コンテンツ
    • @{triggerOutputs()?['body/body']}

 

サービス正常性通知メールの本文はHTMLメールです。AOAIに渡す際のトークン数節約の為にテキストに変換します。ちなみにこのアクションでテキスト変換した場合リンクや画像は[url]という形で残ります。

View the description of the service health
event.                                                                                                                                                                                                                                                           

Microsoft
[https://images.ecomm.microsoft.com/cdn/mediahandler/azure-emails-templates/production/shared/images/templates/shared/microsoft-2x.png]


ADMINS AND USERS MAY SEE A DELAY IN MICROSOFT TEAMS APP USAGE REPORTS WITHIN THE
ADMIN CENTER

ID: TM678339
[https://nam12.safelinks.protection.outlook.com/?url=https%3A%2F%2Fnam.safelink.emails.azure.net%2Fredirect%2F%3Fdestination%3Dhttps%253A%252F%252Fadmin.microsoft.com%252FAdminPortal%252Fhome%2523%252Fservicehealth%252F%253A%252Falerts%252FTM678339%253Fshdlinksource%253DIncidentMail%26p%3DbT0wZjdmZjJkMi0yYjI5LTQ2ZGMtYTExZi0zNjQ2YWZmOWQ3NmUmdT1hZW8mbD1ob21l&data=05%7C01%7Csasami_axis%40xvxfc.onmicrosoft.com%7C33d44d3c4c0e4972478108dbc20efef3%7Ce67f4bb8ad2944589ae283b1ed90bdbd%7C0%7C0%7C638317141916003469%7CUnknown%7CTWFpbGZsb3d8eyJWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C3000%7C%7C%7C&sdata=LEHfpLRnkVRKfmeKKLG4TGVVC%2FwNqMDvRmelDs1F9VU%3D&reserved=0]

Status

Service Restored

(以下略)

 

3.[コントロール]スコープ-AOAI completion

3-1.[データ操作]作成-API Key

あらかじめ準備しておいたAOAIのAPIキーを設定します。

 

3-2.[データ操作]作成-prompt

extract following entities from text, answer in following JSON format.
---
entities
-ID
-Title
-Status
-Impacted Services
-User impact
-Details
-URL
---
Requirements
-The " in the extracted string is replaced with '.
-Title is translated into Japanese and extracted.
-Status is translated into Japanese and extracted.
-Impacted Services is extracted in its original text.
-User impact is translated into Japanese and extracted.
-Details is translated into Japanese and extract multiple lines from the line following the 'Details' line to the line immediately before 'Are you experiencing this issue?'.  Newline characters should be represented by '\n'.
-For URL, select one that includes 'IncidentMail' near the ID.
---
Answer in the following JSON format
{
  "ID": "",
  "Title": "",
  "Status": "",
  "Impacted Services": "",
  "User impact":"",
  "Details": "",
  "URL": ""
}
---
text
@{body('Html_からテキスト')}

AOAI Completionに渡すプロンプトです。下記のようなことを指示しています。

  • テキストから指定の項目を抽出すること
    • "は'に置き換えること
    • Title, Status, User impact は日本語に翻訳すること
    • Impacted Service は原文のまま抽出すること
    • Details は'Details'から'Are you experiencing this issue?'あたりの複数行を抽出し日本語に翻訳。改行は¥nで表すこと
    • URLはIDの付近にあり'IncidentMail'を含むこと
  • 応答は指定のJSON形式とすること

 

3-3.[HTTP]HTTP-AOAI completion

  • 方法
    • POST
  • URI
    • あらかじめAOAI Studioで取得したエンドポイントURI
  • ヘッダー
    • Content-type:application/json
    • api-key:@{outputs('作成-API_Key')}
  • クエリ
    • 未設定
  • 本文
    • 下記
  • Cookie
    • 未設定
  • 認証
    • なし

[HTTPアクションの本文]

{
  "prompt": "@{outputs('作成-prompt')}",
  "temperature": 0.2,
  "top_p": 1,
  "frequency_penalty": 0,
  "presence_penalty": 0,
  "max_tokens": 4000,
  "stop": null
}

APIを呼び出します。

成功した場合は次のようなJSONで返されます。

{
  "id": "cmpl-84mXXXXXXXXXXXXXXXXXXXXXfD3eK",
  "object": "text_completion",
  "created": 1696148607,
  "model": "gpt-35-turbo-instruct",
  "choices": [
    {
      "text": "<<ここに指定のJSON形式での回答>>",
      "index": 0,
      "finish_reason": "stop",
      "logprobs": null
    }
  ],
  "usage": {
    "completion_tokens": 432,
    "prompt_tokens": 2071,
    "total_tokens": 2503
  }
}

 

3-4.[データ操作]JSONの解析

HTTPアクションの出力JSONをパースします。

  • コンテンツ
    • @{body('HTTP-AOAI_completion')}
  • スキーマ
    • 下記
{
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        },
        "object": {
            "type": "string"
        },
        "created": {
            "type": "integer"
        },
        "model": {
            "type": "string"
        },
        "choices": {
            "type": "array",
            "items": {
                "type": "object",
                "properties": {
                    "text": {
                        "type": "string"
                    },
                    "index": {
                        "type": "integer"
                    },
                    "finish_reason": {
                        "type": "string"
                    },
                    "logprobs": {}
                },
                "required": [
                    "text",
                    "index",
                    "finish_reason",
                    "logprobs"
                ]
            }
        },
        "usage": {
            "type": "object",
            "properties": {
                "prompt_tokens": {
                    "type": "integer"
                },
                "total_tokens": {
                    "type": "integer"
                }
            }
        }
    }
}

 

3-6.[データ操作]JSONの解析-content

AOAIは、作成-system contentで指定したアダプティブカードのJSON形式で回答するよう指示していますのでこJSONをパースします。

@{first(body('JSON_の解析')?['choices'])?['text']}
{
    "type": "object",
    "properties": {
        "ID": {
            "type": "string"
        },
        "Title": {
            "type": "string"
        },
        "Status": {
            "type": "string"
        },
        "Impacted Services": {
            "type": "string"
        },
        "User impact": {
            "type": "string"
        },
        "Details": {
            "type": "string"
        },
        "URL": {
            "type": "string"
        }
    }
}

 

4.[Microsoft Teams]チャットやチャネルにカードを投稿する

  • 投稿者
    • フローボット
  • 投稿先
    • Channel
  • Teams
    • 通知するチームを選択
  • Channel
    • 通知するチャネルを選択
  • Adaptive Card
{
    "type": "AdaptiveCard",
    "body": [
        {
            "type": "Container",
            "items": [
                {
                    "type": "TextBlock",
                    "text": "@{body('JSON_の解析-content')?['ID']} - @{body('JSON_の解析-content')?['Title']}",
                    "wrap": true,
                    "weight": "Bolder"
                }
            ],
            "bleed": true,
            "style": "attention"
        },
        {
            "type": "Table",
            "columns": [
                {
                    "width": 1
                },
                {
                    "width": 4
                }
            ],
            "rows": [
                {
                    "type": "TableRow",
                    "cells": [
                        {
                            "type": "TableCell",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "影響サービス",
                                    "wrap": true
                                }
                            ]
                        },
                        {
                            "type": "TableCell",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "@{body('JSON_の解析-content')?['Impacted services']}",
                                    "wrap": true
                                }
                            ]
                        }
                    ]
                },
                {
                    "type": "TableRow",
                    "cells": [
                        {
                            "type": "TableCell",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "状況",
                                    "wrap": true
                                }
                            ]
                        },
                        {
                            "type": "TableCell",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "@{body('JSON_の解析-content')?['Status']}",
                                    "wrap": true
                                }
                            ]
                        }
                    ]
                },
                {
                    "type": "TableRow",
                    "cells": [
                        {
                            "type": "TableCell",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "ユーザーへの影響",
                                    "wrap": true
                                }
                            ]
                        },
                        {
                            "type": "TableCell",
                            "items": [
                                {
                                    "type": "TextBlock",
                                    "text": "@{body('JSON_の解析-content')?['User Impact']}",
                                    "wrap": true
                                }
                            ]
                        }
                    ]
                }
            ],
            "gridStyle": "default"
        },
        {
            "type": "TextBlock",
            "wrap": true,
            "id": "details",
            "text": "@{body('JSON_の解析-content')?['Details']}",
            "separator": true
        },
        {
            "type": "ActionSet",
            "actions": [
                {
                    "type": "Action.OpenUrl",
                    "title": "詳細",
                    "url": "@{body('JSON_の解析-content')?['URL']}"
                }
            ]
        },
        {
            "type": "TextBlock",
            "wrap": true,
            "size": "Small",
            "text": "@{body('JSON_の解析-content')?['URL']}"
        }
    ],
    "msteams": {
        "width": "Full"
    },
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.4"
}

 

5.[コントロール]スコープ-チームメンバーにフィード通知

2.の「チャネルやチャットにカードを投稿する」アクションの通知は簡素なので、別途フィード通知を送るようにしています。

5-1.[Office 365 Groups]グループメンバーの一覧表示

  • グループID
    • 4.で指定したチーム

フィード通知は1ユーザーごとに送信する必要があるので、チームメンバーを取得します。

 

5-2.[コントロール]Apply to each-ユーザーごと

  • 以前の手順から出力を選択
    • @{outputs('グループ_メンバーの一覧表示')?['body/value']}

5-1で取得したユーザー1人づつに次のアクションを実行します。

5-2-1.[Microsoft Teams]フィード通知を投稿する

  • 投稿者
    • フローボット
  • 通知の種類
    • チーム
  • 受信者
    • @{items('Apply_to_each-ユーザーごとに')?['userPrincipalName']}
  • 通知テキスト
    • @{body('JSON_の解析-content')?['ID']} - @{body('JSON_の解析-content')?['Title']}
  • Team
    • 2でメッセージを投稿したチーム
  • Channel
    • 2でメッセージを投稿したチャネル
  • メッセージID
    • @{outputs('チャットやチャネルにカードを投稿する')?['body/id']}

 

以上です。

 

参考にした情報

アダプティブカードのTable早速使わせていただきました!!

 

今後やってみたい事

Ai Builder でこちらの機能が利用可能となったら、AOAIを呼び出す代わりに試してみたいと考えています。

Power Automate (プレビュー) でテキスト世代モデルを使用する | Microsoft Learn

 

さいごに

新しいTeamsではアダプティブカードが既定で省略表示されてしまう仕様が悲しいです。