🐕

Semantic KernelでGPT-4でのチャットアプリを実装する

2023/05/04に公開

Azure AI ServiceのGPT-4ではAIとチャットすることで様々な創造が可能になっています。
今回はBlazor ServerでSemantic KernelというMicrosoftのライブラリを使いチャットアプリの実装する方法について記述します。

まずは、Semantic KernelをNugetします。現在、Previewですのでプレリリースにチェックをつけて検索することが必要です。

次に、Semantic Kernelを呼び出すクラスを実装していきます。
Razorからはinjectして使用します。

SemanticKernelLogic.cs
using Markdig;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.AI.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.AI.OpenAI.ChatCompletion;

namespace SemanticKernelBlazorSample.Logic
{
    public class SementicKernelLogic
    {
        public IKernel kernel;
        public IChatCompletion GtpChat4 { get; set; }
        private readonly ILogger<SementicKernelLogic> _logger;
        private readonly IConfiguration _configuration;
        private readonly OpenAIChatHistory chatHistory;
        public SementicKernelLogic(ILogger<SementicKernelLogic> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
            string baseUrl = _configuration.GetValue<string>("BaseUrl") ?? string.Empty;
            string key = _configuration.GetValue<string>("Gtp4Key") ?? string.Empty;

            kernel = new KernelBuilder().Configure(c =>
            {
                c.AddAzureChatCompletionService("gpt-4", "Gtp-4", baseUrl, key);
                
            }).WithLogger(_logger).Build();
            GtpChat4 = kernel.GetService<IChatCompletion>();
            
            chatHistory = (OpenAIChatHistory)GtpChat4.CreateNewChat();
            
        }
        public async Task<string> Run(string input)
        {
            chatHistory.AddUserMessage(input);
            var setting = new ChatRequestSettings
            {
                Temperature = 0.8,
                MaxTokens = 2000,
                FrequencyPenalty = 0.5
            };
            string reply = await GtpChat4.GenerateMessageAsync(chatHistory,setting);
            var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().UseAutoLinks().UseBootstrap().UseDiagrams().UseGridTables().Build();
            var htmlReply = Markdown.ToHtml(reply, pipeline);
            chatHistory.AddAssistantMessage(reply);
            return htmlReply;
        }
    }
}

コンストラクタではそれぞれの環境に合わせてパラメータを設定してください。
そして、実際にAzure AI Serviceに問い合わせをするRunメソッドではまずChatRequestSettingsではおなじみのパラメータの調整を行います。
これをメソッドの外に出すなど工夫の余地があると思います。

そして、GenerateMesssageAsync()でAzure AI Serviceに問い合わせを行います。そして、結果がreplyに入ってきます。
例によって、replyはマークアップされたテキストで帰ってきますので適宜HTMLに変換して表示するなどの後処理をするときれいに表示できます。

Razor側は以下のようにしました。

index.razor
@page "/"
@using SemanticKernelBlazorSample.Data;
@using SemanticKernelBlazorSample.Logic
@inject SementicKernelLogic logic

<textarea rows="4" cols="80" @bind=Search /><br />
<button type="submit" @onclick="OnSearch">検索</button><br />
@if (Messages != null)
{
    <table class="table table-striped">
        <tbody>

            @foreach (var message in Messages.OrderByDescending(x => x.Time))
            {
                <tr>
                    <td>@message.Name</td>
                    @if (message.Name == "Reply")
                    {
                        <td>@((MarkupString)message.Message)</td>
                    }
                    else
                    {
                        <td>@message.Message</td>
                    }
                </tr>
            }
        </tbody>
    </table>

}


@code {
    private string? Search;
    private string? Response;
    private List<ChatMessage> Messages = new();

    private async void OnSearch()
    {
        if (!string.IsNullOrWhiteSpace(Search))
        {
            ChatMessage UserMessage = new();
            UserMessage.Name = "User";
            UserMessage.Message = Search;
            Messages.Add(UserMessage);
            Search = string.Empty;
            StateHasChanged();
            Response = await logic.Run(UserMessage.Message);

            ChatMessage reply = new();
            reply.Name = "Reply";
            reply.Message = Response;
            Messages.Add(reply);
            StateHasChanged();

        }
        Search = string.Empty;
    }
}

最後にProgram.csにAddScopedでSementicKernelLogicをサービス登録してあげれば動くと思います。

Discussion