Skip to content

Latest commit

 

History

History
185 lines (135 loc) · 7.67 KB

File metadata and controls

185 lines (135 loc) · 7.67 KB

STEP 02: Blazor로 AI 웹 앱 만들어보기

이 단계에서는 Semantic Kernel을 Blazor에 적용해보고, 기본적인 채팅 경험 구현, Markdown 표시를 Blazor에서 어떻게 하는 지 알아봅니다.

모델 응답을 받고 화면에 표시

Step-02-start 폴더의 HikingMateWebApp을 복사해서 다른 곳에 옮기거나, 해당 소스 코드로 바로 연습을 해보겠습니다.

해당 프로젝트가 있는 경로에서 터미널 실행 혹은 cd로 이동 후 HikingMate.WebApp\HikingMate.WebApp로 이동한 후 dotnet run을 실행합니다.

cd HikingMate.WebApp\HikingMate.WebApp
dotnet run

그러면 웹 프로젝트가 실행될 것이고, 기본적인 채팅 인터페이스가 보일 것입니다. 만약 브라우저에서 웹페이지가 보이지 않는다면 브라우저에서 http://localhost:5047에 들어가 봅니다. 여기에서 채팅창에 입력을 누르고 엔터를 눌러도 아무것도 실행되지 않습니다. 그렇다면 먼저 모델을 연결하고 사용자 입력에 따라 모델이 응답이 표시되도록 해보겠습니다.

먼저 Kernel 개체를 생성하겠습니다. HikingMate.WebApp/Components/Pages/ChatRoom.razor 파일을 엽니다. 그리고 아래 @code 영역에 코드를 추가합니다. Kernel, ChatCompletionService, ChatHistory를 선언해줍니다.

Kernel? kernel;
ChatHistory? chatHistory;
IChatCompletionService chatCompletionService;

Load 메서드를 추가합니다. Load 메서드에서는 Kernel, ChatCompletionService, ChatHistory 개체를 생성합니다. 모델은 gpt-4o를 사용하겠습니다.

public async Task Load()
{
    await Task.Delay(1);
    messages.Clear();
    var modelId = "gpt-4o";
    var client = GitHubModelsClient.GetClient();
    kernel = Kernel.CreateBuilder()
                        .AddOpenAIChatCompletion(modelId, client)
                        .Build();
    chatCompletionService = kernel.Services.GetService<IChatCompletionService>();
    chatHistory = new ChatHistory();
}

그리고 Load 메서드를 소스 코드 내의 OnInitializedAsync 에서 실행하겠습니다.

protected async override Task OnInitializedAsync()
{
    await Load();
}

그리고 Services/GitHubClient.cs파일로 이동해서 이전에 사용했던 PAT 토큰을 입력해줍니다.

public static OpenAIClient GetClient()
{
    var githubPAT = "github-PAT";//PAT 토큰으로 교체
    var uri = "https://models.inference.ai.azure.com";
    var client = new OpenAIClient(new ApiKeyCredential(githubPAT), new OpenAIClientOptions
    {
        Endpoint = new Uri(uri)
    });

    return client;
}

자, 이제 모델 사용 준비를 마쳤습니다. 이제 사용자 입력을 받아 모델의 응답을 받을 수 있도록 UI 작업을 해보겠습니다. 다시 HikingMate.WebApp/Components/Pages/ChatRoom.razor 파일을 엽니다.

먼저 input을 선언합니다.

string? input;

파일의 html 영역에서 textarea를 찾아 @bind="@input"@bind:event="oninput"를 입력합니다. 이는 텍스트 입력 컨트롤에서 oninput 이벤트가 발생할 때 텍스트 입력 컨트롤의 값을 방금 선언한 input과 바인딩시킵니다.

<textarea class="input-lg" placeholder="무엇을 도와드릴까요?" @bind="@input" @bind:event="oninput"></textarea>

그리고 @code 영역에 input을 ChatHistory에 추가하고 모델에 보내고 응답을 받는 코드를 추가하겠습니다.

private async Task Send()
{
    if (string.IsNullOrEmpty(input) || chatHistory == null || kernel == null)
        return;

    var userMessage = new ChatMessage(ChatMessageRole.User, input);
    messages.Add(userMessage);
    chatHistory.AddUserMessage(input);
    input = string.Empty;
    await InvokeAsync(StateHasChanged);

    var assistantMessage = new ChatMessage(ChatMessageRole.Assistant);
    messages.Add(assistantMessage);

    var result = chatCompletionService.GetStreamingChatMessageContentsAsync(chatHistory);
    await foreach (var text in result)
    {
        await Task.Delay(20);
        assistantMessage.Message += text;
        await InvokeAsync(StateHasChanged);
    }
}

그리고 Button이 클릭될 때 Send메서드를 실행하도록 html 영역에서 Send 버튼 코드에 @onclick="@(Send)"를 추가해 메서드를 연결합니다.

<button class="btn send-button" @onclick="@(Send)">
    <i class="fas fa-paper-plane"></i>
</button>

그리고 dotnet run을 터미널에 입력해 앱을 실행해보겠습니다. 그리고 안녕이라고 입력하고 버튼을 눌러 모델이 정상적으로 응답하는 지 확인합니다. 만약 모델의 응답이 제대로 화면에 표시되지 않는 경우 도움을 요청합니다.

엔터키를 눌렀을 때 채팅이 보내지도록 하기

지금까지 구현된 것으로는 보내기 버튼을 눌러야 채팅이 보내집니다. 하지만 채팅을 치면서 보내기 버튼을 클릭하는 것은 불편합니다. 엔터를 치면 채팅이 보내지도록 하겠습니다. 먼저 HandleKeyDown 메서드를 추가해 키보드 이벤트에서 엔터키가 입력되면 Send 메서드를 실행하도록 합니다.

private async Task HandleKeyDown(KeyboardEventArgs e)
{
    var isEnterKey = e.Key == "Enter" && !e.ShiftKey;
    if (isEnterKey)
        await Send();
}

그 다음 html 영역의 textarea에 @onkeydown="HandleKeyDown" 코드를 추가해 HandleKeydown 메서드를 연결합니다.

<textarea class="input-lg" @onkeydown="HandleKeyDown" placeholder="무엇을 도와드릴까요?" @bind="@input" @bind:event="oninput"></textarea>

그리고 dotnet run을 터미널에 입력해 앱을 실행해보겠습니다. 안녕이라고 입력하고 엔터를 눌러 모델이 정상적으로 응답하는 지 확인합니다. 그 다음 서울 트래킹 코스를 추천해줘라고 입력하고 응답을 살펴봅니다. 응답에 * 문자도 있을 것이고 알아볼 수는 있지만 정리되어 보이지는 않습니다. 이는 GPT와 같은 일반적인 LLM은 응답을 Markdown 포맷으로 하기 때문입니다. Blazor에서 Markdown을 간단히 표시하는 방법에 대해서 알아보겠습니다.

Markdown 표시하기

Models/ChatMessage.cs 파일을 열어 클래스 안에 아래의 코드를 추가해줍니다. 아래의 코드는 텍스트를 Markdig이라는 라이브러리를 사용하여 HTML으로 바꿔줍니다.

public string MarkdownMessage
{
    get
    {
        var message = Message;
        if (!string.IsNullOrEmpty(message))
        {
            var pipeline = new Markdig.MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
            return Markdown.ToHtml(message);
        }

        return message;
    }
}

그리고 다시 Components/Pages/ChatRoom.razor로 이동해서 메시지가 표시되는 곳의 코드를 아래와 같이 바꿔줍니다. MarkupString은 해당 텍스트가 단순 출력 텍스트가 아니라 Html Markup 텍스트라고 전달해줍니다.

<div class="@item.CSS">
    <div class="user">@item.Username</div>
    @* <div class="msg">@(@item.Message))</div> *@
    <div class="msg">@((MarkupString)@item.MarkdownMessage)</div>
</div>

다시 dotnet run을 터미널에 입력해 앱을 실행해보겠습니다. 서울 트래킹 코스를 추천해줘라고 입력하고 응답을 살펴봅니다. 알아보기 쉽게 표시됩니다.

수고하셨습니다🎉 Blazor로 AI 웹 앱 만들어보기를 완료하셨습니다. 이제 STEP 03: 페르소나 설정하기 단계로 넘어가 보세요.