Get insight about your data from an .NET Azure AI chat app

Get started with Semantic Kernel and the gpt-35-turbo model, from a simple .NET 8.0 console application. Use the AI model to get analytics and information about your previous hikes. It consists of a simple console application, running locally, that will read the file hikes.md and send request to an Azure OpenAI service deployed in your Azure subscription and provide the result in the console. Follow these steps to provision Azure OpenAI and learn how to use Semantic Kernel.

Get started with the .NET Azure OpenAI with a gpt-35-turbo model, from a simple .NET 8.0 console application. Use the AI model to get analytics and information about your previous hikes. It consists of a simple console application, running locally, that will read the file hikes.md and send request to an Azure OpenAI service deployed in your Azure subscription and provide the result in the console. Follow these steps to provision Azure OpenAI and learn how to use the .NET Azure OpenAI SDK.

Prerequisites

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.

  1. Clone the repository: dotnet/ai-samples

  2. From a terminal or command prompt, navigate to the quickstarts directory.

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

Try "Chatting About My Previous Hikes" sample

  1. From a terminal or command prompt, navigate to the semantic-kernel\03-ChattingAboutMyHikes directory.
  1. From a terminal or command prompt, navigate to the azure-openai-sdk\03-ChattingAboutMyHikes directory.
  1. 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.

Explore 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 client is created, we read the content of the file hikes.md and use it to 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.

// Provide context for the AI model
ChatHistory chatHistory = new($"""
    You are upbeat and friendly. You introduce yourself when first saying hello. 
    Provide a short answer only based on the user hiking records below:  

    {File.ReadAllText("hikes.md")}
    """);
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");

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.

// Start the conversation
chatHistory.AddUserMessage("Hi!");
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");

chatHistory.Add(await service.GetChatMessageContentAsync(chatHistory, new OpenAIPromptExecutionSettings() { MaxTokens = 400 }));
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");

To maintain the chat history or context, make sure you add the response from the model to the chatHistory. It's time to make our user request about our data again using the AddUserMessage and GetChatMessageContentAsync function.

// Continue the conversation with a question.
chatHistory.AddUserMessage("I would like to know the ratio of the hikes I've done in Canada compared to other countries.");
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");

chatHistory.Add(await service.GetChatMessageContentAsync(chatHistory, new OpenAIPromptExecutionSettings() { MaxTokens = 400 }));
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last().Content}");

Customize the system prompt and change the request, asking for different questions (ex: How many times did you hiked when it was raining? How many times did you hiked in 2021? etc.) to see how the model responds.

Explore 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 read the content of the file hikes.md and use it to 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 upbeat and friendly. You introduce yourself when first saying hello. 
Provide a short answer only based on the user hiking records below:  

""" + markdown;

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! 
""";

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. It's time to make our user request about our data again using the ChatRequestUserMessage and GetChatCompletionsAsync function.

var hikeRequest = 
"""
I would like to know the ration of hike I did in Canada compare to hikes done in other countries.
""";

Console.WriteLine($"\n\nUser >>> {hikeRequest}");
completionOptions.Messages.Add(new ChatRequestUserMessage(hikeRequest));
response = await openAIClient.GetChatCompletionsAsync(completionOptions);

Customize the system prompt and change the request, asking for different questions (ex: How many times did you hiked when it was raining? How many times did you hiked in 2021? etc.) to see how the model responds.

Clean up resources

When you no longer need the sample application or resources, remove the corresponding deployment and all resources.

azd down

Next steps