使用Semantic Kernel 建立自然語言請假系統

既然我們已經知道,可以透過 Semantic Kernel 輕易地建立聊天機器人/智能助理,我們之前(參考這篇)也看到了如何用自然語言驅動 AI ,來自動呼叫 IoT 控制開關燈類別中的方法,體驗過了它的威力,接著,我們就來實作一下,如何透過 Semantic Kernel 來建立一個可以透過自然語言請假的對談機器人。

我得說,我過去幾年做過無數次這個範例,試圖用自然語言以對談方式來完成請假。從最初LINE Bot 出現的時候,以手工苦刻的方式,來建立請假機器人,到使用 Azure AI 上的 Language Understanding 解決方案,讓機器人能夠『稍微』看懂用戶以自然語言的方式輸入的請假資訊,但整個過程從來沒有愉快過。

過去,電腦對自然語言的理解實在太差了,直到GPT的出現,直到有了LLM,一切才開始不同。現在,我們可以輕易地透過 Semantic Kernel 使用大語言模型,來建立一個類似底下這樣,表現非常好的自然語言請假系統:
enter image description here

你會發現,我們實作了一個可以幫助用戶請假的對談機器人。他會蒐集用戶的請假資訊,然後在資訊滿足之後,呼叫API來完成請假動作。

上面這段對談紀錄當中,黃色的部分就是AI自動進行的 Action,也就是具體的『請假』或是『查詢』動作,其他的則是自然語言的交談對話。

我們之前說過,透過 Semantic Kernel ,我們可以快速地完成上面這個功能的開發。為了完成這個需求,我們建立了底下這個 LeaveRequestPlugin 類別。這個類別裡面有三個方法,你可以從Description描述看到這些方法的功能(當然,範例裡面的Action暫且用示意的方式來呈現,不真的寫入資料庫):

// 請假功能 Plugin
public class LeaveRequestPlugin
{
    [KernelFunction]
    [Description("取得請假天數")]
    public int GetLeaveRecordAmount([Description("要查詢請假天數的員工名稱")] string employeeName)
    {
        //修改顯示顏色
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine($"\n [action]查詢 {employeeName} 請假天數。\n");
        //還原顯示顏色
        Console.ResetColor();

        if (employeeName.ToLower() == "david")
            return 5;
        else if (employeeName.ToLower() == "eric")
            return 8;
        else
            return 3;
    }

    [KernelFunction]
    [Description("進行請假")]
    public bool LeaveRequest([Description("請假起始日期")] DateTime 請假起始日期, [Description("請假天數")] string 天數, [Description("請假事由")] string 請假事由, [Description("代理人")] string 代理人,
    [Description("請假者姓名")] string 請假者姓名)
    {
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine($"\n [action]建立假單:  {請假者姓名} 請假 {天數}天,從 {請假起始日期} 開始,事由為 {請假事由},代理人 {代理人}\n");
        //還原顯示顏色
        Console.ResetColor();

        return true;
    }

    [KernelFunction]
    [Description("取得今天日期")]
    public DateTime GetCurrentDate()
    {
        return DateTime.UtcNow.AddHours(8);
    }
}

主程式的部分,則大同小異,我們採用 OpenAI 的 gpt-4-turbo-preview Model,作為這個服務的 AI Provider,我們給的 system prompt是底下程式碼中撰寫的這樣:

        var OpenAIModel = "gpt-4-turbo-preview";
        var OpenAIKey = "👉ApiKey👈";

        // Create a new kernel builder
        var builder = Kernel.CreateBuilder()
                    .AddOpenAIChatCompletion(OpenAIModel, OpenAIKey);
        builder.Plugins.AddFromType<LeaveRequestPlugin>();  
        Kernel kernel = builder.Build();

        // Create chat history 物件,並且加入
        var history = new ChatHistory(
            @"你是企業的請假助理,可以協助員工進行請假,或是查詢請假天數等功能。
                 若員工需要請假,你需要蒐集請假起始日期、天數、請假事由、代理人、請假者姓名等資訊。最後呼叫 LeaveRequest Method。
                 若員工需要查詢請假天數,你需要蒐集請假者姓名,最後呼叫 GetLeaveRecordAmount Method。
                 --------------
                 * 所有對談請用正體中文回答
                 * 請以口語化的方式來回答,要適合對談機器人的角色
                ");

當我們在主程式中給了這樣的 prompt ,並且將 LeaveRequestPlugin 掛上之後,主程式只有底下這樣的典型的 Semantic Kernel 對談 Loop:

        // Get chat completion service
        var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>();

        // 開始對談
        Console.Write("用戶 > ");
        string? userInput;
        while (!string.IsNullOrEmpty(userInput = Console.ReadLine()))
        {
            // Add user input
            history.AddUserMessage(userInput);

            // Enable auto function calling
            OpenAIPromptExecutionSettings openAIPromptExecutionSettings = new()
            {
                ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
            };

            // Get the response from the AI
            var result = await chatCompletionService.GetChatMessageContentAsync(
                history,
                executionSettings: openAIPromptExecutionSettings,
                kernel: kernel);

            // Print the results
            Console.WriteLine("AI助理 > " + result);

            // Add the message from the agent to the chat history
            history.AddMessage(result.Role, result.Content ?? string.Empty);

            // Get user input again
            Console.Write("\n用戶 > ");
        }
    }
}

就這樣程式寫完了。

開始運行之後,剩下的就是 AI 自己決定在需要查詢用戶請假天數時,去呼叫了 GetLeaveRecordAmount(string employeeName) 這個方法,然後把員工名稱當作參數傳入。有趣的是,當AI知道需要用戶名稱這個參數時,會主動問用戶的名稱。此外,當用戶提到從 “明天” 開始請假時(參考下圖),AI也會主動呼叫 GetCurrentDate() 這個位於 LeaveRequestPlugin 類別中的方法,來取得今天的時間日期,並推斷出明天的日期,因此就無需再去問用戶明天是幾月幾號:
enter image description here

我還嘗試把 description和變數名稱都特別改成中文,看看運行起來的狀況如何? 顯然,對於LLM來說,這根本不是問題。

這一切邏輯和行為,都是透過 GPT 和 Semantic Kernel 攜手自動完成,開發人員寫的只是提示(Prompt)以及適當的提供API或類別中的方法。

透過 Semantic Kernel ,這是有史以來讓我首次覺得,撰寫一個要能夠以自然語言進行請假的系統是如此的簡單、快速、有趣,這讓所謂的 AI 助理(Assistant) 和 Copilots 的開發,瞬間變得非常的容易。

下一個世代的應用程式軟體開發方式,已在眼前。


參考 課程:
https://www.studyhost.tw/NewCourses/LineBot

留言

這個網誌中的熱門文章

在POC或迷你專案中使用 LiteDB

使用Qdrant向量資料庫實作語意相似度比對

專業的價值...

使用 Airtable 在小型需求上取代傳統資料庫

周末讀書會 - 一如既往