다음을 통해 공유


.NET 콘솔 앱에서 채팅 완료 구성 사용

이 가이드에서는 AZURE App Configuration에서 동적으로 로드된 채팅 완료 구성을 사용하여 AI 채팅 애플리케이션을 빌드하고 프롬프트에서 반복합니다.

필수 조건

콘솔 앱 만들기

  1. 프로젝트에 대한 새 폴더를 만듭니다. 새 폴더에서 다음 명령을 실행하여 새 .NET 콘솔 앱 프로젝트를 만듭니다.

    dotnet new console
    
  2. 프로젝트에 다음 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
    
  3. Program.cs 파일을 열고 파일 맨 위에 다음 네임스페이스를 추가합니다.

    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.Configuration.AzureAppConfiguration;
    using Azure.Identity;
    using Azure.AI.OpenAI;
    using OpenAI.Chat;
    
  4. AddAzureAppConfiguration 파일에서 메서드를 호출하여 App Configuration 저장소에 연결합니다.

    Microsoft Entra ID(권장) 또는 연결 문자열 사용하여 App Configuration에 연결할 수 있습니다. 이 예제에서는 Microsoft Entra ID를 DefaultAzureCredential 사용하여 App Configuration 저장소에 인증합니다. 다음 지침에 따라 App Configuration 데이터 판독기 역할을 나타내는 ID에 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();
    
  5. Azure OpenAI 리소스에 AzureOpenAIClient 연결할 인스턴스를 만듭니다. 인증에 Microsoft Entra ID 또는 API 키를 사용할 수 있습니다.

    Microsoft Entra ID를 사용하여 Azure OpenAI 리소스에 액세스하려면 DefaultAzureCredential. Cognitive Services OpenAI 사용자 역할을 나타내는 DefaultAzureCredentialID에 할당합니다. 자세한 단계는 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가 App Configuration의 Key Vault 참조인 경우 다음 코드 조각을 호출에 AddAzureAppConfiguration 추가하고 앱에 Key Vault에 대한 액세스 권한을 부여해야 합니다.

    options.ConfigureKeyVault(keyVaultOptions =>
    {
        keyVaultOptions.SetCredential(credential);
    });
    
  6. 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; }
    }
    
  7. 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))
        });
    }
    
  8. 다음으로, Program.cs 파일의 기존 코드를 업데이트하여 Azure App Configuration에서 구성을 새로 고치고, 채팅 완료 설정에 최신 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();
    }
    
  9. 이전 단계를 완료한 후 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; }
    }
    

앱 빌드 및 실행

  1. AZURE_APPCONFIGURATION_ENDPOINT 환경 변수를 Azure Portal의 저장소 개요 아래에 있는 App Configuration 저장소의 엔드포인트로 설정합니다.

    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>'
    
  2. 환경 변수가 제대로 설정되면 다음 명령을 실행하여 앱을 빌드하고 실행합니다.

    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...
    
    
  3. Azure Portal에서 만든 App Configuration 저장소 인스턴스를 선택합니다. 작업 메뉴에서 구성 탐색기를 선택하고 ChatApp:Model 키를 선택합니다. Messages 속성의 값을 업데이트합니다.

    • 역할: 시스템
    • 콘텐츠: "당신은 쾌활한 투어 가이드입니다".
  4. 새로 고침 간격이 경과할 때까지 잠시 기다린 다음 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...