|
| 1 | +--- |
| 2 | +author: jefmarti |
| 3 | +ms.service: app-service |
| 4 | +ms.devlang: dotnet |
| 5 | +ms.topic: article |
| 6 | +ms.date: 04/10/2024 |
| 7 | +ms.author: jefmarti |
| 8 | +--- |
| 9 | + |
| 10 | +You can use Azure App Service to work with popular AI frameworks like LangChain and Semantic Kernel connected to OpenAI for creating intelligent apps. In the following tutorial, we are adding an Azure OpenAI service using Semantic Kernel to a .NET 8 Blazor web application. |
| 11 | + |
| 12 | +#### Prerequisites |
| 13 | + |
| 14 | +- An [Azure OpenAI resource](https://learn.microsoft.com/azure/ai-services/openai/quickstart?pivots=programming-language-csharp&tabs=command-line%2Cpython#set-up) or an [OpenAI account](https://platform.openai.com/overview). |
| 15 | +- A .NET 8 Blazor Web App. Create the application with a template [here](https://dotnet.microsoft.com/learn/aspnet/blazor-tutorial/intro). |
| 16 | + |
| 17 | +### Setup Blazor web app |
| 18 | + |
| 19 | +For this Blazor web application, we are building off the Blazor [template](https://dotnet.microsoft.com/learn/aspnet/blazor-tutorial/intro) and creating a new razor page that can send and receive requests to an Azure OpenAI OR OpenAI service using Semantic Kernel. |
| 20 | + |
| 21 | +1. Right click on the **Pages** folder found under the **Components** folder and add a new item named *OpenAI.razor* |
| 22 | +2. Add the following code to the **OpenAI.razor* file and click **Save** |
| 23 | + |
| 24 | +```csharp |
| 25 | +@page "/openai" |
| 26 | +@rendermode InteractiveServer |
| 27 | + |
| 28 | +<PageTitle>OpenAI</PageTitle> |
| 29 | + |
| 30 | +<h3>OpenAI Query</h3> |
| 31 | + |
| 32 | +<input placeholder="Input query" @bind="newQuery" /> |
| 33 | +<button class="btn btn-primary" @onclick="SemanticKernelClient">Send Request</button> |
| 34 | + |
| 35 | +<br /> |
| 36 | + |
| 37 | +<h4>Server response:</h4> <p>@serverResponse</p> |
| 38 | + |
| 39 | +@code { |
| 40 | + |
| 41 | + public string? newQuery; |
| 42 | + public string? serverResponse; |
| 43 | + |
| 44 | +} |
| 45 | +``` |
| 46 | + |
| 47 | +Next, we need to add the new page to the navigation so we can navigate to the service. |
| 48 | + |
| 49 | +1. Go to the *NavMenu.razor* file under the **Layout** folder and add the following div in the nav class. Click **Save** |
| 50 | + |
| 51 | +```csharp |
| 52 | + |
| 53 | +<div class="nav-item px-3"> |
| 54 | + <NavLink class="nav-link" href="openai"> |
| 55 | + <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> OpenAI |
| 56 | + </NavLink> |
| 57 | +</div> |
| 58 | +``` |
| 59 | + |
| 60 | +After the Navigation is updated, we can start preparing to build the OpenAI client to handle our requests. |
| 61 | + |
| 62 | +### API keys and endpoints |
| 63 | + |
| 64 | +In order to make calls to OpenAI with your client, you need to first grab the Keys and Endpoint values from Azure OpenAI, or OpenAI and add them as secrets for use in your application. Retrieve and save the values for later use. |
| 65 | + |
| 66 | +For Azure OpenAI, see [this documentation](https://learn.microsoft.com/azure/ai-services/openai/quickstart?pivots=programming-language-csharp&tabs=command-line%2Cpython#retrieve-key-and-endpoint) to retrieve the key and endpoint values. For our application, you need the following values: |
| 67 | +
|
| 68 | +- `deploymentName` |
| 69 | +- `endpoint` |
| 70 | +- `apiKey` |
| 71 | +- `modelId` |
| 72 | + |
| 73 | +For OpenAI, see this [documentation](https://platform.openai.com/docs/api-reference) to retrieve the API keys. For our application, you need the following values: |
| 74 | +- `apiKey` |
| 75 | +- `modelId` |
| 76 | + |
| 77 | +Since we are deploying to App Service, we can secure these secrets in **Azure Key Vault** for protection. Follow the [Quickstart](https://learn.microsoft.com/azure/key-vault/secrets/quick-create-cli#create-a-key-vault) to set up your Key Vault and add the secrets you saved from earlier. |
| 78 | +Next, we can use Key Vault references as app settings in our App Service resource to reference in our application. Follow the instructions in the [documentation](https://learn.microsoft.com/azure/app-service/app-service-key-vault-references?source=recommendations&tabs=azure-cli) to grant your app access to your Key Vault and to set up Key Vault references. |
| 79 | +Then, go to the portal Environment Variables blade in your resource and add the following app settings: |
| 80 | + |
| 81 | +For Azure OpenAI, use the following settings: |
| 82 | + |
| 83 | +| Setting name| Value | |
| 84 | +|-|-|-| |
| 85 | +| `DEPOYMENT_NAME` | @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/) | |
| 86 | +| `ENDPOINT` | @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/) | |
| 87 | +| `API_KEY` | @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/) | |
| 88 | +| `MODEL_ID` | @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/) | |
| 89 | +
|
| 90 | + |
| 91 | +For OpenAI, use the following settings: |
| 92 | + |
| 93 | +| Setting name| Value | |
| 94 | +|-|-|-| |
| 95 | +| `OPENAI_API_KEY` | @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/) | |
| 96 | +| `OPENAI_MODEL_ID` | @Microsoft.KeyVault(SecretUri=https://myvault.vault.azure.net/secrets/mysecret/) | |
| 97 | +
|
| 98 | + |
| 99 | +Once your app settings are saved, you can bring them into the code by injecting IConfiguration and referencing the app settings. Add the following code to your *OpenAI.razor* file: |
| 100 | + |
| 101 | +For Azure OpenAI: |
| 102 | +```csharp |
| 103 | +@inject Microsoft.Extensions.Configuration.IConfiguration _config |
| 104 | + |
| 105 | +@code { |
| 106 | + |
| 107 | + private async Task SemanticKernelClient() |
| 108 | + { |
| 109 | + string deploymentName = _config["DEPLOYMENT_NAME"]; |
| 110 | + string endpoint = _config["ENDPOINT"]; |
| 111 | + string apiKey = _config["API_KEY"]; |
| 112 | + string modelId = _config["MODEL_ID"]; |
| 113 | + } |
| 114 | +``` |
| 115 | + |
| 116 | +For OpenAI: |
| 117 | +```csharp |
| 118 | +@inject Microsoft.Extensions.Configuration.IConfiguration _config |
| 119 | + |
| 120 | +@code { |
| 121 | + |
| 122 | + private async Task SemanticKernelClient() |
| 123 | + { |
| 124 | + // OpenAI |
| 125 | + string OpenAIModelId = _config["OPENAI_MODEL_ID"]; |
| 126 | + string OpenAIApiKey = _config["OPENAI_API_KEY"]; |
| 127 | + } |
| 128 | +``` |
| 129 | + |
| 130 | +### Semantic Kernel |
| 131 | + |
| 132 | +Semantic Kernel is an open-source SDK that enables you to easily develop AI agents to work with your existing code. You can use Semantic Kernel with Azure OpenAI and OpenAI models. |
| 133 | + |
| 134 | +To create the OpenAI client, we'll first start by installing Semantic Kernel. |
| 135 | + |
| 136 | +To install Semantic Kernel, browse the NuGet package manager in Visual Studio and install the **Microsoft.SemanticKernel** package. For NuGet Package Manager instructions, see [here](https://learn.microsoft.com/nuget/consume-packages/install-use-packages-visual-studio#find-and-install-a-package). For CLI instructions, see [here](https://learn.microsoft.com/nuget/consume-packages/install-use-packages-dotnet-cli). |
| 137 | +Once the Semantic Kernel package is installed, you can now initialize the kernel. |
| 138 | + |
| 139 | +### Initialize the kernel |
| 140 | + |
| 141 | +To initialize the Kernel, add the following code to the *OpenAI.razor* file. |
| 142 | + |
| 143 | +```csharp |
| 144 | + |
| 145 | +@code { |
| 146 | + |
| 147 | + @using Microsoft.SemanticKernel; |
| 148 | + |
| 149 | + private async Task SemanticKernelClient() |
| 150 | + { |
| 151 | + var builder = Kernel.CreateBuilder(); |
| 152 | + |
| 153 | + var kernel = builder.Build(); |
| 154 | + } |
| 155 | + |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +Here we're adding the using statement and creating the Kernel in a method that we can use when we send the request to the service. |
| 160 | + |
| 161 | +### Add your AI service |
| 162 | + |
| 163 | +Once the Kernel is initialized, we can add our chosen AI service to the kernel. Here we define our model and pass in our key and endpoint information to be consumed by the chosen model. |
| 164 | + |
| 165 | +For Azure OpenAI, use the following code: |
| 166 | + |
| 167 | +```csharp |
| 168 | +var builder = Kernel.CreateBuilder(); |
| 169 | +builder.Services.AddAzureOpenAIChatCompletion( |
| 170 | + deploymentName: deploymentName, |
| 171 | + endpoint: endpoint, |
| 172 | + apiKey: apiKey, |
| 173 | + modelId: modelId |
| 174 | +); |
| 175 | +var kernel = builder.Build(); |
| 176 | +``` |
| 177 | + |
| 178 | +For OpenAI, use the following code: |
| 179 | + |
| 180 | +```csharp |
| 181 | + |
| 182 | +var builder = Kernel.CreateBuilder(); |
| 183 | +builder.Services.AddOpenAIChatCompletion( |
| 184 | + modelId: OpenAIModelId, |
| 185 | + apiKey: OpenAIApiKey, |
| 186 | +); |
| 187 | +var kernel = builder.Build(); |
| 188 | +``` |
| 189 | + |
| 190 | +### Configure prompt and create semantic function |
| 191 | + |
| 192 | +Now that our chosen OpenAI service client is created with the correct keys we can add a function to handle the prompt. With Semantic Kernel you can handle prompts by the use of a semantic function, which turn the prompt and the prompt configuration settings into a function the Kernel can execute. Learn more on configuring prompts [here](https://learn.microsoft.com/semantic-kernel/prompts/configure-prompts?tabs=Csharp). |
| 193 | + |
| 194 | +First, we create a variable that holds the user's prompt. Then add a function with execution settings to handle and configure the prompt. Add the following code to the *OpenAI.razor* file: |
| 195 | + |
| 196 | +```csharp |
| 197 | + |
| 198 | +@using Microsoft.SemanticKernel.Connectors.OpenAI |
| 199 | + |
| 200 | +private async Task SemanticKernelClient() |
| 201 | +{ |
| 202 | + var builder = Kernel.CreateBuilder(); |
| 203 | + builder.Services.AddAzureOpenAIChatCompletion( |
| 204 | + deploymentName: deploymentName, |
| 205 | + endpoint: endpoint, |
| 206 | + APIKey: APIKey, |
| 207 | + modelId: modelId |
| 208 | + ); |
| 209 | + var kernel = builder.Build(); |
| 210 | + |
| 211 | + var prompt = @"{{$input}} " + newQuery; |
| 212 | + |
| 213 | + var summarize = kernel.CreateFunctionFromPrompt(prompt, executionSettings: new OpenAIPromptExecutionSettings { MaxTokens = 100, Temperature = 0.2 }); |
| 214 | + |
| 215 | + } |
| 216 | + |
| 217 | +``` |
| 218 | + |
| 219 | +Lastly, we need to invoke the function and return the response. Add the following to the *OpenAI.razor* file: |
| 220 | + |
| 221 | +```csharp |
| 222 | + |
| 223 | +private async Task SemanticKernelClient() |
| 224 | +{ |
| 225 | + |
| 226 | + var builder = Kernel.CreateBuilder(); |
| 227 | + builder.Services.AddAzureOpenAIChatCompletion( |
| 228 | + deploymentName: deploymentName, |
| 229 | + endpoint: endpoint, |
| 230 | + APIKey: APIKey, |
| 231 | + modelId: modelId |
| 232 | + ); |
| 233 | + var kernel = builder.Build(); |
| 234 | + |
| 235 | + var prompt = @"{{$input}} " + newQuery; |
| 236 | + |
| 237 | + var summarize = kernel.CreateFunctionFromPrompt(prompt, executionSettings: new OpenAIPromptExecutionSettings { MaxTokens = 100, Temperature = 0.2 }) |
| 238 | + |
| 239 | + var result = await kernel.InvokeAsync(summarize); |
| 240 | + |
| 241 | + serverResponse = result.ToString(); |
| 242 | + |
| 243 | + } |
| 244 | +``` |
| 245 | + |
| 246 | +Here's the example in its completed form. In this example, use the Azure OpenAI chat completion service OR the OpenAI chat completion service, not both. |
| 247 | + |
| 248 | +```csharp |
| 249 | +@page "/openai" |
| 250 | +@rendermode InteractiveServer |
| 251 | +@inject Microsoft.Extensions.Configuration.IConfiguration _config |
| 252 | + |
| 253 | +<PageTitle>OpenAI</PageTitle> |
| 254 | + |
| 255 | +<h3>OpenAI input query: </h3> |
| 256 | +<input class="col-sm-4" @bind="newQuery" /> |
| 257 | +<button class="btn btn-primary" @onclick="SemanticKernelClient">Send Request</button> |
| 258 | + |
| 259 | +<br /> |
| 260 | +<br /> |
| 261 | + |
| 262 | +<h4>Server response:</h4> <p>@serverResponse</p> |
| 263 | + |
| 264 | +@code { |
| 265 | + |
| 266 | + @using Microsoft.SemanticKernel; |
| 267 | + @using Microsoft.SemanticKernel.Connectors.OpenAI |
| 268 | + |
| 269 | + private string? newQuery; |
| 270 | + private string? serverResponse; |
| 271 | + |
| 272 | + private async Task SemanticKernelClient() |
| 273 | + { |
| 274 | + // Azure OpenAI |
| 275 | + string deploymentName = _config["DEPLOYMENT_NAME"]; |
| 276 | + string endpoint = _config["ENDPOINT"]; |
| 277 | + string apiKey = _config["API_KEY"]; |
| 278 | + string modelId = _config["MODEL_ID"]; |
| 279 | + |
| 280 | + // OpenAI |
| 281 | + // string OpenAIModelId = _config["OPENAI_DEPLOYMENT_NAME"]; |
| 282 | + // string OpenAIApiKey = _config["OPENAI_API_KEY"]; |
| 283 | + // Semantic Kernel client |
| 284 | + var builder = Kernel.CreateBuilder(); |
| 285 | + // Azure OpenAI |
| 286 | + builder.Services.AddAzureOpenAIChatCompletion( |
| 287 | + deploymentName: deploymentName, |
| 288 | + endpoint: endpoint, |
| 289 | + apiKey: apiKey, |
| 290 | + modelId: modelId |
| 291 | + ); |
| 292 | + // OpenAI |
| 293 | + // builder.Services.AddOpenAIChatCompletion( |
| 294 | + // modelId: OpenAIModelId, |
| 295 | + // apiKey: OpenAIApiKey |
| 296 | + // ); |
| 297 | + var kernel = builder.Build(); |
| 298 | + |
| 299 | + var prompt = @"{{$input}} " + newQuery; |
| 300 | + |
| 301 | + var summarize = kernel.CreateFunctionFromPrompt(prompt, executionSettings: new OpenAIPromptExecutionSettings { MaxTokens = 100, Temperature = 0.2 }); |
| 302 | + |
| 303 | + var result = await kernel.InvokeAsync(summarize); |
| 304 | + |
| 305 | + serverResponse = result.ToString(); |
| 306 | + |
| 307 | + } |
| 308 | +} |
| 309 | +``` |
| 310 | + |
| 311 | +Now save the application and follow the next steps to deploy it to App Service. If you would like to test it locally first at this step, you can swap out the config values at with the literal string values of your OpenAI service. For example: string modelId = 'gpt-4-turbo'; |
| 312 | + |
| 313 | +### Deploy to App Service |
| 314 | + |
| 315 | +If you have followed the steps above, you're ready to deploy to App Service. If you run into any issues remember that you need to have done the following: grant your app access to your Key Vault, add the app settings with key vault references as your values. App Service resolves the app settings in your application that match what you've added in the portal. |
| 316 | + |
| 317 | + |
| 318 | +### Authentication |
| 319 | + |
| 320 | +Although optional, it's highly recommended that you also add authentication to your web app when using an Azure OpenAI or OpenAI service. This can add a level of security with no other code. Learn how to enable authentication for your web app [here](https://learn.microsoft.com/azure/app-service/scenario-secure-app-authentication-app-service). |
| 321 | + |
| 322 | +Once deployed, browse to the web app and navigate to the OpenAI tab. Enter a query to the service and you should see a populated response from the server. The tutorial is now complete and you now know how to use OpenAI services to create intelligent applications. |
0 commit comments