コルネの進捗や備忘録が記されたなにか

進捗や成果物や備忘録てきななにかを雑に更新していきます。

Power Apps の Tab 遷移順について

はじめに

私も仕様変更をすっかり失念していて沼に嵌ってしまったので、自戒も込めて Power Apps の Tab 遷移の挙動についての共有です。

公式ドキュメント

learn.microsoft.com

2026年4月現在の Tab 遷移の挙動

まずクラシックコントロールで利用可能な "TabIndex" ですが、こちらは現在 0 より大きい数を設定することはできません。
0 より大きい値を設定したとしても、それは 0 として扱われます。
* TabIndex = -1 は現時点でも機能し、これは Tab 遷移をしない扱いとなります。

現在の Tab 遷移順は、画面上に設定されたコントロールの座標によって制御され、左から右へ、そして上から下へと "Z" 順のパターンで遷移します。

ただし、コンテナはギャラリー、Form のような親コントロール内にあるコントロールは例外です。
これらは画面上の順序に関わらず、親コントロールの順序が来た場合、その中にあるコントロールが続いて順に遷移していく動作となります。

ここで気を付けなくてはいけないのは、この挙動を確認できるのは公開中のアプリのみであるという点です。

開発中のアプリではコントロールを配置した順にタブ遷移が行われてしまいます。

また、X や Y の値が Power Fx 式によって更新されるなど、コントロールが画面上で動的に移動する場合も、ナビゲーションの順序は更新されないため、このようなアプリを作成する場合は注意が必要です。

この挙動は "TabIndex" プロパティが廃止されたモダンコントロールも同様です。

旧設定を利用したい場合

もし "TabIndex" で設定した数値での Tab 遷移を行いたい場合はこちらの"廃止済み"の設定をオンにする必要があります。

おわりに

すっかり忘れてた...

そして確かにググると私が5年前に書いたこの記事がヒットする...
よくない...

koruneko.hatenablog.com

ただ過去のブログを更新する余力は個人にはないのです...

見つけ次第地道にアップデートしていきます。

Power Apps のエクスポートコントロールを PC でも利用する & 2026/01/06 時点の挙動について

はじめに

Power Apps のコントロールには「インポート」や「エクスポート」コントロールがあることはご存知でしょうか?

こちらのコントロールを利用することで、データ(テーブル)を一度ローカルに保存した後、別の Power Apps アプリでそのデータをインポートして利用する。みたいなことができるようになります。

learn.microsoft.com

似たような機能として SaveData 関数 や LoadData 関数 があります。
こちらはいわゆるキャッシュデータの作成/読み込み機能のようなものですね。

learn.microsoft.com

これらの機能のうち、今回は先に紹介した エクスポート/インポート コントロールについての Tips について紹介します。

エクスポート/インポート コントロール

制限事項

ドキュメントでも記載されている通り、こちらのエクスポートコントロールは Web ブラウザーではサポートされていない(利用できない)です。

では PC では利用できないのか?というと必ずしもそういうわけではありません。

PC での利用方法

Web ブラウザーで利用できないだけであって、他の開き方を利用すればこちらのコントロールを PC でも利用することが可能です。

そのやり方は Power Apps のデスクトップアプリケーションを利用するというやり方です。

Microsoft Store では Power Apps のアプリが配布されています。

apps.microsoft.com

こちらを利用することで PC でもエクスポートコントロールを利用することが可能です。

ユーザーに「デスクトップアプリ or モバイルアプリでこのアプリのエクスポート機能は利用してね」というような案内が必要になってきますが、一応運用回避するやり方もあるということを覚えておくと、いざというとき役に立つかもです。

2026/01/06 時点のこちらのコントロールの挙動について

ここまで回避方法を記載しましたが、 2026/01/06 現在、こちらのインポートコントロールは正常に動作していません。

インポートを行っても処理が正常に完了せず、またモニターで確認をすることでエラー原因を特定することもできません。

正直あまり使わないコントロールなので記憶が定かではないのですが、昔は使えたはず...
最近のアップデートによって使えなくなったか、実は今後非推奨になるコントロールの可能性もありますかね。

ここでこれ以上私の推測を話しても仕方がないので、現在 Microsoft に問い合わせ中ですので、続報がわかり次第追記したいと思います。

このことをメモとして記載するためにこの記事書いたまである。

おわりに

Power Apps には色々なコントロールや関数があります。
普段使わないコントロールや関数も試してみることで新たな気づきを得られることもありますので、是非試してみてくださいね。

新年1発目 vibe.powerapps.com で 2026 年を記念したアプリを作成する

はじめに

Happy New Year!

2026 年、新年を記念したアプリを vibe.powerapps.com で作成してみました!

特に技術的な紹介とかしている記事ではないですw
所謂ネタブログ

vibe.powerapps.com に関する記載はこちらをご覧ください。

koruneko.hatenablog.com

2026 年記念アプリを作成する

プロンプト

以下のようなプロンプトを入力しました。

Please create an app inspired by the 2026 Year of the Horse.

そして修正してもらったプロンプトがこちら。

Design an interactive app themed around the 2026 Year of the Horse, blending cultural education, entertainment, and personalization. Key features should include: a dynamic calendar highlighting important dates and traditions related to the Year of the Horse; daily horoscopes and personality insights for users based on their birth year and Chinese zodiac; a virtual horse companion that users can customize, care for, and interact with; educational modules about the symbolism, history, and global celebrations of the Year of the Horse; gamified challenges and quizzes to unlock themed rewards; social sharing options for users to post achievements and festive greetings; and a vibrant, animated interface inspired by traditional Chinese art and modern design. Ensure accessibility, multi-language support, and mobile responsiveness.

Plan

What are we solving for?
Design an interactive app themed around the 2026 Year of the Horse, blending cultural education, entertainment, and personalization. Key features should include: a dynamic calendar highlighting important dates and traditions related to the Year of the Horse; daily horoscopes and personality insights for users based on their birth year and Chinese zodiac; a virtual horse companion that users can customize, care for, and interact with; educational modules about the symbolism, history, and global celebrations of the Year of the Horse; gamified challenges and quizzes to unlock themed rewards; social sharing options for users to post achievements and festive greetings; and a vibrant, animated interface inspired by traditional Chinese art and modern design. Ensure accessibility, multi-language support, and mobile responsiveness.

User roles
general user
Individuals exploring cultural insights, entertainment, and personalized features around the Year of the Horse.
As a user, I need to:

  • View a dynamic zodiac-themed calendar so that I can learn about key cultural dates and traditions.
  • Access daily horoscope and personality insights based on my birth year so that I can enjoy personalized guidance.
  • Customize and interact with a virtual horse companion so that I feel engaged and entertained.
  • Complete gamified quizzes and cultural challenges so that I can unlock themed rewards and learn interactively.
  • Share festive greetings and achievements on social platforms so that I can celebrate with friends and family.
  • Switch between languages so that I can comfortably use the app in my preferred language.
  • Navigate an accessible and visually appealing interface so that I can enjoy the content regardless of device or abilities.

content curator
User responsible for updating cultural and educational content, horoscopes, and challenges.
As a user, I need to:

  • Update educational modules about traditions and symbolism so that users stay informed with accurate cultural knowledge.
  • Publish daily horoscope information so that users receive fresh and personalized insights.
  • Create gamified quizzes and challenges so that users remain entertained while learning.

community manager
User managing social interactions, sharing features, and engagement campaigns.
As a user, I need to:

  • Monitor and manage social sharing options so that festive messages are safe and appropriate.
  • Analyze user engagement trends so that I can provide targeted cultural campaigns.

Agent や Workflow(Agent Flow) の Create はできないんですよねー...

Data

App

言語に日本語がないので追加してみましょう。

日本語追加できた!
でも元から上にあったやつただの飾りになっちゃったけど...?

Vive Power Apps 難しい...

おわりに

2025年、 Vibe Coding はバズワードの1つとなり、恐らく 2026 年はより技術的にできることが増えていくことが予想されますが、 Power Apps での Vibe Coding はまだまだ課題が多そうな印象ですねー...

改めまして、2025 年私のブログを読んでいただけたり、コミュニティイベントに参加してくれた皆さま、そして SNS やリアルで関わっていただけた皆さまありがとうございました。
2026 年も技術情報の発信やコミュニティイベントを開催していきますので、よろしくお願いいたします。
情報発信については、新しい形での発信も予定しており、現在頑張っているところですので、発表を楽しみにしてもらえると嬉しいです。

直近で予定しているイベントとしては Japan Power Platform Community Caravan があります。

jppcc.connpass.com

2026年に開催が予定されているイベント会場は以下です。

日本各地で開催し、オンサイト限定のネットワーキングも目的としたイベントです!
ご都合があえば皆さま是非ご参加ください。

それでは改めまして、皆さま 2026 年度もよろしくお願いいたします。

Power Automate の「イベントの更新」で既存出席者には通知を飛ばさないようにする

はじめに

Outlook や Teams では作成したイベントで参加者の追加 or 削除をした場合、更新内容を「追加/削除された出席者のみ」に送付することが可能です。

社内イベントなどで、参加希望者にインビテーションを送付したりするような際、参加者の追加とかはできれば自動化したいーと思うかもしれません。

これを自動化するには、 Power Automate のクラウドフローを利用することになりますが、クラウドフローで実現しようとした際少し工夫が必要になってきます。

今回は、上記のようなフローの作成方法を簡単に紹介します。

フローの作成

利用するアクション

アクションは「イベントの更新 (V4)」を利用して、参加者の追加・削除を行います。

アクションを利用する際に気を付けること

こちらのアクションの必須項目は

  • 予定表 ID
  • ID
  • 件名
  • 開始時刻
  • 終了時刻
  • タイム ゾーン

の6項目です。

learn.microsoft.com

先で紹介したような「参加者の更新を自動化する」というようなシナリオだと、上記必須項目に加えて「必須出席者」の7項目だけ入力を行おうとするパターンが多いと思います。

ただこの7項目だけですと、多くの場合既存参加者にもまとめてイベントの更新通知が送られてしまいます。

原因はよくよく確認すると他の項目が、元の値から変更されてしまっているからです。

「イベントの取得」などで取得した結果と、実行履歴を比較してみてください。

多くの場合参加者情報を表す "requiredAttendees" だけでなく、

  • body
  • location
  • importance
  • showAs

の4項目が変化してしまっているかと思います。

まず "body" は「本文」にあたり、なにも設定されていない場合はそのまま本文が空白で更新されてしまいます。

次に "location" は「場所」にあたり、オンサイトイベントで場所を指定している場合はもちろん、 Teams 会議の場合でも「Microsoft Teams 会議」が設定されることになります。
ここも何も指定しないと空白で更新されてしまいます。

"importance" は「重要度」にあたり、なにも設定しない場合はデフォルト値である "low" に設定されてしまいます。
Outlook や Teams でイベントを作成した場合、デフォルトでは "normal" が設定されます。
ここに差異が発生しているのですね。

最後に "showAs" は「表示方法」にあたり、なにも設定しない場合はデフォルト値である "free" = 「空き時間」に設定されてしまいます。
Outlook や Teams でイベントを作成した場合、デフォルトでは "budy" = 「取り込み中」 が設定されます。
ここも同様に差異が発生しているのですね。

上記のように、参加者にも関係ある情報が変化してしまっているので、全員に通知が飛んでしまっているのですね。

従って、これらの値についても元の値と同じ値となるようにアクションを設定してあげる必要があるというわけです。

もちろん、他にもイベントの設定を変更していた場合は同様に同じ値となるようにアクションを設定しなければならないということですね。

まとめ

まとめると、「イベントの更新 (V4)」は

🆖:設定した項目の値のみ更新
🆗:すべてのパラメーターについて値の更新

ということです。

おわりに

このフローは色々なところで利用される割に、自分のブログはもちろん、他のブログなどでもあまり触れらていなかったのでまとめてみました。

【イベント宣伝】今年も Japan Power Platform Community Caravan を開催します!

コミュニティ紹介

Power Platform で【つながる】 【ひろげる】 をテーマにしたコミュニティイベント「Japan Power Platform Community Caravan」を日本各地開催!

コミュニティに参加して日本各地のユーザーとつながり、最新情報や事例を共有し、ネットワークを広げる絶好の機会です。

👉 コミュニティ詳細はこちら:

jppcc.connpass.com

開催場所一覧

名古屋会場

~Power Platformに関わる全ての人のための集い~
Japan Power Platform Community Caravan in 名古屋では、実装の疑問・お悩みをみんなで解決するセッションを開催!
初心者もベテランも歓迎、ネットワーキングで仲間と学ぼう!

📅 1/17(土) 13:30 〜 18:30
📍 日本マイクロソフト 中部支店
👉 詳細はこちら: Japan Power Platform Community Caravan in 名古屋 - connpass

東京会場

~立ち上げ、推進、そして経験者まで。社内コミュニティに関わる全ての人のための集い~
Japan Power Platform Community Caravan in 東京では、社内コミュニティの課題を語り合うラウンドテーブル形式イベント!

📅 1/31(土) 13:00 〜 18:30
📍 品川グランドセントラルタワー
👉 詳細・申込: Japan Power Platform Community Caravan in 東京 - connpass

広島会場

~仲間と学びを分かち合うことを楽しむ時間を一緒に創りましょう!~
Japan Power Platform Community Caravan in 広島では、「Microsoft Power Platform なんでも意見交換会」を開催!

📅 2/7(土) 13:00~17:00
📍 株式会社ビットゼミ
👉 詳細・申込: Japan Power Platform Community Caravan in 広島 - connpass

佐賀会場

~Power Platform お悩み・困りごと相談~
Japan Power Platform Community Caravan in 佐賀では、Power BIのハンズオンを中心に、Power Platformのお悩み相談を行うイベントを開催!

📅 2/14(土) 13:00 〜 17:00
📍 MAIC佐賀(Microsoft Base 佐賀) ](https://maic-saga.com/)
👉 詳細・申込: Japan Power Platform Community Caravan in 佐賀 - connpass

沖縄会場

~Power Platform お悩み・困りごと相談~
Japan Power Platform Community Caravan in 沖縄では、Power BIを中心にPower Platformのお悩み相談が行えるイベントを開催!

📅 2/21(土) 13:00 〜 18:00
📍 沖縄県立図書館 4階ビジネスルーム
👉 詳細・申込: Japan Power Platform Community Caravan in 沖縄 - connpass

石川会場

~Power Platform 活用の一歩を踏み出そう!~
Japan Power Platform Community Caravan in 石川では、Power Platoformの基礎や活用事例を学び、お悩みを相談できるイベントを開催!

📅 2/28(土) 13:00 〜 18:30
📍 マイクロソフトベース金沢
👉 詳細・申込: Japan Power Platform Community Caravan in 石川 - connpass

サポーターの募集について

本イベントではサポーターとなっていただける企業・個人事業主様を募集しております。
サポーターとなっていいただける企業様には以下いずれかのサポートをお願いしたいです。

  • イベント会場へのドリンクや軽食類の差し入れのご提供
  • 多くの属性の方にイベントを周知していただくためのイベントの告知支援

サポーターとなっていただける企業・個人事業主様は各イベント会場で紹介を行わせていただきます。

※1 差し入れのご提供にて、サポーターとして紹介させていただくのは事前に申し出ていただいた場合に限定させていただきます。
※2 本イベントでは直接的な金銭のやり取りはご遠慮させていただいております。

サポーターとなっていただける場合はこちらのフォームからご応募お願いいたします。
https://aka.ms/JPPCC-Supporter

記念グッズについて

JPPCC 2026の開催を記念して、イベント記念Tシャツを作成しました!
興味のある方は各自イベント記念Tシャツよりお好きな商品をご自身でご購入ください。

clubt.jp

※Tシャツの代金はご自身での負担となります。ご了承の程よろしくお願いいたします。

突如現れた vibe.powerapps.com を試してみる

はじめに

日本時間、11/18(火)の午後あたりから、Power Apps のホーム画面を開くと見慣れないポップアップが表示されていました。

learn.microsoft.com

新しい Plan Designer だ!

これは Power Platform Community Conference 2025 で発表された、Plan Designer を利用することで、TypeScript ベースのコードにてアプリが作成される機能のようですね。

早速試してみたので、利用方法やざっくり機能を紹介してみようと思います。

前提条件

現状こちらの機能を利用するためには以下要件を満たしている必要があります。

  • 対象の Power Platform 環境が「米国」であること  
  • ブラウザの言語が「英語」であること

Please pick a United States-based environment
The environment "[Your Env Name]" is not set in the United States region or English locale. Please select another environment. Learn more

上記制限があるため、今回の記事のスクショやプロンプトはすべて英語で行っています。

Vibe Power Apps(仮)を試してみる

アクセス

私の紹介したポップアップが表示されていない場合は https://vibe.powerapps.com/ からアクセスすることが可能です。

環境を指定して開きたい場合は https://vibe.powerapps.com/environments/[Your Env ID]/home で直接開くことができます。

画面の紹介

まず最初にアクセスし、またアプリを作成し始める画面がこちらになります。

サンプルを選択すると、サンプルプロンプトが挿入されます。

そして、鉛筆アイコン(Enhance prompt)を選択すると Copilot によって、プロンプトが強化されます。

Before

I want to create an app for employees that helps with hardware requests.

After

Design an employee-facing app for managing hardware requests within an organization. The app should allow employees to browse available hardware inventory, submit detailed requests specifying device type, preferred specifications, quantity, intended use, and urgency. Include options to upload supporting documents or images. Provide real-time notifications for request status updates (submitted, under review, approved, rejected, fulfilled). Enable employees to view their request history and current status. For administrators, implement dashboards to review, approve/reject, assign hardware, track inventory levels, and generate usage reports. Integrate role-based access control, audit logs, and ensure the interface is responsive and user-friendly for both desktop and mobile devices.

結構便利な機能ですね。

"Plans" タブを選択すると、作成したプランを確認することが可能です。

"Apps" タブを選択すると、作成されたアプリを確認することが可能です。

Plan を作成してみる

以下のようなプロンプトを試してみます。

I want to create a task management application. Make it possible to track progress in WBS format, Gantt charts, and Kanban boards.

日本語訳すると、以下のような指示を出しています。

タスク管理アプリを作成したい。WBS形式やガントチャート、カンバン形式で進捗が確認できるようにして。

では、こちらのプロンプトを強化してもらいましょう。

Design a comprehensive task management application that enables users to organize and track project progress using multiple visualization formats: Work Breakdown Structure (WBS), Gantt charts, and Kanban boards. The app should allow users to create projects, define hierarchical tasks/subtasks (WBS), assign resources, set dependencies, and deadlines. Integrate interactive Gantt charts for timeline planning, drag-and-drop scheduling, and progress tracking. Include Kanban boards for workflow management, enabling users to move tasks across customizable columns (e.g., To Do, In Progress, Done). Provide real-time updates, notifications, and collaboration features such as comments, file attachments, and user mentions. Support filtering, search, and reporting on task status, resource allocation, and project milestones. Ensure role-based access control, responsive design for desktop and mobile, and export options for WBS, Gantt, and Kanban views.

日本語訳

包括的なタスク管理アプリを設計してください。このアプリは、ユーザーがプロジェクトの進捗を複数の可視化形式で整理・追跡できるようにします:作業分解構成図(WBS)、ガントチャート、カンバンボード。 アプリでは、プロジェクトの作成、階層的なタスク/サブタスク(WBS)の定義、リソースの割り当て、依存関係や期限の設定が可能であること。 タイムライン計画のためのインタラクティブガントチャートを統合し、ドラッグ&ドロップによるスケジューリングや進捗管理を実現してください。 ワークフロー管理のためにカンバンボードを含め、ユーザーが「To Do」「In Progress」「Done」などのカスタマイズ可能な列間でタスクを移動できるようにします。 リアルタイム更新、通知、コメント、ファイル添付、ユーザーへのメンションなどのコラボレーション機能を提供してください。 タスクのステータス、リソース配分、プロジェクトのマイルストーンに関するフィルタリング、検索、レポート機能をサポートします。 ロールベースのアクセス制御、デスクトップとモバイルに対応したレスポンシブデザイン、WBS・ガント・カンバンビューのエクスポートオプションを確保してください。

めちゃくちゃ雑な指示から、必要であろう要件が簡潔にまとめられていますね!

要件をまとめるのが苦手な方でもこれならある程度のアプリが作成できるようになりそうですし、他にも更新された要件を確認することで、今後 Copilot や人に要件を伝えるにあたって、どのような点を押さえて伝えればよいのかの勉強にもなりそうです。

では上記プロンプトで早速 Plan を作成してみようと思います。

ちょっと見た目が変わっているかもしれませんが、なんとなく既視感のある Plan Designer の画面かと思います。

作成完了するまで5分程掛かるのでコーヒーでも飲んで待ちましょう。

作成が完了すると、こんな感じでアプリとコードを確認することができます。

なお、こちらのコードは Read Only なのでコードの編集は少なくとも今時点ではできなさそうです。

ダークモードが欲しいので追加でリクエストしてみます。

ちゃんとシステムに合わせてテーマ変更できる機能まで作ってくれました!

また、特定の要素を変更することもできます。
プロンプト入力欄の左下にある"Toggle inline edits"を選択します。

するとこのような形で要素を指定することができます。

例えばタイトルの要素を指定してみると、こんな感じでその要素に対するプロンプトや、色などを変更できるオプションが表示されます。

こんな感じで特定の要素を手軽に変更できます。

「表示されているタイトルの変更」という一見すぐ終わる指示を出してみましたが、実際はドキュメントの修正など色々関連してやらないといけないタスクがあり、Plan くんはそれを理解したうえでちゃんとやってくれていますね。

指示者がこんだけで終わるだろ。と、現場の意見を聞かずに作業を調整するとダメな例をまさかここで見ることになるとはw

最後に作成された Plan を保存して、公開してみたいと思います。

右上にある見慣れた保存アイコンと公開アイコンがそれですね。

公開を行おうとすると、下書き状態の Dataverse を公開するメッセージと、 Dataverse を選択する画面がでてくるため、 Dataverse を選択のうえ、 Publish を選択します。

公開が完了すると、先ほど紹介した Plan 一覧に表示されるようになります。

従来の Power Apps の画面上でも確認できます。

アプリは Code Apps として作成されていますね。

ソリューションは既定のソリューションに含まれています。

どこでソリューション指定できるんだろうか??

おわりに

Microsoft Igniteが始まる前に記事を書きたかったので、だいぶ雑な説明になってしまって申し訳ないです。

PPCC では発表されたこれらがいよいよ現実として迫ってきている感がありますね!

news.microsoft.com

Copilot Studio で Web 検索をオンにすることができません

はじめに

いつからこの事象が発生しているのか、またこれが正式な機能変更なのかは定かではないのですが、Copilot Studio の設定から「Web 検索」をオンにして「すべての公開 Web サイトをエージェントが検索できるようにします。」が利用できなくなっています。

learn.microsoft.com

本来あるはずの設定項目

Web 検索が見当たらない

なお、この問題は多くのエージェントで確認できましたが一部エージェントでは確認できませんでした。

回避方法

新規作成時に行える回避

新規作成時に、"特定の操作"を行うことでこの問題は回避することができます。

現在エージェントの新規作成を行おうとすると、「説明」にてチャットベースでエージェントの構築をすることができ、「構成」で「説明」で生成された設定内容を確認のうえ編集することができます。

ここで、「説明」でチャットを行う前に、「構成」を開きます。
これにより「Web 検索」の設定を行うことができるようになります。

ただ、構成で「オン」にしてても実際に作成されるエージェントの設定では「オフ」になっているので気を付けましょう。

「説明」でチャットを行った後に「構成」を開いてしまうと、「Web 検索」の設定がグレーアウトしてしまいます。

この状態で作成を行うと、「Web 検索」の設定項目自体が消えます。

この回避方法は新規作成するエージェントのみにできる対応ですね。

コードベースで修正を行う

Copilot Studio は VSCode を用いてコードベースで編集することが可能です。

やり方など詳しくは以下をご確認ください。

koruneko.hatenablog.com

さて、「Web 検索」の設定項目が見えなくなったエージェントのコードビューをみてみましょう。

みるべき対象のファイルは"agent.mcs.yml"です。

# Name: ユーザーサポート エージェント
# ユーザーの質問や依頼に対して、迅速かつ丁寧にサポートを提供するエージェントです。分かりやすい説明と親切な対応で、ユーザーの課題解決を支援します。
kind: GptComponentMetadata
instructions: |-
  ・ユーザーからの質問や依頼に対して、迅速かつ丁寧に対応すること。
  ・ユーザーが求める情報やサポートを的確に提供すること。
  ・分かりやすい言葉で説明し、専門用語の使用は必要に応じて補足すること。
  ・ユーザーの立場に立って、親切で思いやりのある対応を心がけること。
  ・不明点や追加の要望があれば、積極的に確認し、最適な解決策を提案すること。
  ・安全性やプライバシーに配慮し、個人情報の取り扱いには十分注意すること。
gptCapabilities: {}
conversationStarters:
  - title: 質問への回答
    text: ユーザーからの質問に分かりやすく答えてください。

  - title: 情報提供
    text: ユーザーが求める情報を調べて提供してください。

  - title: 使い方案内
    text: サービスや機能の使い方を説明してください。

  - title: トラブル対応
    text: ユーザーが困っている場合、解決策を提案してください。

  - title: 追加サポート
    text: ユーザーの要望に応じて、追加のサポートを提案してください。

  - title: 安全性の説明
    text: 個人情報の取り扱いについて説明してください。

上記のようになっているはずです。

このうち、「Web 検索」に当たる設定は gptCapabilities: 内にあるはずです。

現在はこちらが空白になっていますね。

gptCapabilities: {}

一応この設定のままエージェントをテストしてみます。

以下のように LLM の一般ナレッジを使用して生成されたことが確認できるかと思います。

では、「Web 検索」をオンにする設定を記載してみます。

「Web 検索」をオンにするには以下の設定を記載します。

gptCapabilities:
  webBrowsing: true

この設定を追加することで、Web 上の画面でも「Web 検索」の項目が復活していることが確認できます。

この設定にて先ほどと同じプロンプトでテストしてみます。

Web からの結果を参照して回答してくれていますね。

最後にコードベースでこちらの設定を false にして「Web 検索」をオフにしてみます。

gptCapabilities:
  webBrowsing: false

すると、Web 上の画面からなんと「Web 検索」の項目が消えていますw

一応こちらでも同じプロンプトを実行してみると、 LLM の一般ナレッジを使用して生成されたことが確認できるかと思います。

おわりに

挙動からして不具合な気がしますね。。。

ただ現行でエージェントを動かしている方や、これから新規作成しようとしている方に支障があると思ったので、回避方法を簡単にまとめておきました。

もうちょっと動作安定してくれないかなぁ。。。

Copilot Studio で環境/エージェントごとにメッセージ消費の制限を行う方法

はじめに

Copilot Studio は Power Apps や Power Automate とは異なり、ユーザーライセンスではなくテナントライセンスとして購入を行うライセンス形態となっています。

www.microsoft.com

そうなってきた場合、環境/エージェントごとなどで利用できるメッセージ数に上限を設けたい!というような要望が出てくると思います。

この記事では 2025/09/01 時点での環境/エージェントごとにメッセージ消費の制限を行う方法を備忘録も兼てまとめたいと思います。

参考文献

Copilot Studio の利用制限

作成権限

Copilot Studio で作成したエージェントを利用するには、メッセージパックに値するライセンスをテナントで購入する必要があります。

その他にも、エージェントを作成するにはユーザーに対して以下何れかを割り当てる必要があります。

環境ごとに利用可能なメッセージ容量を割り当てる

Power Platform 管理センター にアクセスして、 ライセンス > Copilot Studio を開きます。

こちら

"Manage Copilot Credits" もしくは "セッションの管理" を選択します。

これは現状どちら選択してもそんなに変化はありません。
どちらも環境一覧が表示されますので。

Manage Copilot Credits

セッションの管理

制限を設定したい環境を選択します。

ここでは、テナント内で設定可能なメッセージ数の範囲内で、メッセージの制限や、超過時の挙動、超過の通知を設定することが可能です。

"Copilot Credits" にてその環境で利用可能なメッセージを割り当てることが可能です。
ここで設定したメッセージ数は他の環境による影響を受けることなく利用可能です。 つまり、テナント利用可能なメッセージ容量を超過してもこの環境には影響がなく、また他の環境からここで割り当てられた容量を消費される。という恐れがないということですね。

従って、プロジェクトの予算などでメッセージパックを購入した場合でもこちらの容量を設定するのがよいでしょう。
もちろんテナント全体での容量の割り振りの際でもこちらの設定を利用します。

こちらの設定はデフォルトは0になっています。

"容量の超過" では記載の通りですが、「この環境で容量がゼロに達したときの対処方法を選択します。」

現在設定可能なのは、容量超過時にテナントにプールされている容量を利用するかどうか?となっています。

こちらの設定はデフォルトでオンになっています。

"超過の通知" では、設定したメッセージ容量の X% になったら環境管理者に向けて通知を送信する機能です。

こちらの設定はデフォルトでオフになっています。

その他知っておくとよい制限は以下ですかね。

しきい値の使用量
テナントが前払いキャパシティの 125% に達すると、強制措置がトリガーされます。

超過分への対応 (125%)
カスタム エージェントは無効です。 エージェントを無効にしても、進行中の会話は中断されません。 その後のエージェントの呼び出しの試行はすべて、容量が増加またはリセットされるまで拒否されます。

強制施行された後のエージェントの動作
強制施行がトリガーされ、現在の会話が終了すると、エージェントは無効になります。 エンドユーザーが施行後にエージェントと対話しようとすると、次の応答が表示されます: 「申し訳ありませんが、どのようにサポートしたらいいのかわかりません」

この 125% の制限はあくまでもテナントの容量に対する措置となるようです。

環境ごとに割り当てたメッセージ容量にはこの 125% の措置は適用されないので注意してください。(環境ごとの制限にも適用されちゃうと、容量凄い超過できちゃいますからね。)

エージェントごとに利用可能なメッセージ容量を割り当てる

エージェントごとに利用可能なメッセージ容量を割り当てたい場合は先ほどと同様に、Power Platform 管理センター にアクセスして、 ライセンス > Copilot Studio を開きます。

こちら

"エージェントを管理する" を選択します。

こちらでは公開済みのエージェントが表示される(はず)です。(こちらに関しては公式ドキュメントがどこにあるのかわからなかった)

制限を適用したいエージェントの三点リーダーより "制限を設定する" を選択します。

すると環境ごとの制限時と同じような制限設定が表示されます。

"Copilot Credits" ではそのエージェントで使用可能なメッセージ容量を設定します。

ここで設定可能なメッセージ容量はその環境で利用可能なメッセージ容量の範囲内となっております。

"通知と調整のしきい値" は "Copilot Credits" を設定した場合に設定可能です。

"使用を停止する" にチェックを入れた場合は "Copilot Credits" で設定したメッセージ容量となった場合は対象のエージェントは動作しなくなります。

"超過の通知" は "Copilot Credits" で設定したメッセージ容量の X% となった場合にエージェント所有者に通知を送ります(ここ要検証)。

おわりに

Copilot Studio はエージェントの単純な機能だけでなく、管理回りのアップデートも頻繁に行われています。

まだまだ実運用を行う上では、あの機能もあればいいなーというのが多くあるかもしれませんが(特に管理回り)、日々日々アップデートが行われていますので、ロードマップを確認するなどして、組織やプロジェクトでの Copilot Studio の管理戦略を立てていきましょう。

欲しい機能がこないなーという場合は是非 Issues を挙げてください!

Power Apps Code Apps + GitHub Copilot で Vibe Coding!

はじめに

現在 Early Access Preview の機能として Power Apps を Visual Studio Code (VSCode) などでコードファーストで開発を行えるような機能が提供されています。

github.com

特徴として以下のようなことが挙げられています。

  • ローカル開発と Power Platform 上で利用可能
  • Microsoft Entra 認証・認可を標準搭載
  • 1,500以上の Power Platform コネクタを JavaScript から直接呼び出し可能
  • コマンドラインから簡単にアプリを Power Platform に公開・ホスト
  • 組織のポリシー(共有制限、条件付きアクセス、DLP)を遵守

メリットとしては以下と紹介されていますね。

  • React、Angular、Vue などのカスタムコードを Power Platform 上でシームレスに実行
  • UI とロジックの完全制御
  • エンタープライズ認証
  • 簡易なデプロイと ALM

動画でのこちらの機能の紹介は以下 YouTube をご確認ください。

www.youtube.com

こちらの記事では Power Apps Code Apps + GitHub Copilot で Vibe Coding を体験するところまで記載します。

Power Apps Code Apps を試す

前提条件

開発者ツール

Power Apps Code Apps を利用するためには以下が必要になってきます。

インストールがまだの場合はまずは上記のインストールをお願いします。

Power Platform の環境設定

Power Apps Code Apps を利用できるようにするために、管理センターより設定の変更を行います。

  1. Power Platform 管理センターにアクセス
  2. 管理 > 環境 より、Power Apps Code Apps を利用したい環境を選択
  3. "設定"を選択
  4. 製品 > 機能 を選択
  5. Power Apps Code Apps の機能を有効化して保存

Power Apps Code Apps の機能が表示されない場合は以下を実行してくれとのことです。

[!NOTE]
If the Power Apps Code Apps setting doesn't appear in the admin center UI it is because a UI update hasn't reached your environment yet. You can get the setting to appear by appending a query string to the admin center URI. E.g.

https://admin.powerplatform.microsoft.com/manage/environments/1c137ea4-049e-ef11-8a66-000d3a106833/settings/Features
to
https://admin.powerplatform.microsoft.com/manage/environments/1c137ea4-049e-ef11-8a66-000d3a106833/settings/Features?ecs.ShowCodeAppSetting=true

ライセンス

Power Apps Premium licenseが必要なようです。

最初のプロジェクトを作成する

まずは YouTube に従って試してみようと思います。

VSCode を開き、ターミナル > 新しいターミナル より新しいターミナル(Ctrl + Shift + @)を起動します。

任意のディレクトリに移動して、以下コマンドを実行します。

npm create vite@latest MyFirstCodeApp -- --template react-ts

途中、実行していいか?という問いや、パッケージの名前を聞かれますので、適宜入力を行い進めて下さい。

次に ファイル > フォルダーを開く (Ctrl + K Ctrl + O)より、先ほど作成されたフォルダを選択します。

再びターミナルを起動させて以下を実行します。

npm install

次に Node.js の型定義ファイルのインストールを行います。

npm i --save-dev @types/node

実行が完了したら、"vite.config.ts"を以下の書き換えます。

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from "path";

// https://vite.dev/config/
export default defineConfig({
  base: "./",
  server: {
    host: "::",
    port: 3000,
  },
  plugins: [react()],
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});

github.com

書き換えたら以下を実行して Web アプリを起動させてみましょう。

npm run dev 

以下のように表示されると思いますので、"Ctrl" を押しながらクリックして開きます。

するとこのようなシンプルなページが表示されるかと思います。

確認が完了したら、VSCode に戻り、 Ctrl + C でサーバの停止を行います。

続いて、Power Platform テナントへの接続を行います。
以下を実行して認証情報の登録を行います。

pac auth create

サインインを行うポップアップが表示されるかと思いますので、任意のアカウントで認証を行ってください。

次に環境の選択を行います。

pac env select -env https://[Your Env].crm7.dynamics.com/

[Your Env] の箇所は自身の環境にあわせて変更してください。

Code App の初期化を行います。

pac code init --displayName "My First Code App"

これにより "power.config.json" が作成されているはずです。

続いて Power SDK のインストールを行います。

github.com

現時点では tag "v0.0.4" の "7-31-pa-client-power-code-sdk-0.0.1.tgz" が一番最新のようですので、こちらをインストールします。

github.com

npm install --save-dev "@pa-client/power-code-sdk@https://github.com/microsoft/PowerAppsCodeApps/releases/download/v0.0.4/7-31-pa-client-power-code-sdk-0.0.1.tgz"

インストールが完了したら "package.json" を以下のように書き換えて、 npm run dev を実行時に start pac code run も実行されるように修正します。

{
    "dev": "start pac code run && vite"
}
{
  "name": "myfirstcodeapp",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
-    "dev": "vite",
+    "dev": "start pac code run && vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^19.1.1",
    "react-dom": "^19.1.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.33.0",
    "@pa-client/power-code-sdk": "https://github.com/microsoft/PowerAppsCodeApps/releases/download/v0.0.4/7-31-pa-client-power-code-sdk-0.0.1.tgz",
    "@types/node": "^24.3.0",
    "@types/react": "^19.1.10",
    "@types/react-dom": "^19.1.7",
    "@vitejs/plugin-react": "^5.0.0",
    "eslint": "^9.33.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.20",
    "globals": "^16.3.0",
    "typescript": "~5.8.3",
    "typescript-eslint": "^8.39.1",
    "vite": "^7.1.2"
  }
}

次に "src" 配下に "PowerProvider.tsx" という TypeScipt ファイルの作成を行います。

以下をコピペします。

import { initialize } from "@pa-client/power-code-sdk/lib/Lifecycle";
import { useEffect, type ReactNode } from "react";

interface PowerProviderProps {
    children: ReactNode;
}

export default function PowerProvider({ children }: PowerProviderProps) {
    useEffect(() => {
        const initApp = async () => {
            try {
                await initialize();
                console.log('Power Platform SDK initialized successfully');
            } catch (error) {
                console.error('Failed to initialize Power Platform SDK:', error);
            }
        };
        
        initApp();
    }, []);

    return <>{children}</>;
}

github.com

続いて "main.tsx" に PowerProvider を追加します。

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'
import PowerProvider from './PowerProvider.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <PowerProvider>
      <App />
    </PowerProvider>
  </StrictMode>,
)

github.com

更新が完了したら再び以下を実行します。

npm run dev

これにより今度は Power SDK Server も起動しますので、こちらの記載の URL にアクセスしてみます。

すると以下のように Power Apps が起動されることを確認することができます!

記載の通り、これはローカルで実行されているだけで、クラウド上で利用可能な状態にはなっていません。

なので次はこのアプリを Power Apps 上にプッシュしてあげましょう。

まずはこれらのビルドを行います。

npm run build

ビルドが完了したらコードのプッシュを行います。

pac code push

少し待つとアプリが正常にプッシュされたというメッセージと共に URL が返されるのでアクセスしてみます。

アプリを保存中... アプリが正常にプッシュされました。 次の URL からアプリをテストできます\nhttps://apps.powerapps.com/play/e/[Your Env ID]/a/[Your App ID]

問題なくできていれば、以下のようにアプリが公開されているはずです。

アプリ一覧にも"コード"という種類で追加されていますね。
こちらは Power Apps Studio 上からでは編集できないようです。

おわりに

長くなったのでいったんここで区切ります。

データやコネクタ接続などはまた別で。

Generative Pages と違い、こちらはコードを弄ることができるので、コードに慣れた開発者視点でいうといいかもしれませんね。

ただし、Generative Pages と違いセットアップが面倒な点が課題かもしれません。

Power Apps や Copilot Studio のカスタム プロンプトでコードインタープリター機能を利用する

はじめに

Power Apps や Copilot Studio の カスタム プロンプト(AI Builder)にて「コード インタープリターを有効化」する機能が追加されました。

こちらは現在 Preview 環境でのみ利用可能です。

Preview 環境は以下のように URL 内に"Preview"を付与することで試すことができます。

Power Apps
https://make.preview.powerapps.com/

Copilot Studio
https://copilotstudio.preview.microsoft.com/

Learn ではまだこの情報は多分公開されていないかな?と思いますが、こちらの公式 YouTube チャンネルにて機能が紹介されていましたので、併せてご確認していただけるといいと思います。

www.youtube.com

コード インタープリターを試す

コード インタープリターの有効化

Preview 環境でも設定を行わないと、以下のように

この環境またはテナントでコード インタープリターが無効化されています。管理者にお問い合わせください。

というメッセージが表示され利用できません。

この設定は、Power Platform 管理センターの
パイロット > 設定 > Copilot Studio > Copilot Studio でのコード生成と実行
より、この機能を有効化したい環境を選択することで設定することができます。

コードインタープリター機能を備えたカスタム プロンプトの作成

それではコードインタープリター機能を利用したカスタム プロンプトを作成していきます。

今回は Copilot Studio で作成していくことにします。

まずはプロンプトの作成を行いましょう。

設定を開いたら、コードインタープリターの有効化を行います。

これで準備は完了です。

あとは任意のプロンプトを記載します。

今回は渡されたデータより、インサイト情報の可視化を行うようなカスタム プロンプトの作成をしてみます。

指示の例

あなたのタスク:
{{分析対象のデータ}} より、 {{開始日時}} から {{終了日時}} までのデータを取得し、インサイト情報の可視化を行います。
グラフには日本語は利用せずに英語を利用するようにしてください。

グラフは画像として返却してください。

サンプルとしてテスト実行してみます。

テストとして、以下より日経平均株価のデータをお借りしました。

indexes.nikkei.co.jp

グラフがちゃんと作成されていますね!

「コード」を選択すると生成され実行された Python コードが確認できます。

import logging
from workerinterfaces import ExecutorInterface, ConnectorClient, ExecutionResult, read_file_from_input, write_file_to_output
from typing import Any, Dict
import pandas as pd
import io
import matplotlib.pyplot as plt
import matplotlib

class PromptExecutor(ExecutorInterface):
    async def execute(self, logger: logging.Logger, connector_client: ConnectorClient, input: Dict[str, Any]) -> ExecutionResult:
        logger.info("Starting execution for graph visualization with English labels")
        output = {"files": []}
        message_id = input.get("RequestId", "")
        StreamFileOutputs = input.get("StreamFileOutputs", False)

        data_input_id = "_5206_6790_5bfe_8c61_306e_30c7_30fc_30bf"
        start_input_id = "_958b_59cb_65e5_6642"
        end_input_id = "_7d42_4e86_65e5_6642"

        # Read the uploaded data as document
        file_result = read_file_from_input(input, message_id, data_input_id)

        if file_result is None:
            logger.error("No data file provided.")
            return ExecutionResult(400, {}, {"text": "No data file was provided.", "mimetype": "text/markdown"})

        # Try to read file as Excel (from BytesIO or from file if string path)
        try:
            if isinstance(file_result, io.BytesIO):
                df = pd.read_excel(file_result)
            elif isinstance(file_result, str):
                df = pd.read_excel(file_result)
            else:
                logger.error("Unsupported file input type.")
                return ExecutionResult(400, {}, {"text": "Unsupported file input type.", "mimetype": "text/markdown"})
        except Exception as e:
            logger.error("Failed to read Excel: %s", str(e))
            return ExecutionResult(400, {}, {"text": f"Failed to read Excel file: {str(e)}", "mimetype": "text/markdown"})

        logger.info("Excel file successfully loaded. Columns: %s", [str(c) for c in df.columns])

        # Start/end date extraction and parsing
        start_str = input.get(start_input_id)
        end_str = input.get(end_input_id)
        if not start_str or not end_str:
            logger.error("Start or end date not provided.")
            return ExecutionResult(400, {}, {"text": "Start or end date missing.", "mimetype": "text/markdown"})

        try:
            start_date = pd.to_datetime(start_str, errors="raise")
            end_date = pd.to_datetime(end_str, errors="raise")
        except Exception as e:
            logger.error("Failed to parse dates: %s", str(e))
            return ExecutionResult(400, {}, {"text": f"Failed to parse start/end date: {str(e)}", "mimetype": "text/markdown"})

        # Log column types
        col_types = {col: str(df[col].dtype) for col in df.columns}
        logger.info("Loaded column types: %s", col_types)

        # Enhanced datetime column detection and parsing
        datetime_column = None
        possible_datetime_cols = []
        for col in df.columns:
            # If column name contains "日付" (date in Japanese), likely a candidate
            if "日付" in col or "date" in col.lower():
                possible_datetime_cols.append(col)
            # Otherwise check dtype
            if pd.api.types.is_datetime64_any_dtype(df[col]):
                possible_datetime_cols.append(col)
        if not possible_datetime_cols:
            # fallback: try every column for convertible to datetime
            for col in df.columns:
                try:
                    _test = pd.to_datetime(df[col], errors="raise")
                    possible_datetime_cols.append(col)
                except Exception:
                    continue

        # If multiple candidates, choose the first
        if possible_datetime_cols:
            datetime_column = possible_datetime_cols[0]
        else:
            logger.error("No datetime-like column found to filter.")
            return ExecutionResult(400, {}, {"text": "No datetime column found for date filtering.", "mimetype": "text/markdown"})

        # Force parsing of datetime column; log before/after min/max
        raw_dates = df[datetime_column].copy()
        try:
            df[datetime_column] = pd.to_datetime(df[datetime_column], errors="coerce")
            logger.info("Parsed '%s' column as datetime. Nulls: %d. Min: %s, Max: %s",
                        datetime_column,
                        df[datetime_column].isnull().sum(),
                        df[datetime_column].min(),
                        df[datetime_column].max())
            if df[datetime_column].isnull().all():
                logger.error("Failed to parse any dates in '%s' column.", datetime_column)
                return ExecutionResult(400, {}, {"text": f"Could not parse any dates in column: {datetime_column}. Please check the file's format and try again.", "mimetype": "text/markdown"})
        except Exception as e:
            logger.error("Error parsing datetime column '%s': %s", datetime_column, str(e))
            return ExecutionResult(400, {}, {"text": f"Error parsing column '{datetime_column}' as datetime: {str(e)}", "mimetype": "text/markdown"})

        # Filter DataFrame by date
        before_rows = df.shape[0]
        filtered_df = df[(df[datetime_column] >= start_date) & (df[datetime_column] <= end_date)]
        after_rows = filtered_df.shape[0]
        logger.info("Filter: dates between %s and %s; Before: %d rows, After: %d rows",
                    start_date.date(), end_date.date(), before_rows, after_rows)

        if filtered_df.empty:
            min_date, max_date = df[datetime_column].min(), df[datetime_column].max()
            msg = (
                f"No records in the specified date range ({start_date.date()} – {end_date.date()}). "
                f"Detected date column: '{datetime_column}' with range [{min_date.date() if pd.notnull(min_date) else 'N/A'} – "
                f"{max_date.date() if pd.notnull(max_date) else 'N/A'}]. "
                "Please verify that your file's date values match the intended filter, format, and timezone."
            )
            logger.warning("Zero records after filter. %s", msg.replace('\n', ' '))  # Avoid newlines in log
            return ExecutionResult(400, {}, {"text": msg, "mimetype": "text/markdown"})

        # Find value and/or category columns (fallback: use remaining non-date columns)
        value_col = None
        for col in filtered_df.columns:
            if col != datetime_column and pd.api.types.is_numeric_dtype(filtered_df[col]):
                value_col = col
                break
        category_col = None
        for col in filtered_df.columns:
            if col != datetime_column and not pd.api.types.is_numeric_dtype(filtered_df[col]):
                category_col = col
                break

        matplotlib.use('Agg')  # Output as image
        plt.figure(figsize=(8,5))

        plot_generated = False
        if value_col:
            if category_col and filtered_df[category_col].nunique() <= 10:
                for key, grp in filtered_df.groupby(category_col):
                    plt.plot(grp[datetime_column], grp[value_col], marker='o', label=str(key))
                plt.xlabel(str(datetime_column))
                plt.ylabel(str(value_col))
                plt.title(f"Time Series by {category_col}")
                plt.legend(title=str(category_col))
                plot_generated = True
            else:
                plt.plot(filtered_df[datetime_column], filtered_df[value_col], marker='o')
                plt.xlabel(str(datetime_column))
                plt.ylabel(str(value_col))
                plt.title("Time Series")
                plot_generated = True
        elif category_col:
            cat_counts = filtered_df[category_col].value_counts().sort_values(ascending=False)
            cat_counts.plot(kind="bar")
            plt.xlabel(str(category_col))
            plt.ylabel("Count")
            plt.title(f"Counts by {category_col}")
            plot_generated = True
        else:
            group = filtered_df.groupby(filtered_df[datetime_column].dt.date).size()
            group.plot(kind="bar")
            plt.xlabel("Date")
            plt.ylabel("Count")
            plt.title("Record Count by Date")
            plot_generated = True

        if not plot_generated:
            logger.info("No appropriate columns for plotting.")
            return ExecutionResult(400, {}, {"text": "No appropriate data found for plotting.", "mimetype": "text/markdown"})

        image_name = "insight_chart.png"
        image_bytes = io.BytesIO()
        plt.tight_layout()
        plt.savefig(image_bytes, format="png")
        plt.close()
        image_bytes.seek(0)

        write_file_to_output(
            input,
            output,
            message_id,
            image_name,
            "image/png",
            image_bytes
        )

        output["text"] = (
            "# Data Insight Chart\n\n"
            f"![Insight Chart]({image_name})\n\n"
            "This chart provides a visualization of data in the specified period. "
            f"All labels and titles are in English as required.\n"
            f"\nFiltered on date column '{datetime_column}' for range [{start_date.date()} – {end_date.date()}]."
        )
        output["mimetype"] = "text/markdown"
        logger.info("Chart generated and image prepared.")

        return ExecutionResult(200, {}, output)

「実行の選択」では実行されたログを確認することもできます。

2025-08-24 10:08:35,245 - CodeExecution [code.py:12 in function execute] - INFO - Starting execution for graph visualization with English labels
2025-08-24 10:08:36,935 - CodeExecution [code.py:41 in function execute] - INFO - Excel file successfully loaded. Columns: ['データ日付', '終値', '始値', '高値', '安値']
2025-08-24 10:08:36,951 - CodeExecution [code.py:59 in function execute] - INFO - Loaded column types: {'データ日付': 'object', '終値': 'float64', '始値': 'float64', '高値': 'float64', '安値': 'float64'}
2025-08-24 10:08:36,951 - CodeExecution [code.py:91 in function execute] - INFO - Parsed 'データ日付' column as datetime. Nulls: 1. Min: 2022-01-04 00:00:00, Max: 2025-08-22 00:00:00
2025-08-24 10:08:36,966 - CodeExecution [code.py:107 in function execute] - INFO - Filter: dates between 2025-08-01 and 2025-08-24; Before: 892 rows, After: 15 rows
2025-08-24 10:08:37,263 - CodeExecution [code.py:195 in function execute] - INFO - Chart generated and image prepared.

問題なければ保存を行い、カスタム プロンプトの説明を記載します。

例えばこんな感じ。

Copilot Studio で利用する

先ほど作成したカスタム プロンプトをエージェントに追加して試してみます。

...が、現状 Copilot Studio くんがファイルの読み込みをなんかうまいことやってくれないので動かないですね...

ということで、仕方ないので Dataverse のデータから情報を返すようにします。

これで情報返ってくるようにはなったものの、テストの画面では画像上手く表示できないですね...

Copilot Chat にデプロイしてみます。

表示できない...

スナップショットを保存して、どういった結果が返ってきているのか見てみましょう。

{
    "text": "2025年7月1日から2025年8月24日までの日経平均株価のデータを取得し、インサイト情報を可視化しました。以下のリンクからグラフをご覧いただけます。\n\n![Nikkei Stock Chart](nikkei_stock_base_chart.png)\n\nこのグラフには、以下の情報が含まれています:\n- 終値(ベース)\n- 始値(ベース)\n- 高値(ベース)\n- 安値(ベース)\n\n何か他にご質問や追加の分析が必要でしたら、お知らせください。"
}

![Nikkei Stock Chart](nikkei_stock_base_chart.png) が返されていますね。
これでは画像表示できなくっても当然かと思います。

出力形式を「ドキュメント/画像」に変更すればいけるのでは?と思われるかもですが

ここは変更して保存しても反映が行われないため注意です。

ということで HTML ベースで出力してみてもらいましょう。

出力を JSON に変更して、生成された HTML をみてみると、いい感じに出力されていそうです。

<div>
    <script type="text/javascript">window.PlotlyConfig = { MathJaxConfig: 'local' };</script>
    <script charset="utf-8" src="https://cdn.plot.ly/plotly-3.0.0.min.js"></script>
    <div id="425cfa7c-4706-4f4f-ba04-ec9f0cba8c01" class="plotly-graph-div" style="height:100%; width:100%;"></div>
    <script
        type="text/javascript">                window.PLOTLYENV = window.PLOTLYENV || {}; if (document.getElementById("425cfa7c-4706-4f4f-ba04-ec9f0cba8c01")) { Plotly.newPlot("425cfa7c-4706-4f4f-ba04-ec9f0cba8c01", [{ "connectgaps": true, "line": { "color": "#3366CC" }, "mode": "lines+markers", "name": "Closing Price (Base)", "x": ["2025-08-03T15:00:00.000000000", "2025-08-04T15:00:00.000000000", "2025-08-05T15:00:00.000000000", "2025-08-06T15:00:00.000000000", "2025-08-07T15:00:00.000000000", "2025-08-11T15:00:00.000000000", "2025-08-12T15:00:00.000000000", "2025-08-13T15:00:00.000000000", "2025-08-14T15:00:00.000000000", "2025-08-17T15:00:00.000000000", "2025-08-18T15:00:00.000000000", "2025-08-19T15:00:00.000000000", "2025-08-20T15:00:00.000000000", "2025-08-21T15:00:00.000000000"], "y": { "dtype": "f8", "bdata": "ZmZmZlas40B7FK5HsczjQFK4HoVb6+NAzczMzGQM5EDD9Shcj2vkQArXo3DF2+RACtejcFUh5UAfhetRKNPkQLgehetJLuVAuB6F60lY5UB7FK5HSUPlQJqZmZkR8eRACtejcEXO5EB7FK5HKdHkQA==" }, "type": "scatter" }, { "connectgaps": true, "line": { "color": "#DC3912" }, "mode": "lines+markers", "name": "Opening Price (Base)", "x": ["2025-08-03T15:00:00.000000000", "2025-08-04T15:00:00.000000000", "2025-08-05T15:00:00.000000000", "2025-08-06T15:00:00.000000000", "2025-08-07T15:00:00.000000000", "2025-08-11T15:00:00.000000000", "2025-08-12T15:00:00.000000000", "2025-08-13T15:00:00.000000000", "2025-08-14T15:00:00.000000000", "2025-08-17T15:00:00.000000000", "2025-08-18T15:00:00.000000000", "2025-08-19T15:00:00.000000000", "2025-08-20T15:00:00.000000000", "2025-08-21T15:00:00.000000000"], "y": { "dtype": "f8", "bdata": "uB6F69mW40AAAAAAIMnjQIXrUbjOveNASOF6FIbb40BmZmZmdijkQNejcD1KjuRA7FG4Hl0K5UApXI\u002fCJQ\u002flQI\u002fC9ShM7uRAzczMzJw35UCamZmZ0WjlQOxRuB4VMeVAH4XrUejj5EC4HoXrudDkQA==" }, "type": "scatter" }, { "connectgaps": true, "line": { "color": "#109618" }, "mode": "lines+markers", "name": "High Price (Base)", "x": ["2025-08-03T15:00:00.000000000", "2025-08-04T15:00:00.000000000", "2025-08-05T15:00:00.000000000", "2025-08-06T15:00:00.000000000", "2025-08-07T15:00:00.000000000", "2025-08-11T15:00:00.000000000", "2025-08-12T15:00:00.000000000", "2025-08-13T15:00:00.000000000", "2025-08-14T15:00:00.000000000", "2025-08-17T15:00:00.000000000", "2025-08-18T15:00:00.000000000", "2025-08-19T15:00:00.000000000", "2025-08-20T15:00:00.000000000", "2025-08-21T15:00:00.000000000"], "y": { "dtype": "f8", "bdata": "AAAAAIix40CPwvUoLNnjQK5H4Xpc8uNAUrgehYMZ5EAK16NwPYbkQIXrUbj2\u002fuRAhetRuG435UD2KFyP+hflQClcj8KtMeVAcT0K12Nn5UAK16NwjWzlQFyPwvU4NOVAUrgehcP15EDXo3A9EtzkQA==" }, "type": "scatter" }, { "connectgaps": true, "line": { "color": "#FF9900" }, "mode": "lines+markers", "name": "Low Price (Base)", "x": ["2025-08-03T15:00:00.000000000", "2025-08-04T15:00:00.000000000", "2025-08-05T15:00:00.000000000", "2025-08-06T15:00:00.000000000", "2025-08-07T15:00:00.000000000", "2025-08-11T15:00:00.000000000", "2025-08-12T15:00:00.000000000", "2025-08-13T15:00:00.000000000", "2025-08-14T15:00:00.000000000", "2025-08-17T15:00:00.000000000", "2025-08-18T15:00:00.000000000", "2025-08-19T15:00:00.000000000", "2025-08-20T15:00:00.000000000", "2025-08-21T15:00:00.000000000"], "y": { "dtype": "f8", "bdata": "PQrXo1B140CPwvUovLzjQI\u002fC9SjkvONASOF6FIbb40CamZmZASTkQPYoXI9yjORAZmZmZgYA5UDD9Shc183kQFK4HoUb3uRApHA9Cscv5UCkcD0KfzLlQM3MzMyE3ORAmpmZmYnI5ECuR+F6bKvkQA==" }, "type": "scatter" }], { "template": { "data": { "barpolar": [{ "marker": { "line": { "color": "white", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "barpolar" }], "bar": [{ "error_x": { "color": "#2a3f5f" }, "error_y": { "color": "#2a3f5f" }, "marker": { "line": { "color": "white", "width": 0.5 }, "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "bar" }], "carpet": [{ "aaxis": { "endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f" }, "baxis": { "endlinecolor": "#2a3f5f", "gridcolor": "#C8D4E3", "linecolor": "#C8D4E3", "minorgridcolor": "#C8D4E3", "startlinecolor": "#2a3f5f" }, "type": "carpet" }], "choropleth": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "choropleth" }], "contourcarpet": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "contourcarpet" }], "contour": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "contour" }], "heatmap": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "heatmap" }], "histogram2dcontour": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2dcontour" }], "histogram2d": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "histogram2d" }], "histogram": [{ "marker": { "pattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 } }, "type": "histogram" }], "mesh3d": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "type": "mesh3d" }], "parcoords": [{ "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "parcoords" }], "pie": [{ "automargin": true, "type": "pie" }], "scatter3d": [{ "line": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatter3d" }], "scattercarpet": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattercarpet" }], "scattergeo": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergeo" }], "scattergl": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattergl" }], "scattermapbox": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermapbox" }], "scattermap": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scattermap" }], "scatterpolargl": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolargl" }], "scatterpolar": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterpolar" }], "scatter": [{ "fillpattern": { "fillmode": "overlay", "size": 10, "solidity": 0.2 }, "type": "scatter" }], "scatterternary": [{ "marker": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "type": "scatterternary" }], "surface": [{ "colorbar": { "outlinewidth": 0, "ticks": "" }, "colorscale": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "type": "surface" }], "table": [{ "cells": { "fill": { "color": "#EBF0F8" }, "line": { "color": "white" } }, "header": { "fill": { "color": "#C8D4E3" }, "line": { "color": "white" } }, "type": "table" }] }, "layout": { "annotationdefaults": { "arrowcolor": "#2a3f5f", "arrowhead": 0, "arrowwidth": 1 }, "autotypenumbers": "strict", "coloraxis": { "colorbar": { "outlinewidth": 0, "ticks": "" } }, "colorscale": { "diverging": [[0, "#8e0152"], [0.1, "#c51b7d"], [0.2, "#de77ae"], [0.3, "#f1b6da"], [0.4, "#fde0ef"], [0.5, "#f7f7f7"], [0.6, "#e6f5d0"], [0.7, "#b8e186"], [0.8, "#7fbc41"], [0.9, "#4d9221"], [1, "#276419"]], "sequential": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]], "sequentialminus": [[0.0, "#0d0887"], [0.1111111111111111, "#46039f"], [0.2222222222222222, "#7201a8"], [0.3333333333333333, "#9c179e"], [0.4444444444444444, "#bd3786"], [0.5555555555555556, "#d8576b"], [0.6666666666666666, "#ed7953"], [0.7777777777777778, "#fb9f3a"], [0.8888888888888888, "#fdca26"], [1.0, "#f0f921"]] }, "colorway": ["#636efa", "#EF553B", "#00cc96", "#ab63fa", "#FFA15A", "#19d3f3", "#FF6692", "#B6E880", "#FF97FF", "#FECB52"], "font": { "color": "#2a3f5f" }, "geo": { "bgcolor": "white", "lakecolor": "white", "landcolor": "white", "showlakes": true, "showland": true, "subunitcolor": "#C8D4E3" }, "hoverlabel": { "align": "left" }, "hovermode": "closest", "mapbox": { "style": "light" }, "paper_bgcolor": "white", "plot_bgcolor": "white", "polar": { "angularaxis": { "gridcolor": "#EBF0F8", "linecolor": "#EBF0F8", "ticks": "" }, "bgcolor": "white", "radialaxis": { "gridcolor": "#EBF0F8", "linecolor": "#EBF0F8", "ticks": "" } }, "scene": { "xaxis": { "backgroundcolor": "white", "gridcolor": "#DFE8F3", "gridwidth": 2, "linecolor": "#EBF0F8", "showbackground": true, "ticks": "", "zerolinecolor": "#EBF0F8" }, "yaxis": { "backgroundcolor": "white", "gridcolor": "#DFE8F3", "gridwidth": 2, "linecolor": "#EBF0F8", "showbackground": true, "ticks": "", "zerolinecolor": "#EBF0F8" }, "zaxis": { "backgroundcolor": "white", "gridcolor": "#DFE8F3", "gridwidth": 2, "linecolor": "#EBF0F8", "showbackground": true, "ticks": "", "zerolinecolor": "#EBF0F8" } }, "shapedefaults": { "line": { "color": "#2a3f5f" } }, "ternary": { "aaxis": { "gridcolor": "#DFE8F3", "linecolor": "#A2B1C6", "ticks": "" }, "baxis": { "gridcolor": "#DFE8F3", "linecolor": "#A2B1C6", "ticks": "" }, "bgcolor": "white", "caxis": { "gridcolor": "#DFE8F3", "linecolor": "#A2B1C6", "ticks": "" } }, "title": { "x": 0.05 }, "xaxis": { "automargin": true, "gridcolor": "#EBF0F8", "linecolor": "#EBF0F8", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "#EBF0F8", "zerolinewidth": 2 }, "yaxis": { "automargin": true, "gridcolor": "#EBF0F8", "linecolor": "#EBF0F8", "ticks": "", "title": { "standoff": 15 }, "zerolinecolor": "#EBF0F8", "zerolinewidth": 2 } } }, "font": { "family": "Arial", "size": 14 }, "title": { "text": "Nikkei Stock Average (Base Prices)" }, "xaxis": { "title": { "text": "Date" } }, "yaxis": { "title": { "text": "Price (Base Currency)" } }, "legend": { "title": { "text": "Series" } } }, { "displayModeBar": false, "responsive": true }) };            </script>
</div>

こいつを利用してみると...

リンクを返しやがりましたね。

これは Copilot Chat で利用した場合も同様です。

以下のリンクから、2025年7月1日から2025年8月24日までの日経平均株価のデータを可視化したグラフをご覧いただけます。

日経平均株価のグラフ

このグラフには、期間中の終値始値、高値、安値が表示されています。詳細なインサイトや追加の分析が必要な場合は、お知らせください。

ダメだ!うまくいかない!!

試した限り、現状ファイルや画像はチャット内で利用できないっぽいですね...

まだ Preview なんで仕方ないかなー

ただ他の分析なんかは動きますので、単純にテキストを変換するようなものは問題なく利用できると思います!

Python を利用した分析などを行わせたい場合はこちらが活用できそうですね。

ライブラリ指定の際の注意点

例えば以下のような指示文で from matplotlib.animation import FuncAnimation を利用させるように指示をだしてみます。

指示

地球の円運動を説明するために可視化を行ってください。
from matplotlib.animation import FuncAnimation を用いてアニメーションで説明してください。

可視化の結果はアニメーション Gif として応答してください。

すると指定したライブラリを利用してコードの生成を行ってくれます。

ただし、以下のように googlefinance ライブラリを使って。とお願いした場合は、指定のライブラリは使えないとのエラーが返されます。

ちなみに yfinance もダメです。

どんなライブラリでも使えるわけではない。ということに注意しましょう。

おわりに

カスタム プロンプトでのコードインタープリター機能が利用できるようになったことで、エージェント活用の幅が広がりましたね!

ただ、肝心のエージェント側の入出力が追い付いてないっぽいので、何とかして欲しいですかね...

こうやったらうまくいったよーという報告あればお願いします!!!


スポンサードリンク