Exercise - Return rich responses with Adaptive Cards

Completed

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:

  1. Navigate to https://github.com/microsoft/learn-declarative-agent-api-plugin-adaptive-cards-typescript.

  2. If you have a GitHub account:

    1. Select Use this template dropdown and from the menu choose Create a new repository.

      Screenshot of the option on GitHub to create a new repository.

    2. From the list of available owners, choose your account.

    3. Name the repository da-ristorante-api.

    4. Confirm creating the repository using the Create repository button.

    5. Wait for GitHub to create the repository. Then, copy the repository URL.

      Screenshot of the option on GitHub to copy the repository URL.

    6. Open a command line.

    7. In a command line, change the working directory to where you want to store the project on your disk.

    8. Clone the repository using the following command: git clone https://github.com/your-user/your-repo.

    9. Open the cloned folder in Visual Studio Code.

  3. If you don't have a GitHub account:

    1. Download the repository contents as a ZIP.

      Screenshot of the option on GitHub to download the repository as a ZIP.

    2. Extract the contents of the ZIP on your disk.

    3. Open the folder in Visual Studio Code.

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:

  1. In the Explorer view, create a new folder named cards.

  2. 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": []
    }
    
  3. Before you continue, create a data file for the Adaptive Card:

    1. 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.

      Screenshot of Visual Studio Code showing commands related to working with Adaptive Cards.

    2. From the list, choose Adaptive Card: New Data File. Visual Studio Code creates a new file named dish.data.json.

    3. 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"
      }
      
    4. Save your changes

  4. Go back to the dish.json file.

  5. From the lens, select Preview Adaptive Card.

    Screenshot of Visual Studio Code showing the Adaptive Card preview.

    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.

  6. 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:

    Screenshot of Visual Studio Code showing the Adaptive Card preview with an image.

  7. 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"
            }
          ]
        }
      ]
    }
    

    Screenshot of Visual Studio Code showing the preview of an Adaptive Card of a dish.

    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:

  1. 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": []
    }
    
  2. Create a data file for the Adaptive Card:

    1. 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.

      Screenshot of Visual Studio Code showing commands related to working with Adaptive Cards.

    2. From the list, choose Adaptive Card: New Data File. Visual Studio Code creates a new file named order.data.json.

    3. Replace its contents with a data that represents the order summary:

      {
        "order_id": 6210,
        "status": "confirmed",
        "total_price": 25.48
      }
      
    4. Save your changes

  3. Go back to the order.json file.

  4. From the lens, select Preview Adaptive Card.

  5. 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.

    Screenshot of Visual Studio Code showing the Adaptive Card preview of an order.

    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.

    Screenshot of Visual Studio Code showing a preview of an Adaptive Card without the order number.

    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:

  1. Open the cards/dish.json file and copy its contents.

  2. Open the appPackage/ai-plugin.json file.

  3. To the functions.getDishes.capabilities.response_semantics property, add a new property named static_template and set its contents to the Adaptive Card.

  4. 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": []
      }
    }
    
  5. Save your changes.

Add Adaptive Card template to display the order summary

In Visual Studio Code:

  1. Open the cards/order.json file and copy its contents.

  2. Open the appPackage/ai-plugin.json file.

  3. To the functions.placeOrder.capabilities.response_semantics property, add a new property named static_template and set its contents to the Adaptive Card.

  4. 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": []
      }
    }
    
  5. 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:

  1. From the Activity Bar, choose Microsoft 365 Agents Toolkit.

  2. In the Accounts section, ensure that you're signed in to your Microsoft 365 tenant with Microsoft 365 Copilot.

    Screenshot of the Microsoft 365 Agents Toolkit accounts section in Visual Studio Code.

  3. From the Activity Bar, choose Run and Debug.

  4. Select the Debug in Copilot configuration and start debugging using the Start Debugging button.

    Screenshot of the Debug in Copilot configuration in Visual Studio Code.

  5. Visual Studio Code builds and deploys your project to your Microsoft 365 tenant and opens a new web browser window.

In the web browser:

  1. When prompted, sign in with the account that belongs to your Microsoft 365 tenant with Microsoft 365 Copilot.

  2. From the side bar, select Il Ristorante.

    Screenshot of the Microsoft 365 Copilot interface with the Il Ristorante agent selected.

  3. Choose the What's for lunch today? conversation starter and submit the prompt.

    Screenshot of the Microsoft 365 Copilot interface with the lunch prompt.

  4. When prompted, examine the data that the agent sends to the API and confirm using the Allow once button.

    Screenshot of the Microsoft 365 Copilot interface with the lunch confirmation.

  5. 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.

    Screenshot of the Microsoft 365 Copilot interface with the lunch response.

  6. Place an order, by typing in the prompt text box: 1x spaghetti, 1x iced tea and submit the prompt.

  7. Examine the data that the agent sends to the API and continue using the Confirm button.

    Screenshot of the Microsoft 365 Copilot interface with the order confirmation.

  8. 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.

    Screenshot of the Microsoft 365 Copilot interface with the order response.

  9. Go back to Visual Studio Code and stop debugging.

  10. Switch to the Terminal tab and close all active terminals.

    Screenshot of the Visual Studio Code terminal tab with the option to close all terminals.