你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
在本指南中,你将生成一个 AI 聊天应用程序,并使用从 Azure 应用程序配置动态加载的聊天补全配置来循环访问提示。
先决条件
- 完成本教程以创建聊天补全配置。
- 最新的 .NET SDK
创建控制台应用
为项目新建一个文件夹。 在新文件夹中,运行以下命令以创建新的 .NET 控制台应用项目:
dotnet new console
在项目中安装以下 NuGet 包:
dotnet add package Microsoft.Extensions.Configuration.AzureAppConfiguration dotnet add package Microsoft.Extensions.Configuration.Binder dotnet add package Azure.Identity dotnet add package Azure.AI.OpenAI
打开 Program.cs 文件,并在文件顶部添加以下命名空间:
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.AzureAppConfiguration; using Azure.Identity; using Azure.AI.OpenAI; using OpenAI.Chat;
通过调用
AddAzureAppConfiguration
Program.cs 文件中的方法连接到应用配置存储。可以使用 Microsoft Entra ID(建议)或连接字符串连接到应用程序配置。 在此示例中,会使用 Microsoft Entra ID 和
DefaultAzureCredential
对应用程序配置存储区进行身份验证。 按照以下 说明 将 应用配置数据读取者 角色分配给由DefaultAzureCredential
表示的标识。 运行应用程序前,请务必留出足够的时间让权限生效。TokenCredential credential = new DefaultAzureCredential(); IConfigurationRefresher refresher = null; // Load configuration from Azure App Configuration IConfiguration configuration = new ConfigurationBuilder() .AddAzureAppConfiguration(options => { Uri endpoint = new(Environment.GetEnvironmentVariable("AZURE_APPCONFIGURATION_ENDPOINT") ?? throw new InvalidOperationException("The environment variable 'AZURE_APPCONFIGURATION_ENDPOINT' is not set or is empty.")); options.Connect(endpoint, credential) // Load all keys that start with "ChatApp:" and have no label. .Select("ChatApp:*") // Reload configuration if any selected key-values have changed. // Use the default refresh interval of 30 seconds. It can be overridden via refreshOptions.SetRefreshInterval. .ConfigureRefresh(refreshOptions => { refreshOptions.RegisterAll(); }); refresher = options.GetRefresher(); }) .Build();
创建连接到 Azure OpenAI 资源的实例
AzureOpenAIClient
。 可以使用 Microsoft Entra ID 或 API 密钥进行身份验证。若要使用 Microsoft Entra ID 访问 Azure OpenAI 资源,请使用
DefaultAzureCredential
。 将“认知服务 OpenAI 用户”角色分配给DefaultAzureCredential
所表示的标识。 有关详细步骤,请参阅 Azure OpenAI 服务指南的基于角色的访问控制 。 运行应用程序前,请务必留出足够的时间让权限生效。// Retrieve the OpenAI connection information from the configuration Uri openaiEndpoint = new(configuration["ChatApp:AzureOpenAI:Endpoint"]); string deploymentName = configuration["ChatApp:AzureOpenAI:DeploymentName"]; // Initialize the AzureOpenAIClient AzureOpenAIClient azureClient = new(openaiEndpoint, credential); // Create a chat client ChatClient chatClient = azureClient.GetChatClient(deploymentName);
若要使用 API 密钥访问 Azure OpenAI 资源,请添加以下代码:
// Initialize the AzureOpenAIClient var apiKey = configuration["ChatApp:AzureOpenAI:ApiKey"]; AzureOpenAIClient client = new(openAIEndpoint, new AzureKeyCredential(apiKey));
如果密钥 ChatApp:AzureOpenAI:ApiKey 是应用配置中的 Key Vault 引用,请确保将以下代码片段添加到
AddAzureAppConfiguration
调用中,并向 应用授予对 Key Vault 的访问权限。options.ConfigureKeyVault(keyVaultOptions => { keyVaultOptions.SetCredential(credential); });
在
ModelConfiguration
文件中定义类。internal class ModelConfiguration { [ConfigurationKeyName("model")] public string? Model { get; set; } [ConfigurationKeyName("messages")] public List<Message>? Messages { get; set; } [ConfigurationKeyName("max_tokens")] public int MaxTokens { get; set; } [ConfigurationKeyName("temperature")] public float Temperature { get; set; } [ConfigurationKeyName("top_p")] public float TopP { get; set; } } internal class Message { [ConfigurationKeyName("role")] public required string Role { get; set; } [ConfigurationKeyName("content")] public string? Content { get; set; } }
更新 Program.cs 文件以添加帮助程序方法来
GetChatMessages
处理聊天消息:// Helper method to convert configuration messages to chat API format static IEnumerable<ChatMessage> GetChatMessages(ModelConfiguration modelConfiguration) { return modelConfiguration.Messages.Select<Message, ChatMessage>(message => message.Role switch { "system" => ChatMessage.CreateSystemMessage(message.Content), "user" => ChatMessage.CreateUserMessage(message.Content), "assistant" => ChatMessage.CreateAssistantMessage(message.Content), _ => throw new ArgumentException($"Unknown role: {message.Role}", nameof(message.Role)) }); }
接下来,更新 Program.cs 文件中的现有代码以从 Azure 应用配置刷新配置,将最新的 AI 配置值应用于聊天完成设置,并从 AI 模型检索响应。
while (true) { // Refresh the configuration from Azure App Configuration await refresher.RefreshAsync(); // Configure chat completion with AI configuration var modelConfiguration = configuration.GetSection("ChatApp:Model").Get<ModelConfiguration>(); var requestOptions = new ChatCompletionOptions() { MaxOutputTokenCount = modelConfiguration.MaxTokens, Temperature = modelConfiguration.Temperature, TopP = modelConfiguration.TopP }; foreach (var message in modelConfiguration.Messages) { Console.WriteLine($"{message.Role}: {message.Content}"); } // Get chat response from AI var response = await chatClient.CompleteChatAsync(GetChatMessages(modelConfiguration), requestOptions); Console.WriteLine($"AI response: {response.Value.Content[0].Text}"); Console.WriteLine("Press Enter to continue..."); Console.ReadLine(); }
完成前面的步骤后, Program.cs 文件现在应包含完整的实现,如下所示:
using Azure.AI.OpenAI; using Azure.Identity; using Azure.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.AzureAppConfiguration; using OpenAI.Chat; TokenCredential credential = new DefaultAzureCredential(); IConfigurationRefresher refresher = null; // Load configuration from Azure App Configuration IConfiguration configuration = new ConfigurationBuilder() .AddAzureAppConfiguration(options => { Uri endpoint = new(Environment.GetEnvironmentVariable("AZURE_APPCONFIGURATION_ENDPOINT") ?? throw new InvalidOperationException("The environment variable 'AZURE_APPCONFIGURATION_ENDPOINT' is not set or is empty.")); options.Connect(endpoint, credential) // Load all keys that start with "ChatApp:" and have no label. .Select("ChatApp:*") // Reload configuration if any selected key-values have changed. // Use the default refresh interval of 30 seconds. It can be overridden via refreshOptions.SetRefreshInterval. .ConfigureRefresh(refreshOptions => { refreshOptions.RegisterAll(); }); refresher = options.GetRefresher(); }) .Build(); // Retrieve the OpenAI connection information from the configuration Uri openaiEndpoint = new(configuration["ChatApp:AzureOpenAI:Endpoint"]); string deploymentName = configuration["ChatApp:AzureOpenAI:DeploymentName"]; // Create a chat client AzureOpenAIClient azureClient = new(openaiEndpoint, credential); ChatClient chatClient = azureClient.GetChatClient(deploymentName); while (true) { // Refresh the configuration from Azure App Configuration await refresher.RefreshAsync(); // Configure chat completion with AI configuration var modelConfiguration = configuration.GetSection("ChatApp:Model").Get<ModelConfiguration>(); var requestOptions = new ChatCompletionOptions() { MaxOutputTokenCount = modelConfiguration.MaxTokens, Temperature = modelConfiguration.Temperature, TopP = modelConfiguration.TopP }; foreach (var message in modelConfiguration.Messages) { Console.WriteLine($"{message.Role}: {message.Content}"); } // Get chat response from AI var response = await chatClient.CompleteChatAsync(GetChatMessages(modelConfiguration), requestOptions); Console.WriteLine($"AI response: {response.Value.Content[0].Text}"); Console.WriteLine("Press Enter to continue..."); Console.ReadLine(); } static IEnumerable<ChatMessage> GetChatMessages(ModelConfiguration modelConfiguration) { return modelConfiguration.Messages.Select<Message, ChatMessage>(message => message.Role switch { "system" => ChatMessage.CreateSystemMessage(message.Content), "user" => ChatMessage.CreateUserMessage(message.Content), "assistant" => ChatMessage.CreateAssistantMessage(message.Content), _ => throw new ArgumentException($"Unknown role: {message.Role}", nameof(message.Role)) }); } internal class ModelConfiguration { [ConfigurationKeyName("model")] public string? Model { get; set; } [ConfigurationKeyName("messages")] public List<Message>? Messages { get; set; } [ConfigurationKeyName("max_tokens")] public int MaxTokens { get; set; } [ConfigurationKeyName("temperature")] public float Temperature { get; set; } [ConfigurationKeyName("top_p")] public float TopP { get; set; } } internal class Message { [ConfigurationKeyName("role")] public required string Role { get; set; } [ConfigurationKeyName("content")] public string? Content { get; set; } }
生成并运行应用
将名为 “AZURE_APPCONFIGURATION_ENDPOINT ”的环境变量设置为 Azure 门户中应用商店 “概述 ”下找到的应用配置存储的终结点。
如果使用 Windows 命令提示符,则请运行以下命令并重启命令提示符,这样更改才会生效:
setx AZURE_APPCONFIGURATION_ENDPOINT "<endpoint-of-your-app-configuration-store>"
如果使用 PowerShell,请运行以下命令:
$Env:AZURE_APPCONFIGURATION_ENDPOINT = "<endpoint-of-your-app-configuration-store>"
如果使用 macOS 或 Linux,请运行以下命令:
export AZURE_APPCONFIGURATION_ENDPOINT ='<endpoint-of-your-app-configuration-store>'
正确设置环境变量后,运行以下命令以生成并运行应用。
dotnet run
应会看到以下输出:
system: You are a helpful assistant. user: What is the capital of France ? AI response: The capital of France is **Paris**. Press Enter to continue...
在 Azure 门户中,选择已创建的应用配置存储实例。 从“操作”菜单中选择“配置资源管理器”,然后选择ChatApp:Model键。 将 Messages 属性的值进行更新:
- 角色:系统
- 内容:“你是一个欢快的导游”。
等待片刻,刷新间隔过后,然后按 Enter 键查看输出中更新的 AI 响应。
system: You are a cheerful tour guide user: What is the capital of France ? AI response: Oh là là! The capital of France is the magnificent **Paris**! Known as the "City of Light" (*La Ville Lumière*), it's famous for its romantic ambiance, iconic landmarks like the Eiffel Tower, the Louvre Museum, and Notre-Dame Cathedral, as well as its delicious pastries and charming cafés. Have you ever been, or is it on your travel bucket list? 😊✨ Press Enter to continue...