Skip to main content Link Menu Expand (external link) Document Search Copy Copied

Task 02 - Create a Semantic Kernel Agent (40 minutes)

Introduction

Agents are software-based entities that leverage AI models to perform work. They are built to perform a wide range of tasks and are called different names, such as chatbot or copilot, based on their jobs. You can provide agents with a persona or “meta prompt” to influence how they respond to inputs, allowing you to direct how your agent plans tasks, generates responses, and interacts with users.

Description

In this task, you will create a copilot agent to help customer service agents create new maintenance requests based on issues mentioned during customer calls. The agent will use plugin functions to perform work. You will give it a persona to help guide the agent in its interactions with customer service agents. You will provide the functionality to allow the agent to generate a new maintenance request based on a natural language request from the call center agent.

Success Criteria

  • You have created a Semantic Kernel agent.
  • You have enabled the /MaintenanceCopilotChat endpoint to the API, which sends user messages to the agent and returns the agent’s response.
  • You have configured the maintenance request and database plugins for your agent to use when generating new maintenance requests and retrieving hotel information.
  • You have provided your agent with a prompt to define its persona.
  • You have created a conversation app using the Copilot Chat page in the Streamlit Dashboard.
  • You can engage the agent via the Steamlit dashboard and, through a natural language conversation, have it create and save a new maintenance request.

Learning Resources

Key Tasks

01: Configure a maintenance copilot agent

Configure an agent named MaintenanceCopilot that can be called through the ContosoSuites API by completing TODOs 1-4 defined in the \src\ContosoSuitesWebAPI\Agents\MaintenanceCopilot.cs file.

Expand this section to view the solution

The structure for the MaintenaceCopilot agent can be found in the src\ConsotoSuitesWebAPI project, but it must be configured before it will function correctly:

  1. Navigate to the web API project in Visual Studio Code and open the Agents\MaintenanceCopilot.cs file.
  2. Complete Exercise 5 Task 2 TODO #1 by adding the following using statements at the top of the file:

     using Microsoft.SemanticKernel;
     using Microsoft.SemanticKernel.ChatCompletion;
     using Microsoft.SemanticKernel.Connectors.OpenAI;
    
  3. Complete Exercise 5 Task 2 TODO #2 by updating the class definition to inject a Kernel service into the primary constructor.

     public class MaintenanceCopilot(Kernel kernel)
    
  4. Complete Exercise 5 Task 2 TODO #3 by uncommenting the class-level variable definitions and providing the _kernel and _history variables for use within the agent.
  5. Complete Exercise 5 Task 2 TODO #4 by commenting out the throw new NotImplementedException() line and uncommenting the remaining code in the function. The lines you are uncommenting allow the Azure OpenAI chat completion service to auto-invoke Kernel functions defined in the database and maintenance request plugins. It also adds the incoming user message to the chat history and inserts the agent’s response once it replies.

02: Configure maintenance request and database plugins

Allow the agent to plan steps using plugin functions by configuring the maintenance request and database plugins.

The maintenance request plugin provides kernel functions capable of creating a new maintenance request and saving it to the MaintenanceRequests container in Azure Cosmos DB.

The database plugin (in the DatabaseService) provides a kernel function for retrieving hotel information from the Azure SQL database.

Expand this section to view the solution

To configure the maintenance request and database plugins:

  1. In Visual Studio Code, open the Plugins\MaintenanceRequestPlugin.cs file in the ContosoSuitesWebAPI project.
  2. Complete Exercise 5 Task 2 TODO #5 by referencing the Microsoft.SemanticKernel library at the top of the file, along with the other using statements.
  3. Complete Exercise 5 Task 2 TODO #s 6 and 8 by adding the Kernel function and Description descriptors of the CreateMaintenanceRequest and SaveMaintenanceRequest functions. The descriptors for each should look like:
    1. For the CreateMaintenanceRequest function:

      [KernelFunction("create_maintenance_request")]
      [Description("Creates a new maintenance request for a hotel.")]
      
    2. For the SaveMaintenanceRequest function:

      [KernelFunction("save_maintenance_request")]
      [Description("Saves a maintenance request to the database for a hotel.")]
      
  4. Complete Exercise 5 Task 2 TODO #s 7 and 9 by adding a Kernel parameter to the beginning of both function method declarations.
    1. For the CreateMaintenanceRequest function, add Kernel kernel as the first parameter:

      public async Task<MaintenanceRequest> CreateMaintenanceRequest(Kernel kernel, int HotelId, string Hotel, string Details, int? RoomNumber,string? location)
      
    2. For the SaveMaintenanceRequest function, add Kernel kernel as the first parameter:

      public async Task SaveMaintenanceRequest(Kernel kernel, MaintenanceRequest maintenanceRequest)
      
  5. Save the MaintenanceRequestPlugin file.
  6. Once you’ve defined your plugin, you must add it to your kernel by creating a new instance of the plugin and adding it to the kernel’s plugin collection.
    1. Open the Program.cs file in the ContosoSuitesWebAPI project.
    2. In the Kernel singleton service builder, add the following code to define a plugin from the MaintenanceRequestPlugin type. This can be added directly below the plugin definition for the DatabaseService.

      kernelBuilder.Plugins.AddFromType<MaintenanceRequestPlugin>("MaintenanceCopilot");
      
    3. Because the MaintenanceRequestPlugin uses dependency injection to provide a CosmosClient, you must add a singleton instance of the that client within your Kernel service definition. Do that by adding the following code just below the plugin line you just added:

        kernelBuilder.Services.AddSingleton<CosmosClient>((_) =>
            {
                string userAssignedClientId = builder.Configuration["AZURE_CLIENT_ID"]!;
                var credential = new DefaultAzureCredential(
                    new DefaultAzureCredentialOptions
                    {
                        ManagedIdentityClientId = userAssignedClientId
                    });
                CosmosClient client = new(
                    accountEndpoint: builder.Configuration["CosmosDB:AccountEndpoint"]!,
                    tokenCredential: credential
                );
                return client;
            });
            return kernelBuilder.Build();
        });
      

      This code is a bit redundant with the CosmosClient code earlier in the Program.cs file. However, the service must be included within the Kernel service definition, or it will not be accessible to the plugin.

    4. The complete builder.Service.AddSingleton<Kernel> method should now look like the following:

        builder.Services.AddSingleton<Kernel>((_) =>
        {
        IKernelBuilder kernelBuilder = Kernel.CreateBuilder();
        kernelBuilder.AddAzureOpenAIChatCompletion(
            deploymentName: builder.Configuration["AzureOpenAI:DeploymentName"]!,
            endpoint: builder.Configuration["AzureOpenAI:Endpoint"]!,
            apiKey: builder.Configuration["AzureOpenAI:ApiKey"]!
        );
        var databaseService = _.GetRequiredService<IDatabaseService>();
        kernelBuilder.Plugins.AddFromObject(databaseService);
      
        #pragma warning disable SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
            kernelBuilder.AddAzureOpenAITextEmbeddingGeneration(
                deploymentName: builder.Configuration["AzureOpenAI:EmbeddingDeploymentName"]!,
                endpoint: builder.Configuration["AzureOpenAI:Endpoint"]!,
                apiKey: builder.Configuration["AzureOpenAI:ApiKey"]!
            );
        #pragma warning restore SKEXP0010 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
      
        kernelBuilder.Plugins.AddFromType<MaintenanceRequestPlugin>("MaintenanceCopilot");
        kernelBuilder.Services.AddSingleton<CosmosClient>((_) =>
            {
                string userAssignedClientId = builder.Configuration["AZURE_CLIENT_ID"]!;
                var credential = new DefaultAzureCredential(
                    new DefaultAzureCredentialOptions
                    {
                        ManagedIdentityClientId = userAssignedClientId
                    });
                CosmosClient client = new(
                    accountEndpoint: builder.Configuration["CosmosDB:AccountEndpoint"]!,
                    tokenCredential: credential
                );
                return client;
            });
            return kernelBuilder.Build();
        });
      

The DatabaseService plugin was already defined in a previous exercise, so just a small change needs to be completed to allow your agent to use it.

  1. Open the Services\DatabaseService.cs file to update the database plugin and make it usable by your agent.
  2. Update the [KernelFunction] descriptor for the GetHotels() method and add the name “get_hotels” to the descriptor. The descriptor should now look like:

     [KernelFunction("get_hotels")]
    

03: Enable the maintenance copilot chat endpoint

Enable the /MaintenanceCopilotChat endpoint on the API.

Expand this section to view the solution

To enable the /MaintenanceCopilotChat endpoint on the API:

  1. In the Program.cs file in the src\ContosoSuitesWebAPI project and locate the app.MapPost("/MaintenanceCopilotChat", async ([FromBody]string message, [FromServices] MaintenanceCopilot copilot) API endpoint definition.
  2. Complete Exercise 5 Task 2 TODO #10 by calling the Chat method of the MaintenanceCopilot, passing in the user message from the request body. Return the response from the Chat method. The code in the API method should look like:

     var response = await copilot.Chat(message);
     return response;
    

Make sure to remove the throw new NotImplementedException(); line, or you will get an error when that line is hit.

04: Update the dashboard

Configure the Copilot Chat page as a conversational app in the Streamlit dashboard. Ensure calls to the MaintenanceCopilotChat API endpoint have a long enough timeout, as some database calls might take longer than the default of 10 seconds.

Expand this section to view the solution
  1. To configure the Copilot Chat page as a conversational app in the Streamlit dashboard, complete Exercise 5 Task 2 TODO #11 by setting response equal to the response from a POST request to the Copilot endpoint. The timeout should be set to 60 seconds or longer. The call should look like:

     response = requests.post(f"{api_endpoint}/MaintenanceCopilotChat", json=message, timeout=60)
    
  2. To define a conversational interface with the copilot, update the “How can I help you today?” prompt if statement with the following code:

     # React to user input
         if prompt := st.chat_input("How I can help you today?"):
             with st.spinner("Awaiting the Copilot's response to your question..."):
                 # Display user message in chat message container
                 st.chat_message("user").markdown(prompt)
                 # Add user message to chat history
                 st.session_state.chat_messages.append({"role": "user", "content": prompt})
                 # Send user message to Copilot and get response
                 response = send_message_to_copilot(prompt)
                 # Display assistant response in chat message container
                 with st.chat_message("assistant"):
                     st.markdown(response)
                 # Add assistant response to chat history
                 st.session_state.chat_messages.append({"role": "assistant", "content": response})
    
  3. Save the file.

05: Test the agent

  1. Use the Copilot Chat page to interact with your agent. Follow the prompts to have it generate a new maintenance request and save it to Azure Cosmos DB.
  2. Test the agent via the Streamlit Copilot Chat page using the following conversational prompts:
    1. First, send this message about a customer-reported issue: “A customer is reporting an issue in room 205 at the Grand Regency. The microwave is not working.”
    2. Next, ask, “Did it get saved to the database?”
    3. Now, ask, “Did you do that before I asked, or after?”
Expand this section to view the solution

To test the agent via the Streamlit Copilot Chat page:

  1. Run the API locally by opening a new terminal window in Visual Studio code, navigating to the src\ContosoSuitesWebAPI directory, and starting the API using the following command:

     dotnet run
    
  2. Open another terminal window, navigate to the src\ContosoSuitesDashboard directory, and run the following command to start the Streamlit dashboard:

     python -m streamlit run Index.py
    
  3. Navigate to the Copilot Chat page using the left-hand menu, then submit the following conversational prompts:
    1. First, send in this message about a customer-reported issue: “A customer is reporting an issue in room 205 at the Grand Regency. The microwave is not working.” You should get a response that a maintenance request was created and will be addressed shortly.
    2. Next, ask, “Did it get saved to the database?” It will reply that, yes, the request has been saved.
    3. Now, ask, “Did you do that before I asked, or after?” You should get a reply that it was saved after you asked.

    The above behavior results from the agent not having explicit instructions on how it should behave or the steps it should take during the process. It simply calls the create_maintenance_request Kernel function and assumes it is done. It is unaware that the request should also be saved to the database. To fix this, you can provide the agent with a persona.

  4. You can verify the new request was saved by navigating to the Vector Search page and entering a search query of “microwave not working at Grand Regency.” Accept the default values for max results and minimum similarity score. Your newly saved record should be the first result in the list.

06: Create a persona and test again

  1. Improve the agent’s behavior by providing it with a persona. By giving instructions on how to plan the creation of a request and saving it to the database, you can guide how the agent interacts with users. The prompt you provide should ensure it has all the required information from the customer service agent and that it requests permission before performing any consequential actions, such as saving the request to the database. It should also inform users the request has been saved and that hotel maintenance will address the issue as soon as possible.
  2. Test the agent via the Copilot Chat page in the dashboard, noting the differences in responses with the persona provided. Use the following conversational prompts to interact with the agent.
    1. Enter, “Can you help me create a maintenance request?”
      1. Notice that the agent is now prompting you to provide additional details, such as the hotel, room number, and a description of the issue.
    2. Next, send the following message: “The customer mentioned they were at the regency. Can you provide me with the exact name of that hotel?”
      1. This request performs a lookup against the Azure SQL database using the DatabaseService plugin.
    3. Then, send “Ok, that customer mentioned that they are in the penthouse suite, room 700, and that the television is no longer working.”
      1. At this point, the agent should provide the details of the maintenance request it will create and ask if it is okay to proceed with creating and saving the request.
    4. Send, “Yes, that looks correct. Please save the request to the database.”
      1. You should get a response: “The maintenance request has been saved to the database. Hotel maintenance has been notified and will address the issue in the Penthouse Suite, room 700, at the Grand Regency as soon as possible.”
Expand this section to view the solution

To give your agent a persona and provide it with instructions so it behaves more consistently:

  1. Open the MaintenanceCopilot.cs file in the src\ContosoSuitesWebAPI project and update the class-level variable defining the ChatHistory to pass the following string into the ChatHistory() object during variable declaration.

     """
     You are a friendly assistant who likes to follow the rules. You will complete required steps
     and request approval before taking any consequential actions, such as saving the request to the database.
     If the user doesn't provide enough information for you to complete a task, you will keep asking questions
     until you have enough information to complete the task. Once the request has been saved to the database,
     inform the user that hotel maintenance has been notified and will address the issue as soon as possible.
     """
    
  2. The final definition for _history should look like:

     private ChatHistory _history = new ("""
         You are a friendly assistant who likes to follow the rules. You will complete required steps
         and request approval before taking any consequential actions, such as saving the request to the database.
         If the user doesn't provide enough information for you to complete a task, you will keep asking questions
         until you have enough information to complete the task. Once the request has been saved to the database,
         inform the user that hotel maintenance has been notified and will address the issue as soon as possible.
         """);
    
  3. Stop and restart the API project in the terminal window running the API.

To test the updated agent and evaluate how its responses differ with a persona assigned:

  1. Return to the running Streamlit dashboard and the Copilot Chat page.
  2. Use the following conversational prompts to interact with the agent.
    1. Enter, “Can you help me create a maintenance request?”
      1. Notice that the agent is now prompting you to provide additional details, such as the hotel, room number, and a description of the issue.
    2. Next, send the following message: “The customer mentioned they were at the regency. Can you provide me with the exact name of that hotel?”
      1. This request performs a lookup against the Azure SQL database using the DatabaseService plugin.
    3. Then, send “Ok, that customer mentioned that they are in the penthouse suite, room 700, and that the television is no longer working.”
      1. At this point, the agent should provide the details of the maintenance request it will create and ask if it is okay to proceed with creating and saving the request.
    4. Send, “Yes, that looks correct. Please save the request to the database.”
    5. You should get a response similar to the following: “The maintenance request has been saved to the database. Hotel maintenance has been notified and will address the issue in the Penthouse Suite, room 700, at the Grand Regency as soon as possible.”