Build an Azure AI chat app with .NET
Get started with Semantic Kernel by creating a simple .NET 8 console chat application. The application will run locally and use the OpenAI gpt-35-turbo
model deployed into an Azure OpenAI account. Follow these steps to provision Azure OpenAI and learn how to use Semantic Kernel.
Get started with the .NET Azure OpenAI SDK by creating a simple .NET 8 console chat application. The application will run locally and use the OpenAI gpt-35-turbo
model deployed into an Azure OpenAI account. Follow these steps to provision Azure OpenAI and learn how to use the .NET Azure OpenAI SDK.
Prerequisites
- .NET 8.0 SDK - Install the .NET 8.0 SDK
- An Azure subscription - Create one for free
- Azure Developer CLI - Install or update the Azure Developer CLI
- Access to Azure OpenAI service.
- On Windows, PowerShell
v7+
is required. To validate your version, runpwsh
in a terminal. It should returns the current version. If it returns an error, execute the following command:dotnet tool update --global PowerShell
.
Deploy the Azure resources
Ensure that you follow the Prerequisites to have access to Azure OpenAI Service as well as the Azure Developer CLI, and then follow the following guide to set started with the sample application.
Clone the repository: dotnet/ai-samples
From a terminal or command prompt, navigate to the quickstarts directory.
This provisions the Azure OpenAI resources. It may take several minutes to create the Azure OpenAI service and deploy the model.
azd up
Note
If you already have an Azure OpenAI service available, you can skip the deployment and use that value in the Program.cs, preferably from an IConfiguration
.
Troubleshoot
On Windows, you might get the following error messages after running azd up
:
postprovision.ps1 is not digitally signed. The script will not execute on the system
The script postprovision.ps1 is executed to set the .NET user secrets used in the application. To avoid this error, run the following PowerShell command:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Then re-run the azd up
command.
Another possible error:
'pwsh' is not recognized as an internal or external command, operable program or batch file. WARNING: 'postprovision' hook failed with exit code: '1', Path: '.\infra\post-script\postprovision.ps1'. : exit code: 1 Execution will continue since ContinueOnError has been set to true.
The script postprovision.ps1 is executed to set the .NET user secrets used in the application. To avoid this error, manually run the script using the following PowerShell command:
.\infra\post-script\postprovision.ps1
The .NET AI apps now have the user-secrets configured and they can be tested.
Trying HikerAI sample
- From a terminal or command prompt, navigate to the
semantic-kernel\02-HikerAI
directory.
- From a terminal or command prompt, navigate to the
azure-openai-sdk\02-HikerAI
directory.
It's now time to try the console application. Type in the following to run the app:
dotnet run
If you get an error message the Azure OpenAI resources may not have finished deploying. Wait a couple of minutes and try again.
Understanding the code
Our application uses the Microsoft.SemanticKernel
package, which is available on NuGet, to send and receive requests to an Azure OpenAI service deployed in Azure.
The entire application is contained within the Program.cs file. The first several lines of code loads up secrets and configuration values that were set in the dotnet user-secrets
for you during the application provisioning.
// == Retrieve the local secrets saved during the Azure deployment ==========
var config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
string endpoint = config["AZURE_OPENAI_ENDPOINT"];
string deployment = config["AZURE_OPENAI_GPT_NAME"];
string key = config["AZURE_OPENAI_KEY"];
The AzureOpenAIChatCompletionService
service facilitates the requests and responses.
// == Create the Azure OpenAI Chat Completion Service ==========
AzureOpenAIChatCompletionService service = new(deployment, endpoint, key);
Once the AzureOpenAIChatCompletionService
service is created, we provide more context to the model by adding a system prompt. This instructs the model how you'd like it to act during the conversation.
// Start the conversation with context for the AI model
ChatHistory chatHistory = new("""
You are a hiking enthusiast who helps people discover fun hikes in their area. You are upbeat and friendly.
You introduce yourself when first saying hello. When helping people out, you always ask them
for this information to inform the hiking recommendation you provide:
1. Where they are located
2. What hiking intensity they are looking for
You will then provide three suggestions for nearby hikes that vary in length after you get that information.
You will also share an interesting fact about the local nature on the hikes when making a recommendation.
""");
Then you can add a user message to the model by using the AddUserMessage
function.
To have the model generate a response based off the system prompt and the user request, use the GetChatMessageContentAsync
function.
// Add user message to chat history
chatHistory.AddUserMessage("Hi! Apparently you can help me find a hike that I will like?");
// Print User Message to console
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");
// Get response
var response = await service.GetChatMessageContentAsync(chatHistory, new OpenAIPromptExecutionSettings() { MaxTokens = 400 });
To maintain the chat history, make sure you add the response from the model.
// Add response to chat history
chatHistory.Add(response);
// Print Response to console
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");
Customize the system prompt and user message to see how the model responds to help you find a hike that you'll like.
Understanding the code
Our application uses the Azure.AI.OpenAI
client SDK, which is available on NuGet, to send and receive requests to an Azure OpenAI service deployed in Azure.
The entire application is contained within the Program.cs file. The first several lines of code loads up secrets and configuration values that were set in the dotnet user-secrets
for you during the application provisioning.
// == Retrieve the local secrets saved during the Azure deployment ==========
var config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
string openAIEndpoint = config["AZURE_OPENAI_ENDPOINT"];
string openAIDeploymentName = config["AZURE_OPENAI_GPT_NAME"];
string openAiKey = config["AZURE_OPENAI_KEY"];
// == Creating the AIClient ==========
var endpoint = new Uri(openAIEndpoint);
var credentials = new AzureKeyCredential(openAiKey);
The OpenAIClient
class facilitates the requests and responses. ChatCompletionOptions
specifies parameters of how the model will respond.
var openAIClient = new OpenAIClient(endpoint, credentials);
var completionOptions = new ChatCompletionsOptions
{
MaxTokens = 400,
Temperature = 1f,
FrequencyPenalty = 0.0f,
PresencePenalty = 0.0f,
NucleusSamplingFactor = 0.95f, // Top P
DeploymentName = openAIDeploymentName
};
Once the OpenAIClient
client is created, we provide more context to the model by adding a system prompt. This instructs the model how you'd like it to act during the conversation.
var systemPrompt =
"""
You are a hiking enthusiast who helps people discover fun hikes in their area. You are upbeat and friendly.
You introduce yourself when first saying hello. When helping people out, you always ask them
for this information to inform the hiking recommendation you provide:
1. Where they are located
2. What hiking intensity they are looking for
You will then provide three suggestions for nearby hikes that vary in length after you get that information.
You will also share an interesting fact about the local nature on the hikes when making a recommendation.
""";
completionOptions.Messages.Add(new ChatRequestSystemMessage(systemPrompt));
Then you can add a user message to the model by using the ChatRequestUserMessage
class.
To have the model generate a response based off the system prompt and the user request, use the GetChatCompletionsAsync
function.
string userGreeting = """
Hi!
Apparently you can help me find a hike that I will like?
""";
completionOptions.Messages.Add(new ChatRequestUserMessage(userGreeting));
Console.WriteLine($"\n\nUser >>> {userGreeting}");
ChatCompletions response = await openAIClient.GetChatCompletionsAsync(completionOptions);
ChatResponseMessage assistantResponse = response.Choices[0].Message;
Console.WriteLine($"\n\nAI >>> {assistantResponse.Content}");
completionOptions.Messages.Add(new ChatRequestAssisstantMessage(assistantResponse.Content));
To maintain the chat history or context, make sure you add the response from the model as a ChatRequestAssistantMessage
.
Customize the system prompt and user message to see how the model responds to help you find a hike that you'll like.
Clean up resources
When you no longer need the sample application or resources, remove the corresponding deployment and all resources.
azd down
Next steps
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for