Exercise - Implement the Azure OpenAI service
Let's start with the simplest service, OpenAiService
. This service only contains two methods that we need to implement so we can implement basic prompts and completions right away. We're not implementing our Azure Cosmos DB for NoSQL data service until later, so we can't persist our sessions across debugging sessions yet.
In this exercise, we have a few key requirements:
- Send a question from the user to the AI assistant and ask for a response.
- Send a series of prompts to the AI assistant and ask for a summarization of the conversation.
Ask the AI model a question
First, implement a question-answer conversation by sending a system prompt, a question, and session ID so the AI model can provide an answer in the context of the current conversation. Make sure you measure the number of tokens it takes to parse the prompt and return a response (or completion in this context).
Open the Services/OpenAiService.cs file.
Within the
GetChatCompletionAsync
method, remove any existing placeholder code.public async Task<(string completionText, int completionTokens)> GetChatCompletionAsync(string sessionId, string userPrompt) { }
Create a
ChatRequestSystemMessage
variable namedsystemMessage
. For this variable, use theUser
role and the_systemPrompt
variable for content.ChatRequestSystemMessage systemMessage = new(_systemPrompt);
Create a
ChatRequestUserMessage
variable nameduserMessage
. For this variable, the role should beChatRole.User
and use theuserPrompt
constructor parameter for the message's content.ChatRequestUserMessage userMessage = new(userPrompt);
Create a new variable named
options
of typeChatCompletionsOptions
. Add the two message variables to theMessages
list, set the value ofUser
to thesessionId
constructor parameter, setMaxTokens
to4000
, and set the remaining properties to the recommended values here.ChatCompletionsOptions options = new() { DeploymentName = _modelName, Messages = { systemMessage, userMessage }, User = sessionId, MaxTokens = 4000, Temperature = 0.3f, NucleusSamplingFactor = 0.5f, FrequencyPenalty = 0, PresencePenalty = 0 };
Tip
4096 is the maximum number of tokens for the gpt-35-turbo model. We're just rounding down here to simplify things.
Asynchronously invoke the
GetChatCompletionsAsync
method of the Azure OpenAI client variable (_client
). Pass in theoptions
variable you created. Store the result in a variable namedcompletions
of typeChatCompletions
.ChatCompletions completions = await _client.GetChatCompletionsAsync(options);
Tip
The
GetChatCompletionsAsync
method returns an object of typeTask<Response<ChatCompletions>>
. TheResponse<T>
class contains a implicit conversion to typeT
allowing you to select a type based on your application's needs. You can store the result as eitherResponse<ChatCompletions>
to get the full metadata from the response or justChatCompletions
if you only care about the content of the result itself.Finally, return a tuple as the result of the
GetChatCompletionAsync
method with the content of the completion as a string, the number of tokens associated with the prompt, and the number of tokens for the response.return ( response: completions.Choices[0].Message.Content, promptTokens: completions.Usage.PromptTokens, responseTokens: completions.Usage.CompletionTokens );
Save the Services/OpenAiService.cs file.
Ask the AI model to summarize a conversation
Now, send the AI model a different system prompt, your current conversation, and session ID so the AI model can summarize the conversation in a couple of words.
Within the
SummarizeAsync
method, remove any existing placeholder code.public async Task<string> SummarizeAsync(string sessionId, string conversationText) { }
Create a
ChatRequestSystemMessage
variable namedsystemMessage
. For this variable, use theUser
role and the_summarizePrompt
variable for content.ChatRequestSystemMessage systemMessage = new(_summarizePrompt);
Create another
ChatRequestUserMessage
variable nameduserMessage
. Use theUser
role again and use theconversationText
constructor parameter for the message's content.ChatRequestUserMessage userMessage = new(conversationText);
Create a
ChatCompletionsOptions
variable namedoptions
with the two message variables in theMessages
list,User
set to thesessionId
constructor parameter,MaxTokens
set to200
, and the remaining properties to the recommended values here.ChatCompletionsOptions options = new() { DeploymentName = _modelName, Messages = { systemMessage, userMessage }, User = sessionId, MaxTokens = 200, Temperature = 0.0f, NucleusSamplingFactor = 1.0f, FrequencyPenalty = 0, PresencePenalty = 0 };
Invoke
_client.GetChatCompletionsAsync
asynchronously using theoptions
variable as a parameter. Store the result in a variable namedcompletions
of typeChatCompletions
.ChatCompletions completions = await _client.GetChatCompletionsAsync(options);
Return the content of the completion as a string as the result of the
SummarizeAsync
method.return completions.Choices[0].Message.Content;
Save the Services/OpenAiService.cs file.
Check your work
At this point, your application should have a thorough enough implementation of the Azure OpenAI service that you can test the application. Remember, you don't have a data store implementation yet, so your conversations aren't persisted between debugging sessions.
Open a new terminal.
Start the application with hot reloads enabled using
dotnet watch
.dotnet watch run --non-interactive
Tip
The Hot Reload feature is enabled here if you need to make a small correction to the application's code. For more information, see .NET Hot Reload support for ASP.NET Core.
Visual Studio Code launches the in-tool simple browser again with the web application running. In the web application, create a new chat session and ask the AI assistant a question. The AI assistant now responds with a completion created by the model. You should also notice that the token UI fields are now populated with real-world token usage for each completion and prompt.
Close the terminal.