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

ささみ学習帳

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

Power Apps で利用できる空間サービスについて確認してみた(調査中)

はじめに

Power Apps のキャンバスアプリでマップコントロールまたは住所入力コントロールを追加すると、「空間サービス」というものが利用できるようになります。

これらは2つのコントロールで内部的に使われているものなのかもしれませんが、それぞれ単体で呼び出すことが可能です。この空間サービスについてのドキュメントが見つからなかったので自力で調べてみたところまでのメモです。

私の推測も含む内容ですので誤まっている可能性があることをご注意ください。

 

空間サービスの機能

これらを利用するためにはBing Maps API, Azure Map API それぞれ対応するAPIキーの取得が必要になります。

 

さいごに

地図を表示するアプリを作っていた表示されたので気になって調べてみました。

調べて検証してみたものの使いどころが見いだせなかったのでこの段階で記事にまとめています。

  • ジオコーディングできる!→日本語の情報が乏しい
  • 地図画像が取得できる!→Bing Maps コネクタや対話型マップコントロールでいいよね?

と明確な欠点やより容易な手段があるためです。
どなたかの助けになる事でもあれば幸いです。

何か用途が思いついたら続きを書くかもしれません。

 

 

 

【参考】地理空間機能の完全サポートを有効にする

地理空間機能の完全サポートを有効にするには、Power Plartform管理センターで環境に設定が必要です。

 

Power Platform管理センターにアクセス→環境→…→設定

機能→製品→機能

マップと住所サービス→完全 をオン

サービス使用条件に同意し有効化

機能ページの下端で保存

数分待つと完全サポートが有効になります。

 

 

learn.microsoft.com

 

 

Computer Vision API コネクタをPower Automateクラウドフローで使う場合の注意事項(2024年2月28日現在)

 

はじめに

最近Power Automate クラウドフローでComputer Vision API コネクタのアクションを使ってみてハマった注意点です。複数テナントで現象が再現できたので記事にまとめておきます。

2024年2月28日現在、Computer Vision APIコネクタのアクションを利用する際に下記の条件に該当するとフロー実行時に「Bad Request」となる現象を確認しています。

 

発生条件

下記の条件をすべて満たしている時に発生しています。

  • モダンデザイナーで該当アクションを設定している
  • 画像のソースが"Image Content" or "Image URL"が選択式のアクションを利用している
  • 画像のソースに"Image Content"を選択し、Imageに動的な値を設定している

 

 

発生するアクション

2024年2月28日時点ではComputer Vision APIコネクタには下記のアクションがありますがほとんどで発生します。

アクション 現象発生 備考
画像の分析 あり  
画像を分析する(V3) あり  
オブジェクトを検出する あり  
オブジェクトを検出する(V3) あり  
サムネイルを作成する あり  
サムネイルを作成する(V3) あり  
ドメイン固有のコンテンツを認識する あり  
ドメイン固有のコンテンツを認識する(V3) あり  
画像にタグをつける(V3) あり  
画像のURLを記述する しない コンテンツ指定がない
画像のURLを説明する(V3) しない コンテンツ指定がない
画像の内容を記述する しない 画像ソースはコンテンツ固定
画像の内容を説明する(V3) しない 画像ソースはコンテンツ固定
画像をタグ付け あり  
画像を記述する あり  
画像を説明する(V3) あり  
関心ある分野を取得する(V3) あり  
関心の領域を取得する あり  
光学式文字認識(OCR)でJSONに抽出 あり  
光学式文字認識(OCR)でJSONに抽出(V3) あり  
光学式文字認識(OCR)でテキストに抽出 あり  
光学式文字認識(OCR)でテキストに抽出(V3) あり  

 

現象の回避方法

クラシックデザイナーを使う

モダンデザイナーで該当アクションを編集しない事です。クラシックデザイナーで設定したアクションではこの現象は発生しませんでした。

 

原因の推測

ここからは完全な私の憶測です。クラシックデザイナーとモダンデザイナーで同じ「光学式文字認識(OCR)でテキストに抽出」アクションを設定しコードビューで比較すると、"Image"パラメータに違いがあることがわかりました。

画像

  • クラシックデザイナーで設定した"Image"
    • ”Image": "@body('Get_file_content_using_path')"
  • モダンデザイナーで設定した"Image"
    • ”Image": "@{body('Get_file_content_using_path')}"

この{}の存在が原因の可能性が高いと考えられます。

 

Computer Vision APIコネクタ以外でも発生するのか?

普段私が使っているコネクタで上記の発生条件を満たすものはComputer Vision API コネクタだけでした。他のコネクタのアクションでの発生は私は確認できておりません。

 

さいごに

Power Automateのモダンデザイナーは素性は悪いものではないと思いますが、未完成と思われる個所や、今回のような不具合が散見されあまり評判がよくないと感じます。

とはいえ愚痴っていても何も解決しませんので問題を見つけたらフィードバックをしましょう。モダンデザイナーの「フィードバックを送信する」をクリックして表示されるメニューの3に記述します。Google 翻訳などで英語に翻訳して張り付けるとベターです。

4.に自分のメールアドレスを入力しておくと、後日追加調査の依頼が届くことがあります。ちょっとした手間で問題解決が少し早まるのであれば自分にもメリットがあるので私も何度かやり取りしたことがあります。

 

 

ジオコーディングサービスGeocoding.jp APIをPower Automate クラウドフローから利用する💎

 

ジオコーディングとは

住所などのキーワードから緯度・経度を求めることをジオコーディングといます。PowerAppsでは標準でBingMapsでジオコーディングできますが日本語への対応はいまいちです。そこで、無料で使えるジオコーディングサービスGeocoding.jp API をPowerAutomateクラウドフローで試してみました。

※PowerAutomate クラウドフローでHTTPコネクタを使用、Power Appsで対話型マップコントロールを仕様するためPower Automate Premium等の単体ライセンスが必要になります。

 

今回の想定

今回のクラウドフローはPowerAppsから呼び出す想定のフローです。

アプリで入力されたキーワードをクラウドフローで検索して緯度・経度を取得し、アプリで地図上に表示します。

  1. Power Apps:検索キーワードを入力
  2. Power Apps:キーワードを引数にクラウドフローを呼び出し
  3. Power Automate:ジオコーディングサービスで緯度経度を取得
  4. PowerApps:返された緯度・経度を基に地図を表示

 

動作イメージ

こんな感じで動作させてみました。

 

geocoding.jpとは

個人で運営されているジオコーディングのサイトです。

登録なしで使用できます。

検索の頻度を、10秒に1回程度に抑えてください。」と制限が明記されています。

www.geocoding.jp

 

API仕様

http://www.geocoding.jp/api/?q={query}

認証不要なためとてもシンプルに使えます。

レスポンスはXML形式で下記のような値が返ってきます。

[正常な応答]

<?xml version="1.0" encoding="UTF-8" ?>
<result>
<version>1.2</version>
<address>富士山</address>
<coordinate>
<lat>35.360625</lat>
<lng>138.727363</lng>
<lat_dms>35,21,38.25</lat_dms>
<lng_dms>138,43,38.508</lng_dms>
</coordinate>
<open_location_code>8Q7W9P6G+6W</open_location_code>
<url>https://www.geocoding.jp/?q=%E5%AF%8C%E5%A3%AB%E5%B1%B1</url>
<needs_to_verify>yes</needs_to_verify>
<google_maps>静岡県富士宮市北山 富士山</google_maps>
</result>

[キーワードに該当する情報が存在しなかった場合]

<?xml version="1.0" encoding="UTF-8" ?>
<result>
<address>存在しないキーワードの何か</address>
<error>001</error>
</result>

 

Power Automate フロー全体図

 

Power Automate フロー解説

1.[Power Apps] PowerApps (V2)

Power Appsからキーワードを引数として渡すためのトリガーです。

  • 入力1
    • 文字列
    • UserInput

 

2.[変数]変数を初期化する-Response_XML

Power Appsへ解す値を格納する変数を定義します。初期値として取得できなかった場合に返すXMLを設定します。

  • 名前
  • 種類
    • 文字列
    • <?xml version="1.0" encoding="UTF-8" ?>
      <result>
      <address>@{triggerBody()['text']}</address>
      <error>error</error>
      </result>

 

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

エラー処理の為、スコープを配置しています。

 

3-1.[HTTP]HTTP

APIを呼び出します。

 

3-2.[コントロール]条件-HTTPstatusCode

HTTPアクションから返される状態コードが正常(200)かどうかで分岐します。

  • 条件
    • @{outputs('HTTP')['statusCode']}
    • 次の状態に等しい
    • 200

3-3.(はいの場合)[変数]変数の設定

状態コードが正常(200)の場合、HTTPアクションの出力をResponse_XML変数に格納します。

  • 名前
    • @{body('HTTP')}

4.[データ操作]作成-xml to json

XML形式のままでは扱いづらいため、json形式に変換します。

実行条件の構成で「失敗しました」にもチェックします。今回のフローはPower Appsから呼び出す想定の為、スコープ内でエラーが発生した場合にも実行されるようにして、PowerApps側でエラーを判断します。

  • 入力
    • json(xml(variables('Response_XML')))

この処理でXMLは次のようなJSONに変換されます。

{
  "?xml": {
    "@version": "1.0",
    "@encoding": "UTF-8"
  },
  "result": {
    "version": "1.2",
    "address": "富士山",
    "coordinate": {
      "lat": "35.360625",
      "lng": "138.727363",
      "lat_dms": "35,21,38.25",
      "lng_dms": "138,43,38.508"
    },
    "open_location_code": "8Q7W9P6G+6W",
    "url": "https://www.geocoding.jp/?q=%E5%AF%8C%E5%A3%AB%E5%B1%B1",
    "needs_to_verify": "yes",
    "google_maps": "静岡県富士宮市北山 富士山"
  }
}

 

5.[PowerApps]PowerApps または Flow に応答する

JSONに変換した結果をPower Appsに返します。

  • 出力1
    • 文字列
    • Response
    • @outputs('作成-xml_to_json')

 

Power Apps アプリ概要

フロー動作確認用の簡易アプリです。

  • 使用するコントロール
    • マップコントロール
      • Map1
    • ボタン
      • ButtonCanvas1
    • テキスト入力
      • TextInput1
    • Power Automateクラウドフロー
      • "Geocoding.jpでジオコーディング"

 

  • Map1の主なプロパティ
    • Items
      • colMapPins
    • ItemAddress
      • ”Address”
    • ItemName
      • "Name"
    • ItemLatitudes
      • ”Latitude”
    • ItemLingitude
      • "Longitude"
  • ButtonCanvasの主なプロパティ
    • onSelect
      • UpdateContext({
            Response:'Geocoding.jpでジオコーディング'.Run(TextInput1.Text)
        });
        IfError(Response,
            Notify("検索中にエラーが発生しました"),""
        );
        
        UpdateContext({
            Response_json:ParseJSON(Response.response)
        });
        
        ClearCollect(
            colMapPins,{
                Name:Text(Response_json.result.google_maps),
                Latitude:Value(Response_json.result.coordinate.lat),
                Longitude:Value(Response_json.result.coordinate.lng),
                Address:Text(Response_json.result.address)
            }
        );
        
        If(IsMatch(Response.response,".*error.*"),Notify("キーワードに該当する地点は見つかりませんでした"))

 

参考にしたページ

いつも参考にさせていただいています。ありがとうございます。

qiita.com

 

learn.microsoft.com

 

さいごに

Geocoding.jp はこれだけのサービスを個人で運用されていることに大感謝です。

検索の頻度を、10秒に1回程度に抑えてください。」と制限を守って利用しましょう。

www.geocoding.jp

 

Outlookメールのメッセージを要約する Power Automate クラウドフロー 💎

 

このフローで実現できること

指定したメールを要約して下書き返信メールの形で返します。

Copilt for Microsoft 365でもっとスマートに実現できるようですが、今自分が使える手段で実現してみました。

 

必要なもの

PowerAutomate Premium ライセンス

AI Builder GPTアクションを使用するためPowerAutomate Premiumライセンスが必要になります。

 

実行イメージ

 

例えば、突然何度も往復した後のメールが転送されてきた時に

 

スレッドに指定のカテゴリをセットします。

 

そしてトリガーメールにフラグをつけてフローを実行します。

※以前考えたフローの仕組みを使っています。

フラグが解除されたらフローの実行完了です。下書きにメール1通追加されています。

 

これまでのメールの経緯を要約した内容を確認することができます。

 

 

フロー全体図

フローの考え方

メール本文を取得して、AI BuilderのGPTアクションで要約しています。

カテゴリが設定されたメールを取得する処理と、要約結果を下書き返信メールとして出力する際にGraph APIを利用しています。

 

フロー解説

赤枠の部分のみ解説します。

その他部分は、メールのフラグを使って任意のフローを起動する処理です。

この部分についてはこちらの記事で解説しています。

sasami-axis.hatenablog.com

 

1.[データ操作] 作成-カテゴリ

このフローの処理対象のカテゴリを定義しています。

後続の処理でこの出力を使用します。

2.[変数]変数を初期化する-ReplyMessage

返信メールの本文を格納する変数です。

  • 名前
    • ReplyMessage
  • 種類
    • 文字列

    • 未設定

 

3.[コントロール]スコープ-削除済みアイテムフォルダのIdを取得

今回のフローは要約カテゴリがついたメールを取得して要約した結果を返信メールの下書きとして出力するフローです。ですが、削除済みアイテムには返信することができません。この為、検索から除外するために削除済みアイテムフォルダのIdを取得しています。

3-1.[Office 365 Outlook]HTTP要求を送信します-メールフォルダー一覧

Graph APIでメールフォルダの一覧を取得します。

learn.microsoft.com

結果はこのようなJSONで返されます。

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('1d8722fd-2174-4c63-be00-46191ddf03e8')/mailFolders",
  "value": [
    {
      "id": "AQMkADc2NWVmOGJlLTZmMDgtNGJlZi05OQA3ZS0wZTM3M2E5MjBjNDgALgAAAyc0t6Vx7LRGlfXIRdnvefoBAEuiNmqCqqdDqbqCmnf9MPwAAAIBCgAAAA==",
      "displayName": "削除済みアイテム",
      "parentFolderId": "AQMkADc2NWVmOGJlLTZmMDgtNGJlZi05OQA3ZS0wZTM3M2E5MjBjNDgALgAAAyc0t6Vx7LRGlfXIRdnvefoBAEuiNmqCqqdDqbqCmnf9MPwAAAIBCAAAAA==",
      "childFolderCount": 24,
      "unreadItemCount": 319,
      "totalItemCount": 1469,
      "sizeInBytes": 66057117,
      "isHidden": false
    }
  ]
}
3-2.[データ操作]アレイのフィルター表示-削除済みアイテム

3-1で取得したメールフォルダー一覧から"削除済みアイテム"フォルダーのみにフィルターします。

  • 差出人
    • @body('HTTP_要求を送信します-メールフォルダ一覧')?['value']
  • 条件
    • @{item()?['displayName']}
    • 次の値に等しい
    • 削除済みアイテム
3-3.[コントロール]条件-削除済みアイテムfolderIdが取得できなければ終了

削除済みアイテムフォルダーのIdが取得できない場合はフローを終了します。

  • 条件
    • @{length(body('アレイのフィルター処理-削除済みアイテム'))}
    • 次の値に等しい
    • 0

 

3-4.[データ操作]作成-削除済みアイテムfolderId

3-2.の出力から1つ目を抽出しておきます

  • 入力
    • @{first(body('アレイのフィルター処理-削除済みアイテム'))?['id']}

 

4.[コントロール]スコープ-対象メールを取得して要約

4-1.[Office 365 Outlook]HTTP 要求を送信します-メッセージを取得-要約カテゴリ付き

Office 365 Outlook コネクタの機能ではカテゴリを条件にメールが取得できない為、Graph APIを利用しています。

learn.microsoft.com

こちらについては別の記事でまとめています。

 

sasami-axis.hatenablog.com

結果はこのようなJSONで返されます。Office 365 Outlookコネクタのアクションよりも多くの情報が取得できます。

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('448060bd-fe71-424f-b70b-3456084c7ffd')/messages",
  "value": [
    {
      "@odata.etag": "W/\"CQAAABYAAACVD3WgHj8MTpdR6wepif3oAAC/FlQr\"",
      "id": "AAMkADcyZTA5MTgt---省略---3oAAAAAAEJAACMTpdR6wepif3oAAC_piGIAAA=",
      "createdDateTime": "2024-01-30T12:42:52Z",
      "lastModifiedDateTime": "2024-01-31T13:46:39Z",
      "changeKey": "CQAAABYAAACVD3WgHj8MTpdR6wepif3oAAC/FlQr",
      "categories": [
        "recap"
      ],
      "receivedDateTime": "2024-01-30T12:42:57Z",
      "sentDateTime": "2024-01-30T12:42:56Z",
      "hasAttachments": false,
      "internetMessageId": "<MN2P---省略--R06MB6606.namprd06.prod.outlook.com>",
      "subject": "Re: オンラインミーティングソフトウェアの障害について",
      "bodyPreview": "--------省略--------",
      "importance": "normal",
      "parentFolderId": "AAMkADcyZTJiYjIzLTA5MTgt---省略---3oAAAAAAEJAAA=",
      "conversationId": "AAQkADcyZTJiYjIzLTA5---省略---G9Ikx8DbACZKSI=",
      "conversationIndex": "AQHaU---省略---veAAAAZxA==",
      "isDeliveryReceiptRequested": false,
      "isReadReceiptRequested": false,
      "isRead": true,
      "isDraft": false,
      "webLink": "https://outlook.office365.com/owa/?ItemID=AAMkADcy---省略---3D&exvsurl=1&viewmodel=ReadMessageItem",
      "inferenceClassification": "focused",
      "body": {
        "contentType": "html",
        "content": "<html><head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">--------省略--------</html>"
      },
      "sender": {
        "emailAddress": {
          "name": "Sasami (ささみ)",
          "address": "sasami_axis@********.**.**"
        }
      },
      "from": {
        "emailAddress": {
          "name": "Sasami (ささみ)",
          "address": "sasami_axis@********.**.**"
        }
      },
      "toRecipients": [
        {
          "emailAddress": {
            "name": "アライ クマ",
            "address": "arai_kuma@********.**.**"
          }
        }
      ],
      "ccRecipients": [],
      "bccRecipients": [],
      "replyTo": [],
      "flag": {
        "flagStatus": "notFlagged"
      }
    }
  ]
}

 

4-2.[データ操作]JSON の解析-対象のメール

Graph APIの出力を解析し後続の処理で扱いやすくします。

  • コンテンツ
    • @{body('HTTP_要求を送信します-メッセージを取得-要約カテゴリ付き')}
  • スキーマ
    • {
          "type": "object",
          "properties": {
              "@@odata.context": {
                  "type": "string"
              },
              "value": {
                  "type": "array",
                  "items": {
                      "type": "object",
                      "properties": {
                          "@@odata.etag": {
                              "type": "string"
                          },
                          "id": {
                              "type": "string"
                          },
                          "createdDateTime": {
                              "type": "string"
                          },
                          "lastModifiedDateTime": {
                              "type": "string"
                          },
                          "changeKey": {
                              "type": "string"
                          },
                          "categories": {
                              "type": "array",
                              "items": {
                                  "type": "string"
                              }
                          },
                          "receivedDateTime": {
                              "type": "string"
                          },
                          "sentDateTime": {
                              "type": "string"
                          },
                          "hasAttachments": {
                              "type": "boolean"
                          },
                          "internetMessageId": {
                              "type": "string"
                          },
                          "subject": {
                              "type": "string"
                          },
                          "bodyPreview": {
                              "type": "string"
                          },
                          "importance": {
                              "type": "string"
                          },
                          "parentFolderId": {
                              "type": "string"
                          },
                          "conversationId": {
                              "type": "string"
                          },
                          "conversationIndex": {
                              "type": "string"
                          },
                          "isDeliveryReceiptRequested": {},
                          "isReadReceiptRequested": {
                              "type": "boolean"
                          },
                          "isRead": {
                              "type": "boolean"
                          },
                          "isDraft": {
                              "type": "boolean"
                          },
                          "webLink": {
                              "type": "string"
                          },
                          "inferenceClassification": {
                              "type": "string"
                          },
                          "body": {
                              "type": "object",
                              "properties": {
                                  "contentType": {
                                      "type": "string"
                                  },
                                  "content": {
                                      "type": "string"
                                  }
                              }
                          },
                          "sender": {
                              "type": "object",
                              "properties": {
                                  "emailAddress": {
                                      "type": "object",
                                      "properties": {
                                          "name": {
                                              "type": "string"
                                          },
                                          "address": {
                                              "type": "string"
                                          }
                                      }
                                  }
                              }
                          },
                          "from": {
                              "type": "object",
                              "properties": {
                                  "emailAddress": {
                                      "type": "object",
                                      "properties": {
                                          "name": {
                                              "type": "string"
                                          },
                                          "address": {
                                              "type": "string"
                                          }
                                      }
                                  }
                              }
                          },
                          "toRecipients": {
                              "type": "array",
                              "items": {
                                  "type": "object",
                                  "properties": {
                                      "emailAddress": {
                                          "type": "object",
                                          "properties": {
                                              "name": {
                                                  "type": "string"
                                              },
                                              "address": {
                                                  "type": "string"
                                              }
                                          }
                                      }
                                  },
                                  "required": [
                                      "emailAddress"
                                  ]
                              }
                          },
                          "ccRecipients": {
                              "type": "array"
                          },
                          "bccRecipients": {
                              "type": "array"
                          },
                          "replyTo": {
                              "type": "array"
                          },
                          "flag": {
                              "type": "object",
                              "properties": {
                                  "flagStatus": {
                                      "type": "string"
                                  }
                              }
                          }
                      },
                      "required": [
                          "@@odata.etag",
                          "id",
                          "createdDateTime",
                          "lastModifiedDateTime",
                          "changeKey",
                          "categories",
                          "receivedDateTime",
                          "sentDateTime",
                          "hasAttachments",
                          "internetMessageId",
                          "subject",
                          "bodyPreview",
                          "importance",
                          "parentFolderId",
                          "conversationId",
                          "conversationIndex",
                          "isDeliveryReceiptRequested",
                          "isReadReceiptRequested",
                          "isRead",
                          "isDraft",
                          "webLink",
                          "inferenceClassification",
                          "body",
                          "sender",
                          "from",
                          "toRecipients",
                          "ccRecipients",
                          "bccRecipients",
                          "replyTo",
                          "flag"
                      ]
                  }
              }
          }
      }

 

4-3.[コントロール]条件-エクスポートするメールがなければ終了

  • "@length(body('JSON_の解析-対象のメール')?['value'])"
  • 次の値に等しい
  • 0

Graph APIでメールが1件も取得できなかった場合にはフローを終了しています。

 

4-4.[Content Conversion]HTMLからテキスト

1つめのカテゴリ付きメールのメッセージ本文をテキストに変換します。

自分の用途では複数メールを一度に要約するシーンは想定していないのでこのようにしています。

Outlook on the Webでメールにカテゴリを設定する動作はメッセージの表示方法で動作が異なるようです。メールにカテゴリを追加する操作を行ったとき、スレッド表示の場合はスレッド全体に、個別のメッセージ表示の場合は表示中のメールにのみカテゴリが追加されます。この動作がありますので、複数のメールを一括で要約することは難しいと判断し、1つ目のメールのみを対象としています。

  • コンテンツ
    • @{body('JSON_の解析-1つ目のメール')?['body']?['content']}

 

4-5.[AI Builder]GPT でプロンプトを使用してテキストを作成する

  • プロンプト
    • AIプロンプトのプロンプトビルダーであらかじめカスタムプロンプトを作成しておきます
    • Follow the steps below to process the text. " input text " 
      
      #Step1 
      - Split emails by delimiter 
      -Delimiter:---------------------------------------------- ---------------------------------- 
      
      #Step2 
      Sort emails by oldest sent date and time 
      
      #Step3 
      Summarize the text below in less than two paragraphs without adding new information. When the text below seems too short to make at least one paragraph of summary, answer that you can't summarize a text that is too short. Output must be in Japanese
    • プロンプト解説
      • Outlookの送受信履歴を引用したメールをテキストに変換すると、"------略-----"で区切られた状態で、送信日時の降順に並んでいます。このまま処理すると、時系列を無視して上から文章が要約されてしまうため、要約結果もおかしな状態になりがちでした。そこで3ステップで処理を行うように指示しています。
        • まず区切り文字ごとに分割します
        • 次にメールの送信日時を基準に昇順に並び替えます
        • そして要約を行います。Step3のプロンプトはデフォルトの「テキストを要約する」に日本語で出力する指示を付け加えたものです。
  • Input SourceText
    • @{body('Html_からテキスト')}

 

4-6[コントロール]条件-GPTアクションの結果で分岐

GPTアクション出力の状態コードが200の場合正常に結果が返ってきています。
200以外の場合は何らかの異常(よくあるのはGPTのフィルターに抵触してエラー)が起きた状態です。

  • 条件
    • @{outputs('GPT_でプロンプトを使用してテキストを作成する')['statusCode']}
    • 次に等しい
    • 200
  • ※実行条件の構成で失敗した場合にもこのアクションが実行されるようにしておきます。GPTアクションが失敗した場合エラーとなってしまうためこのようにしています。
4-6-1[変数]変数の設定-ReplyMessage@正常

GPTアクションの出力が200だった場合は、出力からテキストを抽出します。

今回はメールに出力するため、開業が含まれる場合は<p>に変換しています。

  • 名前
    • ReplayMessage
    • <p><span>🤖<元メールを要約するとこんな感じです。<br>
      </span></p>
      <p>@{replace(
          replace(
          outputs('GPT_でプロンプトを使用してテキストを作成する')?['body/responsev2/predictionOutput/text'],
          '\n',
          '</p>\n<p>'
          ),
          '- ','<li>')
      }</p>
4-6-2[変数]変数の設定-ReplyMessage@異常

GPTアクションの結果が200以外の場合は、出力されるJSONのbodyをそのまま出力しています。

 

  • 名前
    • ReplayMessage
    • <p><span>🤖<メール処理中に問題が発生しました。</span></p><p>@{body('GPT_でプロンプトを使用してテキストを作成する')}</p>

 

5.[コントロール]スコープ-メールから要約カテゴリを除去

メールから要約カテゴリを除去します。

カテゴリはこのように複数設定が可能な値ですので、各メールが保持していたカテゴリからフィルター処理で要約カテゴリを除いた分類のセットを作って更新しています。

5-1.[コントロール]Apply to each

  • 以前の手順から出力を選択
    • @{body('JSON_の解析-対象のメール')?['value']}

 

5-2.[データ操作]アレイのフィルター処理-要約カテゴリを除去

  • 差出人
    • @items('Apply_to_each')?['categories']
  • フィルター条件
    • @{item()}
    • 次の値に等しくない
    • @{outputs('作成-カテゴリ')}

 

5-3.[Office 365 Outlook]HTTP要求を送信します-カテゴリを更新

 

6.[コントロール]スコープ-元メールに返信する下書きを作成

GPTアクションの出力を下書き返信メールとして出力します。

あえて返信メールの下書きで保存しているのには理由があります。要約した結果は一度見たら削除する運用を想定しています。普通に元メールにto自分で返信すればOffice 365 Outlookコネクタのアクションで実現はできます。

しかし、その場合は要約メールを削除する際に受信したメールと送信済みメールの2通を削除する手間が発生します。より容易に削除しできる下書きメールでの保存としました。

下書きメールの作成はGraph APIを使っています。この部分についてはこちらの記事で解説しています。

sasami-axis.hatenablog.com

 

 

6-1.[Office 365 Outlook]HTTP要求を送信します-返信の下書きを作成

 

6-2.[Office 365 Users]マイ プロフィールの取得 (V2)

メールの返信先アドレスに使用します。うっかり要約メールをご送信してしまわないように、メールの宛先を自分のメールアドレスで上書きしておくためです。

6-3.[Office 365 Outlook]メールに返信する (V3)

  • メッセージID
    • @{body('JSON_の解析-1つ目のメール')?['id']}
  • 本文
    • @{variables('ReplyMessage')}
    • ※コードビューにしておく

 

 

このフローの制限事項・改善したい点

  • 自分のメールボックスのメールのみ対応
    • 今回のGraph APIの使い方では自分のメールボックスのみ取得できます。
  • 要約の精度
    • プロンプトを見直すことでもう少し改善できそうです
  • GPTアクションのエラー処理
    • エラーの場合も続行させているところが要改善

 

さいごに

Copilot for Outlook (でいいのか?)で実現できるメールの要約を行ってみました。フラグやカテゴリを使ってUIを工夫してはみましたが、機会があればCopilot for M365の要約と比較してみたいです。

このフローは元々はAzure OpenAI Service を使ったフローでしたが、プロンプトビルダーがGAされたとのことで切り替えてみました。シンプルに使えてよい機能ですね。

 

 

 

 

新しいTeamsは配信の最適化(DO)のダウンロードモード100をサポートしておらず更新できなくなる - 「更新プログラムの確認中に問題が発生しました」

 

はじめに

WindowsPCの新しいTeams デスクトップクライアントでプログラムの更新に失敗する要因に関するシステム管理者向けの覚書です。

※2024年2月現在の確認結果です。

新しいTeamsが更新できない?

新しいTeamsをインストールした後、プログラムの更新に失敗してしまうケースがあります。

「…」→「⚙️設定」→「Teamsについて」を表示すると下記のように赤字で「更新プログラムの確認中に問題が発生しました」表示されでプログラムの更新ができていないことが確認できます。

 

更新できない理由-配信の最適化のダウンロードモード(DODownloadMode)が100となっている

新しいTeams は従来のTeamsとは前提条件に変わっている部分があり、その一つに「配信の最適化」があります。配信の最適化のダウンロードモード100(バイパス)がサポートされていないと前提条件にも明記されています。

このサポートされていない状態であるダウンロードモードが100の場合には、新しいTeamsの更新に失敗することを確認しました。

ダウンロードモード100の状態でも、クラシックTeamsから新しいTeamsへのアップグレードは可能です。新しいTeamsになったあと更新が一切できない状況に陥ります。

 

Teams - 新しい Teams の前提条件について | Japan Unified Communications Support Blog (jpucsupport.github.io)

jpucsupport.github.io

 

配信の最適化とは

Windows Update などMicrosoft 製品の更新で共通に使われる仕組みです。

従来のクラシックTeamsの更新は他のMicrosoft製品とは独立していましたが、新しいTeamsでは更新プロセスが他の製品と統合されたようです。

ダウンロードモード100という値は新しいTeamsだけでなく、Windows11でも非推奨となっているようです。従来100を選択していた場合は、自社の方針に従い適切な値を検討いただく必要があるかと思われます。

 

配信の最適化リファレンス - Windows Deployment | Microsoft Learn 右ダウンロードモード

更新できなくてもつかえればいいじゃない?…そうはいきません

Teamsアプリは最新バージョンの利用が前提であり更新せずに使い続けることはできないそのことがサービス契約にも明記されています。Teamsアプリはモダンライフサイクルポリシーが適用され、自動的に更新されることが前提となっています。現在のバージョンが1ヶ月から3ヶ月前のものである場合アプリ内にアラートが表示されます。アラートを無視しさらに経過するとブロックページが表示されプログラムを更新するまで使用することはできなくなるようです。

 

クラシック Teams ユーザーを新しい Teams に自動的に更新する - Microsoft Teams | Microsoft Learn → サービス契約

 

クラシック Teams クライアントの更新プログラム - Microsoft Teams | Microsoft Learn → サービス契約

 

 

配信の最適化(Do)-ダウンロードモードの値を確認する

ダウンロードモードを確認できる場所

レジストリ

コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization

グループポリシー

 

 

Windows 更新プログラム、アプリ、アプリ更新プログラムのダウンロード時に配信の最適化に使用できるダウンロード方法を指定します。

サポートされている値は次のとおりです。

0 = HTTP のみ、ピアリングなし。

1 = HTTP と同じ NAT でのピアリングの組み合わせ。

2 = HTTP とプライベート グループでのピアリングの組み合わせ。既定では、ピアリングは同じ Active Directory サイト (存在する場合) または同じドメインにあるデバイスで発生します。このオプションを選択した場合、ピアリングは複数の NAT にまたがって実行されます。カスタム グループを作成するには、グループ ID とモード 2 を組み合わせて使用します。

3 = HTTP とインターネット ピアリングの組み合わせ。

99 = ピアリングなしの簡易ダウンロード モード。配信の最適化によるダウンロードは HTTP のみを使用して行われ、配信の最適化クラウド サービスへのアクセスは行われません。

100 = バイパス モード。配信の最適化は使用されず、代わりに BITS が使用されます。

 

参考にしたページ

Teams - 新しい Teams の前提条件について | Japan Unified Communications Support Blog (jpucsupport.github.io)

jpucsupport.github.io

 

learn.microsoft.com

 

配信の最適化(DoDownloadMode)

learn.microsoft.com

 

さいごに

Xで「DODownloadMode 100」で検索してもさっぱりヒットしませんので、この問題にぶち当たる人は少ないかもしれませんが、ハマった場合影響範囲が広く解消が限りなく難しい問題になりがちかもしれません。

 

Power Automate クラウドフローでOutlook メールをカテゴリ(分類)を条件に検索する

 

はじめに

Power Automate クラウドフローでOutlook メールをカテゴリ(分類)で検索する方法です。



考え方

Power Automate のOffice 365 Outlook コネクタには「メールを取得する(V3)」アクションでメールを取得することができますが、このアクションはカテゴリ(分類)を条件にメールを取得する機能がありません。また、取得結果の情報にもカテゴリは含まれていない為、後処理で何とかすることもできません。

ですが「HTTP 要求を送信します」アクションを使ってGraph APIを呼び出すことで検索することが可能です。

 

フロー全体図

「オレンジの分類」が付加されたメールを検索するフロー


フロー解説

[Office 365 Outlook]HTTP要求を送信します

 

 

このようなJSONで返されます。「メールを取得する(V3)」アクションでは取得できない多くの情報も得られるのもうれしい点です。

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('448060bd-fe71-424f-b70b-3456084c7ffd')/messages",
  "value": [
    {
      "@odata.etag": "W/\"CQAAABYAAACVD3WgHj8MTpdR6wepif3oAAC/FlQr\"",
      "id": "AAMkADcyZTA5MTgt---省略---3oAAAAAAEJAACMTpdR6wepif3oAAC_piGIAAA=",
      "createdDateTime": "2024-01-30T12:42:52Z",
      "lastModifiedDateTime": "2024-01-31T13:46:39Z",
      "changeKey": "CQAAABYAAACVD3WgHj8MTpdR6wepif3oAAC/FlQr",
      "categories": [
        "オレンジの分類"
      ],
      "receivedDateTime": "2024-01-30T12:42:57Z",
      "sentDateTime": "2024-01-30T12:42:56Z",
      "hasAttachments": false,
      "internetMessageId": "<MN2P---省略--R06MB6606.namprd06.prod.outlook.com>",
      "subject": "Re: オンラインミーティングソフトウェアの障害について",
      "bodyPreview": "--------省略--------",
      "importance": "normal",
      "parentFolderId": "AAMkADcyZTJiYjIzLTA5MTgt---省略---3oAAAAAAEJAAA=",
      "conversationId": "AAQkADcyZTJiYjIzLTA5---省略---G9Ikx8DbACZKSI=",
      "conversationIndex": "AQHaU---省略---veAAAAZxA==",
      "isDeliveryReceiptRequested": false,
      "isReadReceiptRequested": false,
      "isRead": true,
      "isDraft": false,
      "webLink": "https://outlook.office365.com/owa/?ItemID=AAMkADcy---省略---3D&exvsurl=1&viewmodel=ReadMessageItem",
      "inferenceClassification": "focused",
      "body": {
        "contentType": "html",
        "content": "<html><head>\r\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">--------省略--------</html>"
      },
      "sender": {
        "emailAddress": {
          "name": "Sasami (ささみ)",
          "address": "sasami_axis@********.**.**"
        }
      },
      "from": {
        "emailAddress": {
          "name": "Sasami (ささみ)",
          "address": "sasami_axis@********.**.**"
        }
      },
      "toRecipients": [
        {
          "emailAddress": {
            "name": "アライ クマ",
            "address": "arai_kuma@********.**.**"
          }
        }
      ],
      "ccRecipients": [],
      "bccRecipients": [],
      "replyTo": [],
      "flag": {
        "flagStatus": "notFlagged"
      }
    }
  ]
}

 

[データ操作]JSONの解析

JSONの解析を通して動的な値として使えるようにします。

  • コンテンツ
    • @{body('HTTP_要求を送信します-メールをカテゴリで検索')?['value']}
  • スキーマ
    • ※少量データで確認したのみです。null値への対応が必要となるかもしれません
    • {
          "type": "array",
          "items": {
              "type": "object",
              "properties": {
                  "@@odata.etag": {
                      "type": "string"
                  },
                  "id": {
                      "type": "string"
                  },
                  "createdDateTime": {
                      "type": "string"
                  },
                  "lastModifiedDateTime": {
                      "type": "string"
                  },
                  "changeKey": {
                      "type": "string"
                  },
                  "categories": {
                      "type": "array",
                      "items": {
                          "type": "string"
                      }
                  },
                  "receivedDateTime": {
                      "type": "string"
                  },
                  "sentDateTime": {
                      "type": "string"
                  },
                  "hasAttachments": {
                      "type": "boolean"
                  },
                  "internetMessageId": {
                      "type": "string"
                  },
                  "subject": {
                      "type": "string"
                  },
                  "bodyPreview": {
                      "type": "string"
                  },
                  "importance": {
                      "type": "string"
                  },
                  "parentFolderId": {
                      "type": "string"
                  },
                  "conversationId": {
                      "type": "string"
                  },
                  "conversationIndex": {
                      "type": "string"
                  },
                  "isDeliveryReceiptRequested": {
                      "type": [
                          "null",
                          "boolean"
                      ]
                  },
                  "isReadReceiptRequested": {
                      "type": "boolean"
                  },
                  "isRead": {
                      "type": "boolean"
                  },
                  "isDraft": {
                      "type": "boolean"
                  },
                  "webLink": {
                      "type": "string"
                  },
                  "inferenceClassification": {
                      "type": "string"
                  },
                  "body": {
                      "type": "object",
                      "properties": {
                          "contentType": {
                              "type": "string"
                          },
                          "content": {
                              "type": "string"
                          }
                      }
                  },
                  "sender": {
                      "type": "object",
                      "properties": {
                          "emailAddress": {
                              "type": "object",
                              "properties": {
                                  "name": {
                                      "type": "string"
                                  },
                                  "address": {
                                      "type": "string"
                                  }
                              }
                          }
                      }
                  },
                  "from": {
                      "type": "object",
                      "properties": {
                          "emailAddress": {
                              "type": "object",
                              "properties": {
                                  "name": {
                                      "type": "string"
                                  },
                                  "address": {
                                      "type": "string"
                                  }
                              }
                          }
                      }
                  },
                  "toRecipients": {
                      "type": "array",
                      "items": {
                          "type": "object",
                          "properties": {
                              "emailAddress": {
                                  "type": "object",
                                  "properties": {
                                      "name": {
                                          "type": "string"
                                      },
                                      "address": {
                                          "type": "string"
                                      }
                                  }
                              }
                          },
                          "required": [
                              "emailAddress"
                          ]
                      }
                  },
                  "ccRecipients": {
                      "type": "array"
                  },
                  "bccRecipients": {
                      "type": "array"
                  },
                  "replyTo": {
                      "type": "array"
                  },
                  "flag": {
                      "type": "object",
                      "properties": {
                          "flagStatus": {
                              "type": "string"
                          }
                      }
                  }
              },
              "required": [
                  "@@odata.etag",
                  "id",
                  "createdDateTime",
                  "lastModifiedDateTime",
                  "changeKey",
                  "categories",
                  "receivedDateTime",
                  "sentDateTime",
                  "hasAttachments",
                  "internetMessageId",
                  "subject",
                  "bodyPreview",
                  "importance",
                  "parentFolderId",
                  "conversationId",
                  "conversationIndex",
                  "isDeliveryReceiptRequested",
                  "isReadReceiptRequested",
                  "isRead",
                  "isDraft",
                  "webLink",
                  "inferenceClassification",
                  "body",
                  "sender",
                  "from",
                  "toRecipients",
                  "ccRecipients",
                  "bccRecipients",
                  "replyTo",
                  "flag"
              ]
          }
      }

 

特定のフォルダだけ検索・特定のフォルダを除外したい

ここまでのフローではすべてのメールフォルダが検索対象になります。「削除済みアイテム」フォルダも例外ではありません。この為削除したはずのメールも検索結果に表示される可能性がある点は注意が必要です。

※In-Place Archive は対象外でした

 

検索条件にparentFolderId を指定することで対象フォルダまたは対象外フォルダを指定することも可能でした。その為にはメールフォルダのIdが必要になり別のGraph APIを利用する必要があります。

learn.microsoft.com

 

たとえばこのようなフローで「削除済みアイテム」フォルダを除外した検索が可能でした。

参考にしたページ

Graph API公式リファレンスのチェックは必須です。

learn.microsoft.com

 

learn.microsoft.com

 

learn.microsoft.com

 

さいごに

今回もGraph APIが[Office 365 Outlook] コネクタの「HTTP要求を送信します」アクションで使えることを確認しただけですが、備忘録的にまとめてみました。

 

Outlook メールの下書きをPower Automate クラウドフローで作成する

  • はじめに
  • 考え方
  • 1.新規メールの下書きを作成する
    • フロー全体
    • フロー解説
      • [Office 365 Outlook]HTTP 要求を送信します-下書きメールを作成
    • 実行結果
  • 2.返信の下書きを作成する
    • フロー全体図
    • フロー解説
      • [Data Operation]作成-元メッセージのId
      • [Office 365 Outlook]HTTP 要求を送信します-返信の下書きを作成
      • [Office 365 Outlook]HTTP 要求を送信します-返信の下書きメールを更新
    • 実行結果
  • 3.全員に返信する下書きを作成する
    • フロー解説
      • [Office 365 Outlook]HTTP 要求を送信します-全員返信の下書きを作成
    • 実行結果
  • 4.転送の下書きを作成する
    • フロー解説
      • [Office 365 Outlook]HTTP 要求を送信します-転送の下書きを作成
    • 実行結果
  • 5.下書きメールにファイルを添付する
    • フロー全体図
    • フロー解説
      • [Office 365 Outlook]HTTP 要求を送信します-下書きメールを作成
      • [OneDrive for Business]パスによるファイル コンテンツの取得-添付ファイル
      • [Variable]変数を初期化する-FileName
      • [Office 365 Outlook]HTTP 要求を送信します-ファイルを添付する
    • 実行結果
  • 参考にしたページ
  • さいごに

 

はじめに

Power Automate クラウドフローでOutlook メールの下書きを作成する方法です。

 

考え方

Power Automate のOffice 365 Outlook コネクタには下書きメールを作成するアクションは用意されていませんが「HTTP 要求を送信します」アクションを使ってGraph APIを呼び出すことで作成することが可能です。

  • 新規メールの下書き
  • 返信するメールの下書き
  • 全員に返信するメールの下書き
  • 転送するメールの下書き
  • 下書きメールにファイルを添付する

のパターンを確認してみました。

 

1.新規メールの下書きを作成する

Graph API の「メッセージを作成する」を利用します。

learn.microsoft.com

 

フロー全体

1アクションだけです。

 

フロー解説

[Office 365 Outlook]HTTP 要求を送信します-下書きメールを作成

  • URI
  • メソッド
    • POST
  • 本文
    • 下書き保存するメッセージをJSONで指定します。
    • 一例は件名,本文,to,cc,bccを指定するとこんな感じです。
    • {
          "subject": "メールの下書きを作成するよ",
          "body": {
              "contentType": "HTML",
              "content": "<p>メッセージの本文です</p><p>テスト</p><p>なのです。</p>"
          },
          "toRecipients": [
              {
                  "emailAddress": {
                      "name": "strike",
                      "address": "GAT-X105@seed-dummy.onmicrosoft.com"
                  }
              },
              {
                  "emailAddress": {
                      "name": "rouge",
                      "address": "MBF-02@seed-dummy.onmicrosoft.com"
                  }
              }
          ],
          "ccRecipients": [
              {
                  "emailAddress": {
                      "name": "freedom",
                      "address": "ZGMF-X10A@seed-dummy.onmicrosoft.com"
                  }
              },
              {
                  "emailAddress": {
                      "name" : "strike freedom",
                      "address": "ZGMF-X20A@seed-dummy.onmicrosoft.com"
                  }
              }
          ],
          "bccRecipients": [
              {
                  "emailAddress": {
                      "name": "rising freedom",
                      "address": "STTS-909@seed-dummy.onmicrosoft.com"
                  }
              }
          ]
      }
      • subject
        • 件名
      • body
        • contentType
          • HTML , Text ←本文の形式を指定
        • content
          • 本文を記述。(Textの場合は\nで改行) 
      • toRecipients 
        • emailAddress ※複数指定可 
          • name
            • 宛先(To)の表示名(省略可能)
          • address
            • 宛先(To)のメールアドレス
      • ccRecipients
        • emailAddress ※複数指定可 
          • name
            • ccの表示名(省略可能)
          • address
            • ccのメールアドレス
      • bccRecipients
        • emailAddress ※複数指定可 
          • name
            • bccの表示名(省略可能)
          • address
            • bccのメールアドレス 
    •  その他設定可能な属性はこちらに記載されています。

実行結果

メールの下書きが作成されます。

 

続きを読む