Exercise - Return rich responses with Adaptive Cards
Let's start by building Adaptive Card templates for the agent to show the data in its responses. To build the Adaptive Card template, you use the Adaptive Card Previewer Visual Studio Code extensions to easily preview your work directly in Visual Studio Code. Using the extension allows us to build an Adaptive Card template, with references to data. At runtime, the agent fills the placeholder with data it retrieves from the API.
Download the starter project
Start by downloading the sample project. In a web browser:
Navigate to https://github.com/microsoft/learn-declarative-agent-api-plugin-adaptive-cards-typescript.
If you have a GitHub account:
Select Use this template dropdown and from the menu choose Create a new repository.
From the list of available owners, choose your account.
Name the repository da-ristorante-api.
Confirm creating the repository using the Create repository button.
Wait for GitHub to create the repository. Then, copy the repository URL.
Open a command line.
In a command line, change the working directory to where you want to store the project on your disk.
Clone the repository using the following command:
git clone https://github.com/your-user/your-repo
.Open the cloned folder in Visual Studio Code.
If you don't have a GitHub account:
The sample project is a Microsoft 365 Agents Toolkit project that includes a declarative agent with an action built with an API plugin. The API plugin connects to an anonymous API running on Azure Functions also included in the project. The API belongs to a fictitious Italian restaurant and allows you to browse today's menu and place orders.
Build an Adaptive Card for a dish
First, create an Adaptive Card that shows information about a single dish.
In Visual Studio Code:
In the Explorer view, create a new folder named cards.
In the cards folder, create a new file named dish.json. Paste the following contents that represent an empty Adaptive Card:
{ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.5", "body": [] }
Before you continue, create a data file for the Adaptive Card:
Open the command palette by pressing CTRL+P (CMD+P on macOS) on the keyboard. Type
>Adaptive
to find commands related to working with Adaptive Cards.From the list, choose Adaptive Card: New Data File. Visual Studio Code creates a new file named dish.data.json.
Replace its contents with a data that represents a dish:
{ "id": 4, "name": "Caprese Salad", "description": "Juicy vine-ripened tomatoes, fresh mozzarella, and fragrant basil leaves, drizzled with extra virgin olive oil and a touch of balsamic.", "image_url": "https://raw.githubusercontent.com/pnp/copilot-pro-dev-samples/main/samples/da-ristorante-api/assets/caprese_salad.jpeg", "price": 10.5, "allergens": [ "dairy" ], "course": "lunch", "type": "dish" }
Save your changes
Go back to the dish.json file.
From the lens, select Preview Adaptive Card.
Visual Studio Code opens a preview of the card to the side. As you're editing the card, your changes are immediately visible on the side.
To the body array, add a Container element with a reference to the image URL stored in the image_url property.
{ "type": "Container", "items": [ { "type": "Image", "url": "${image_url}", "size": "large" } ] }
Notice how the card preview automatically updates to show your card:
Add references to other dish properties. The complete card looks as follows:
{ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.5", "body": [ { "type": "Container", "items": [ { "type": "Image", "url": "${image_url}", "size": "large" }, { "type": "TextBlock", "text": "${name}", "weight": "Bolder" }, { "type": "TextBlock", "text": "${description}", "wrap": true }, { "type": "TextBlock", "text": "Allergens: ${if(count(allergens) > 0, join(allergens, ', '), 'none')}", "weight": "Lighter" }, { "type": "TextBlock", "text": "**Price:** €${formatNumber(price, 2)}", "weight": "Lighter", "spacing": "None" } ] } ] }
Notice that to display allergens you use a function to join the allergens into a string. If a dish doesn't have allergens, you display none. To ensure that prices are properly formatted, you use the formatNumber function that allows us to specify the number of decimals to show on the card.
Build an Adaptive Card for the order summary
The sample API allows users to browse the menu and place an order. Let's create an adaptive card that shows the order summary.
In Visual Studio Code:
In the cards folder, create a new file named order.json. Paste the following contents that represent an empty Adaptive Card:
{ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.5", "body": [] }
Create a data file for the Adaptive Card:
Open the command palette by pressing CTRL+P (CMD+P on macOS) on the keyboard. Type
>Adaptive
to find commands related to working with Adaptive Cards.From the list, choose Adaptive Card: New Data File. Visual Studio Code creates a new file named order.data.json.
Replace its contents with a data that represents the order summary:
{ "order_id": 6210, "status": "confirmed", "total_price": 25.48 }
Save your changes
Go back to the order.json file.
From the lens, select Preview Adaptive Card.
Next, replace the contents of the order.json file with the following code:
{ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.5", "body": [ { "type": "TextBlock", "text": "Order Confirmation 🤌", "size": "Large", "weight": "Bolder", "horizontalAlignment": "Center" }, { "type": "Container", "items": [ { "type": "TextBlock", "text": "Your order has been successfully placed!", "weight": "Bolder", "spacing": "Small" }, { "type": "FactSet", "facts": [ { "title": "Order ID:", "value": "${order_id} " }, { "title": "Status:", "value": "${status}" }, { "title": "Total Price:", "value": "€${formatNumber(total_price, 2)}" } ] } ] } ] }
Just like in the previous section, you map each element on the card to a data property.
Important
Notice the trailing space after ${order_id}. This is intentional, because of a known issue with Adaptive Cards rendering numbers. To test it, remove the space and see that the number disappears from the preview.
Restore the trailing space so that your card shows properly and save your changes.
Update the API plugin definition
The final step is to update the API plugin definition with Adaptive Cards that Copilot should use to display data from the API to users.
Add Adaptive Card to display a dish
In Visual Studio Code:
Open the cards/dish.json file and copy its contents.
Open the appPackage/ai-plugin.json file.
To the functions.getDishes.capabilities.response_semantics property, add a new property named static_template and set its contents to the Adaptive Card.
The complete code snippet looks like:
{ "$schema": "https://aka.ms/json-schemas/copilot/plugin/v2.1/schema.json", "schema_version": "v2.1", "namespace": "ilristorante", "name_for_human": "Il Ristorante", "description_for_human": "See the today's menu and place orders", "description_for_model": "Plugin for getting the today's menu, optionally filtered by course and allergens, and placing orders", "functions": [ { "name": "getDishes", "description": "Returns information about the dishes on the menu. Can filter by course (breakfast, lunch or dinner), name, allergens, or type (dish, drink).", "capabilities": { "response_semantics": { "data_path": "$.dishes", "properties": { "title": "$.name", "subtitle": "$.description" }, "static_template": { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.5", "body": [ ...trimmed for brevity ] } } } }, { "name": "placeOrder", "description": "Places an order and returns the order details", "capabilities": { "response_semantics": { "data_path": "$", "properties": { "title": "$.order_id", "subtitle": "$.total_price" } } } } ], "runtimes": [ { "type": "OpenApi", "auth": { "type": "None" }, "spec": { "url": "apiSpecificationFile/ristorante.yml" }, "run_for_functions": [ "getDishes", "placeOrder" ] } ], "capabilities": { "localization": {}, "conversation_starters": [] } }
Save your changes.
Add Adaptive Card template to display the order summary
In Visual Studio Code:
Open the cards/order.json file and copy its contents.
Open the appPackage/ai-plugin.json file.
To the functions.placeOrder.capabilities.response_semantics property, add a new property named static_template and set its contents to the Adaptive Card.
The complete file looks like:
{ "$schema": "https://aka.ms/json-schemas/copilot/plugin/v2.1/schema.json", "schema_version": "v2.1", "namespace": "ilristorante", "name_for_human": "Il Ristorante", "description_for_human": "See the today's menu and place orders", "description_for_model": "Plugin for getting the today's menu, optionally filtered by course and allergens, and placing orders", "functions": [ { "name": "getDishes", "description": "Returns information about the dishes on the menu. Can filter by course (breakfast, lunch or dinner), name, allergens, or type (dish, drink).", "capabilities": { "response_semantics": { "data_path": "$.dishes", "properties": { "title": "$.name", "subtitle": "$.description" }, "static_template": { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.5", "body": [ ...trimmed for brevity ] } } } }, { "name": "placeOrder", "description": "Places an order and returns the order details", "capabilities": { "response_semantics": { "data_path": "$", "properties": { "title": "$.order_id", "subtitle": "$.total_price" }, "static_template": { "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", "type": "AdaptiveCard", "version": "1.5", "body": [ ...trimmed for brevity ] } } } } ], "runtimes": [ { "type": "OpenApi", "auth": { "type": "None" }, "spec": { "url": "apiSpecificationFile/ristorante.yml" }, "run_for_functions": [ "getDishes", "placeOrder" ] } ], "capabilities": { "localization": {}, "conversation_starters": [] } }
Save your changes.
Test the declarative agent with API plugin in Microsoft 365 Copilot
The final step is to test the declarative agent with API plugin in Microsoft 365 Copilot.
In Visual Studio Code:
From the Activity Bar, choose Microsoft 365 Agents Toolkit.
In the Accounts section, ensure that you're signed in to your Microsoft 365 tenant with Microsoft 365 Copilot.
From the Activity Bar, choose Run and Debug.
Select the Debug in Copilot configuration and start debugging using the Start Debugging button.
Visual Studio Code builds and deploys your project to your Microsoft 365 tenant and opens a new web browser window.
In the web browser:
When prompted, sign in with the account that belongs to your Microsoft 365 tenant with Microsoft 365 Copilot.
From the side bar, select Il Ristorante.
Choose the What's for lunch today? conversation starter and submit the prompt.
When prompted, examine the data that the agent sends to the API and confirm using the Allow once button.
Wait for the agent to respond. Notice that the popup on a citation now includes your custom Adaptive Card with additional information from the API.
Place an order, by typing in the prompt text box: 1x spaghetti, 1x iced tea and submit the prompt.
Examine the data that the agent sends to the API and continue using the Confirm button.
Wait for the agent to place the order and return the order summary. Notice, that because the API returns a single item, the agent renders it using an Adaptive Card and includes the card directly in its response.
Go back to Visual Studio Code and stop debugging.
Switch to the Terminal tab and close all active terminals.