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
- What are agents?
- What is a Plugin?
- Personas: Giving your Agent a Role
- What is a Planner?
- Introduction to Semantic Kernel
- Understanding the kernel
- Semantic Kernel GitHub repo
- Build conversational apps with Streamlit
- st.chat_input
- st.chat_message
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:
- Navigate to the web API project in Visual Studio Code and open the
Agents\MaintenanceCopilot.cs
file. -
Complete
Exercise 5 Task 2 TODO #1
by adding the followingusing
statements at the top of the file:using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI;
-
Complete
Exercise 5 Task 2 TODO #2
by updating the class definition to inject aKernel
service into the primary constructor.public class MaintenanceCopilot(Kernel kernel)
- 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. - Complete
Exercise 5 Task 2 TODO #4
by commenting out thethrow 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:
- In Visual Studio Code, open the
Plugins\MaintenanceRequestPlugin.cs
file in theContosoSuitesWebAPI
project. - Complete
Exercise 5 Task 2 TODO #5
by referencing theMicrosoft.SemanticKernel
library at the top of the file, along with the otherusing
statements. - Complete
Exercise 5 Task 2 TODO #s 6 and 8
by adding the Kernel function and Description descriptors of theCreateMaintenanceRequest
andSaveMaintenanceRequest
functions. The descriptors for each should look like:-
For the
CreateMaintenanceRequest
function:[KernelFunction("create_maintenance_request")] [Description("Creates a new maintenance request for a hotel.")]
-
For the
SaveMaintenanceRequest
function:[KernelFunction("save_maintenance_request")] [Description("Saves a maintenance request to the database for a hotel.")]
-
- Complete
Exercise 5 Task 2 TODO #s 7 and 9
by adding aKernel
parameter to the beginning of both function method declarations.-
For the
CreateMaintenanceRequest
function, addKernel kernel
as the first parameter:public async Task<MaintenanceRequest> CreateMaintenanceRequest(Kernel kernel, int HotelId, string Hotel, string Details, int? RoomNumber,string? location)
-
For the
SaveMaintenanceRequest
function, addKernel kernel
as the first parameter:public async Task SaveMaintenanceRequest(Kernel kernel, MaintenanceRequest maintenanceRequest)
-
- Save the
MaintenanceRequestPlugin
file. - 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.
- Open the
Program.cs
file in theContosoSuitesWebAPI
project. -
In the
Kernel
singleton servicebuilder
, add the following code to define a plugin from theMaintenanceRequestPlugin
type. This can be added directly below the plugin definition for theDatabaseService
.kernelBuilder.Plugins.AddFromType<MaintenanceRequestPlugin>("MaintenanceCopilot");
-
Because the
MaintenanceRequestPlugin
uses dependency injection to provide aCosmosClient
, you must add a singleton instance of the that client within yourKernel
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 theProgram.cs
file. However, the service must be included within theKernel
service definition, or it will not be accessible to the plugin. -
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(); });
- Open the
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.
- Open the
Services\DatabaseService.cs
file to update the database plugin and make it usable by your agent. -
Update the
[KernelFunction]
descriptor for theGetHotels()
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:
- In the
Program.cs
file in thesrc\ContosoSuitesWebAPI
project and locate theapp.MapPost("/MaintenanceCopilotChat", async ([FromBody]string message, [FromServices] MaintenanceCopilot copilot)
API endpoint definition. -
Complete
Exercise 5 Task 2 TODO #10
by calling theChat
method of theMaintenanceCopilot
, passing in the user message from the request body. Return the response from theChat
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
-
To configure the Copilot Chat page as a conversational app in the Streamlit dashboard, complete
Exercise 5 Task 2 TODO #11
by settingresponse
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)
-
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})
-
Save the file.
05: Test the agent
- 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.
- Test the agent via the Streamlit Copilot Chat page using the following conversational prompts:
- 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.”
- Next, ask, “Did it get saved to the database?”
- 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:
-
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
-
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
- Navigate to the Copilot Chat page using the left-hand menu, then submit the following conversational prompts:
- 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.
- Next, ask, “Did it get saved to the database?” It will reply that, yes, the request has been saved.
- 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. - 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
- 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.
- 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.
- Enter, “Can you help me create a maintenance request?”
- Notice that the agent is now prompting you to provide additional details, such as the hotel, room number, and a description of the issue.
- Next, send the following message: “The customer mentioned they were at the regency. Can you provide me with the exact name of that hotel?”
- This request performs a lookup against the Azure SQL database using the
DatabaseService
plugin.
- This request performs a lookup against the Azure SQL database using the
- Then, send “Ok, that customer mentioned that they are in the penthouse suite, room 700, and that the television is no longer working.”
- 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.
- Send, “Yes, that looks correct. Please save the request to the database.”
- 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.”
- Enter, “Can you help me create a maintenance request?”
Expand this section to view the solution
To give your agent a persona and provide it with instructions so it behaves more consistently:
-
Open the
MaintenanceCopilot.cs
file in thesrc\ContosoSuitesWebAPI
project and update the class-level variable defining theChatHistory
to pass the following string into theChatHistory()
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. """
-
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. """);
-
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:
- Return to the running Streamlit dashboard and the Copilot Chat page.
- Use the following conversational prompts to interact with the agent.
- Enter, “Can you help me create a maintenance request?”
- Notice that the agent is now prompting you to provide additional details, such as the hotel, room number, and a description of the issue.
- Next, send the following message: “The customer mentioned they were at the regency. Can you provide me with the exact name of that hotel?”
- This request performs a lookup against the Azure SQL database using the
DatabaseService
plugin.
- This request performs a lookup against the Azure SQL database using the
- Then, send “Ok, that customer mentioned that they are in the penthouse suite, room 700, and that the television is no longer working.”
- 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.
- Send, “Yes, that looks correct. Please save the request to the database.”
- 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.”
- Enter, “Can you help me create a maintenance request?”