Lab E5 - Adaptive Cards の追加
この lab では、Microsoft 365 Copilot からの応答をテキストからリッチ カードへと強化するために Adaptive Cards を使用します。
この lab で学習する内容:
- Adaptive Cards とは何か
- Adaptive Card を作成してテストする方法
- Microsoft 365 Copilot の応答を Adaptive Cards でリッチ コンテンツに更新する方法

Microsoft 365 が AI モデルとオーケストレーションを提供する宣言型エージェントを構築したい場合は、これらのラボを実施してください。
- 🏁 はじめに
- 🔧 セットアップ
- 🧰 宣言型エージェントの基礎
- 🛠️ API をゼロから構築して統合する
- 🔐 認証
- 🔌 統合
Introduction
Adaptive Cards とは?
Adaptive Cards は、JSON で記述されたプラットフォーム非依存の UI スニペットで、アプリやサービス間でやり取りできます。アプリに届けられると、JSON がネイティブ UI に変換され、環境に自動的に適応します。これにより、主要プラットフォームとフレームワークを跨いで軽量 UI を設計・統合できます。Exercise 1: シンプルな Adaptive Card の作成とテスト
では、Adaptive Card の作成がどれほど楽しいか体験してみましょう。
Step 1: JSON で Adaptive Card を定義する
以下は Adaptive Card の JSON です。まずはコピーしてください。
{
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"text": "Hello, Adaptive Cards!",
"size": "large",
"weight": "bolder"
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "Click me",
"url":"https://www.contoso.com"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.3"
}
この JSON は、テキスト ブロックとボタンが含まれるシンプルな Adaptive Card を定義しています。
Step 2: Adaptive Card をテストする
Adaptive Card をテストするには、Adaptive Cards Designer を利用します。
- Adaptive Cards Designer を開きます。
adaptiveCard.json
ファイルの JSON コンテンツをコピーします。- デザイナー下部の「Card Payload Editor」セクションにペーストします。
- 上部にライブ プレビューが表示されます。
おめでとうございます! これでプラグイン用の Adaptive Card を開発するスキルが身に付きました。
Exercise 2: プラグイン マニフェストの更新
appPackage フォルダーにある trey-plugin.json のプラグイン マニフェスト ファイルを更新し、Adaptive Cards を使用した応答テンプレートを追加します。各関数または API 呼び出しごとにテンプレートを更新します。
Step 1: GET /api/consultants 用の Adaptive Card を追加する
- getConsultants 関数を探し、
properties
ノードの後に以下のstatic_template
ノードを追加します。
"static_template": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"speak": "${name}",
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "${name}",
"weight": "bolder",
"size": "extraLarge",
"spacing": "none",
"wrap": true,
"style": "heading"
},
{
"type": "TextBlock",
"text": "${email}",
"wrap": true,
"spacing": "none"
},
{
"type": "TextBlock",
"text": "${phone}",
"wrap": true,
"spacing": "none"
},
{
"type": "TextBlock",
"text": "${location.city}, ${location.country}",
"wrap": true
}
]
},
{
"type": "Column",
"items": [
{
"type": "Image",
"url": "${consultantPhotoUrl}",
"altText": "${name}"
}
]
}
]
}
]
}
]
}
Step 2: GET /api/me 用の Adaptive Card を追加する
- getUserInformation 関数を探し、
properties
ノードの後に以下のstatic_template
ノードを追加します。
"static_template":{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"speak": "${name}",
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "${name}",
"weight": "bolder",
"size": "extraLarge",
"spacing": "none",
"wrap": true,
"style": "heading"
},
{
"type": "TextBlock",
"text": "${email}",
"wrap": true,
"spacing": "none"
},
{
"type": "TextBlock",
"text": "${phone}",
"wrap": true,
"spacing": "none"
},
{
"type": "TextBlock",
"text": "${location.city}, ${location.country}",
"wrap": true
}
]
},
{
"type": "Column",
"items": [
{
"type": "Image",
"url": "${consultantPhotoUrl}",
"altText": "${name}"
}
]
}
]
}
]
}
]
}
Step 3: GET /api/projects 用の Adaptive Card を追加する
- getProjects 関数を探し、
properties
ノードの後に以下のstatic_template
ノードを追加します。
"static_template": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "Container",
"$data": "${$root}",
"items": [
{
"speak": "${description}",
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"items": [
{
"type": "TextBlock",
"text": "${name}",
"weight": "bolder",
"size": "extraLarge",
"spacing": "none",
"wrap": true,
"style": "heading"
},
{
"type": "TextBlock",
"text": "${description}",
"wrap": true,
"spacing": "none"
},
{
"type": "TextBlock",
"text": "${location.city}, ${location.country}",
"wrap": true
},
{
"type": "TextBlock",
"text": "${clientName}",
"weight": "Bolder",
"size": "Large",
"spacing": "Medium",
"wrap": true,
"maxLines": 3
},
{
"type": "TextBlock",
"text": "${clientContact}",
"size": "small",
"wrap": true
},
{
"type": "TextBlock",
"text": "${clientEmail}",
"size": "small",
"wrap": true
}
]
},
{
"type": "Column",
"items": [
{
"type": "Image",
"url": "${location.mapUrl}",
"altText": "${location.street}"
}
]
}
]
}
]
},
{
"type": "TextBlock",
"text": "Project Metrics",
"weight": "Bolder",
"size": "Large",
"spacing": "Medium",
"horizontalAlignment": "Center",
"separator": true
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "Forecast This Month",
"weight": "Bolder",
"spacing": "Small",
"horizontalAlignment": "Center"
},
{
"type": "TextBlock",
"text": "${forecastThisMonth} ",
"size": "ExtraLarge",
"weight": "Bolder",
"horizontalAlignment": "Center"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "Forecast Next Month",
"weight": "Bolder",
"spacing": "Small",
"horizontalAlignment": "Center"
},
{
"type": "TextBlock",
"text": "${forecastNextMonth} ",
"size": "ExtraLarge",
"weight": "Bolder",
"horizontalAlignment": "Center"
}
]
}
]
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "Delivered Last Month",
"weight": "Bolder",
"spacing": "Small",
"horizontalAlignment": "Center"
},
{
"type": "TextBlock",
"text": "${deliveredLastMonth} ",
"size": "ExtraLarge",
"weight": "Bolder",
"horizontalAlignment": "Center"
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "TextBlock",
"text": "Delivered This Month",
"weight": "Bolder",
"spacing": "Small",
"horizontalAlignment": "Center"
},
{
"type": "TextBlock",
"text": "${deliveredThisMonth} ",
"size": "ExtraLarge",
"weight": "Bolder",
"horizontalAlignment": "Center"
}
]
}
]
}
],
"actions": [
{
"type": "Action.OpenUrl",
"title": "View map",
"url": "${location.mapUrl}"
}
]
}
Step 4: POST /api/billHours 用の Adaptive Card を追加する
- postBillhours 関数を探し、
properties
ノードの後に以下のstatic_template
ノードを追加します。
"static_template": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Project Status Summary",
"weight": "Bolder",
"size": "Large",
"separator": true
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Client Name",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.clientName, results.clientName, 'N/A')}",
"wrap": true
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Project Name",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.projectName, results.projectName, 'N/A')}",
"wrap": true
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Remaining Forecast",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.remainingForecast, results.remainingForecast, 'N/A')}",
"wrap": true
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Message",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.message, results.message, 'N/A')}",
"wrap": true
}
]
}
]
}
Step 5: POST /api/assignConsultant 用の Adaptive Card を追加する
- postAssignConsultant 関数を探し、
properties
ノードの後に以下のstatic_template
ノードを追加します。
"static_template": {
"type": "AdaptiveCard",
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Project Overview",
"weight": "Bolder",
"size": "Large",
"separator": true,
"spacing": "Medium"
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Client Name",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.clientName, results.clientName, 'N/A')}",
"wrap": true
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Project Name",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.projectName, results.projectName, 'N/A')}",
"wrap": true
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Consultant Name",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.consultantName, results.consultantName, 'N/A')}",
"wrap": true
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Remaining Forecast",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.remainingForecast, results.remainingForecast, 'N/A')}",
"wrap": true
}
]
},
{
"type": "Container",
"items": [
{
"type": "TextBlock",
"text": "Message",
"weight": "Bolder",
"spacing": "Small"
},
{
"type": "TextBlock",
"text": "${if(results.message, results.message, 'N/A')}",
"wrap": true
}
]
}
]
}
Exercise 3: Copilot でプラグインをテストする
アプリケーションをテストする前に、appPackage\manifest.json
ファイルでアプリ パッケージのマニフェスト バージョンを更新します。
-
プロジェクトの
appPackage
フォルダーにあるmanifest.json
を開きます。 -
JSON ファイル内の
version
フィールドを探します。次のようになっています:
json "version": "1.0.1"
-
バージョン番号を小さい増分で上げます。例:
json "version": "1.0.2"
-
変更後、ファイルを保存します。
Step 1: プラグインをインストールする
プロジェクトを停止して再起動し、アプリケーション パッケージを再デプロイさせます。
Microsoft Teams が起動します。Copilot に戻ったら、右側のフライアウト 1️⃣ を開いて過去のチャットと宣言型エージェントを表示し、Trey Genie Local エージェント 2️⃣ を選択します。
Step 2: Adaptive Card を表示する
次のようなプロンプトを試してみましょう。
what projects are we doing for adatum?
テキスト応答だけでなく、プロジェクト情報を含むリッチ カードも表示されます。
次に POST 操作のプロンプトを試します。
please charge 1 hour to woodgrove bank in trey research
このリクエストは Copilot が API プラグインへ POST でデータを送信する必要があるため、Confirm ボタンで許可を確認する必要があります。
確認すると、テキスト応答だけでなく、プロジェクトの情報を含むリッチ カードが表示されます。
他のプロンプトも試して、Microsoft 365 Copilot の改善された応答を確認してみてください。
おめでとうございます!
Adaptive Card 応答を最初の API プラグインに追加しました。次の lab では API に認証を追加します。
OAuth 2.0 と Agents Toolkit を使用して認証を追加する lab に進みましょう。これは最も簡単な方法で、Agents Toolkit の自動 Entra ID 登録を使用した F5 プロジェクト開始エクスペリエンスを学習できます。