探索Semantic Kernel內建外掛:深入瞭解ConversationSummaryPlugin的應用

董瑞鹏發表於2024-06-18

前言

經過前幾章的學習我們已經熟悉了Semantic Kernel 外掛的概念,以及基於Prompts構造的Semantic Plugins和基於本地方法構建的Native Plugins。本章我們來講解一下在Semantic Kernel 中內建的一些外掛,讓我們避免重複造輪子。

內建外掛

Semantic Kernel 有非常多的預定義外掛,作為解決通用業務的相關能力。Plugins 地址

image

這裡面每一個類庫都是一組相關功能的外掛SDK,其中Plugins.Core 裡面提供的使我們高頻使用的外掛集合。

Plugins.Core

image

可以看到Plugins.Core 內有以下幾個外掛:

  • ConversationSummaryPlugin: 對話總結外掛
  • FileIOPlugin: 讀寫檔案外掛
  • HttpPluginHttp請求功能的外掛
  • MathPluginMath 計算外掛
  • TextPlugin:字串操作外掛
  • TimePlugin:獲取當前時間和日期外掛
  • WaitPluginWaitPlugin提供了一組函式,在進行其餘操作之前等待。

實戰

我們來對Semantic Kernel中提供的內建外掛來做一個實戰練習

第一步需要安裝Nuget

NuGet\Install-Package Microsoft.SemanticKernel.Plugins.Core -Version 1.14.1-alpha

該包目前只有預覽版本,如果用 VS 的包管理器安裝,那需要勾選包括預覽發行版

ConversationSummaryPlugin

這是一個對話總結外掛,以提示詞構造的Semantic Plugins,外掛內定義了三個Kernel Function分別是:

  • SummarizeConversation :給定一段長的對話記錄,總結談話內容
  • GetConversationActionItems:給定一段長的對話記錄,識別出其中的行動項。
  • GetConversationTopics:給定一段長的對話記錄,識別出值得記住的主題
SummarizeConversation

我們先定義一個對話使用者我們測試的對話資料


string chatTranscript = @"
A: 你好,最近工作很忙碌,我們需要安排下週的會議時間,你覺得周幾比較合適?
B: 嗯,我明白,工作確實很忙。週三或週四應該比較合適,因為那時候大家的日程相對空閒一些。
A: 好的,週三或週四都可以,我們再確認一下其他同事的時間表。
B: 對,最好再和大家核實一下,免得出現時間衝突。
A: 我今天會發郵件詢問大家的意見,然後我們再做最終決定。
B: 好的,我也會在群裡提醒大家留意郵件。

A: 大家好,關於下週的會議安排,我建議定在週四下午兩點,在會議室A舉行,大家覺得怎麼樣?
C: 週四下午兩點可以,我在日曆上已經標註了。
D: 對不起,週四下午我有其他安排,能否改到週三下午呢?
A: 好的,我們儘量照顧大家的時間,那就改到週三下午兩點吧,地點仍然是會議室A。
B: 沒問題,我會通知其他同事,讓大家知道時間的變動。

";

Kernel註冊外掛:

var conversationSummaryPlugin = kernel.ImportPluginFromType<ConversationSummaryPlugin>();

總結會話內容

Console.WriteLine("SamplePlugins - Conversation Summary Plugin - Summarize");
{
    FunctionResult summary = await kernel.InvokeAsync(
        conversationSummaryPlugin["SummarizeConversation"], new() { ["input"] = chatTranscript });

    Console.WriteLine($"Generated Summary:{summary.ToString()}");
}

OutPut:

Generated Summary:In the conversation, A and B discuss scheduling a meeting for the following week, considering Wednesday or Thursday as potential dates due to lighter schedules. A decides to send an email to confirm the availability of all colleagues. Later, A proposes holding the meeting on Thursday at 2 PM in Conference Room A, but D requests a change due to a scheduling conflict. A agrees to reschedule the meeting to Wednesday at 2 PM in the same room, and B confirms that they will inform the rest of the team about the change.

Semantic Kernel的這個外掛我用了GPT-4oKimi都回復的是英文,我感覺這個內建的這個Semantic Kernel 外掛還是不夠完善。

我們可以看一下SummarizeConversation方法的Prompts定義

BEGIN CONTENT TO SUMMARIZE:
{{$INPUT}}

END CONTENT TO SUMMARIZE.

Summarize the conversation in 'CONTENT TO SUMMARIZE', identifying main points of discussion and any conclusions that were reached.
Do not incorporate other general knowledge.
Summary is in plain text, in complete sentences, with no markup or tags.

BEGIN SUMMARY:

簡要理解一下這個提示詞:

  • 開始標記:BEGIN CONTENT TO SUMMARIZE: 這個標記清晰地指示了摘要內容的開始。

  • 輸入佔位符:{{$INPUT}} 這是一個佔位符,用於插入需要被摘要的對話或文字內容。

  • 結束標記:END CONTENT TO SUMMARIZE. 這個標記同樣清晰地指示了摘要內容的結束。

  • 摘要指導:提供了對摘要的具體要求,包括識別對話的主要討論點和結論,並且強調不要包含外部的一般知識。

  • 格式要求:指出摘要應該是純文字,用完整的句子表達,不包含任何標記或標籤。

  • 摘要開始標記:BEGIN SUMMARY: 這個標記指示了摘要部分的開始。

針對上述我們發現的問題:會話摘要全部是中文的問題 我覺得可以進行提示詞的最佳化

最佳化的第一點內容是:總結應選擇最切合內容的語言in the language that best fits the content.

@"BEGIN CONTENT TO SUMMARIZE:
{{$INPUT}}

END CONTENT TO SUMMARIZE.

Please summarize the conversation, highlighting the main points and any conclusions reached, in the language that best fits the content. Do not incorporate any external general knowledge. The summary should be in plain text, in complete sentences, without any markup or tags.

BEGIN SUMMARY:

我們自定義一個外掛測試一下,建立一個CustomConversationSummaryPlugin的外掛,這個和原生SummarizeConversation外掛只有Prompts有區別

    private const int MaxTokens = 1024;

    private readonly KernelFunction _summarizeConversationFunction;
    public CustomConversationSummaryPlugin()
    {


        PromptExecutionSettings settings = new()
        {
            ExtensionData = new Dictionary<string, object>()
            {
                { "Temperature", 0.1 },
                { "TopP", 0.5 },
                { "MaxTokens", MaxTokens }
            }
        };

        this._summarizeConversationFunction = KernelFunctionFactory.CreateFromPrompt(
            CustomConversationSummaryPlugin.SummarizeConversationDefinition,
            description: "Given a section of a conversation transcript, summarize the part of the conversation.",
            executionSettings: settings);
    }

    /// <summary>
    /// Given a long conversation transcript, summarize the conversation.
    /// </summary>
    /// <param name="input">A long conversation transcript.</param>
    /// <param name="kernel">The <see cref="Kernel"/> containing services, plugins, and other state for use throughout the operation.</param>
    [KernelFunction, Description("Given a long conversation transcript, summarize the conversation.")]
    public Task<string> SummarizeConversationAsync(
        [Description("A long conversation transcript.")] string input,
        Kernel kernel) =>
        ProcessAsync(this._summarizeConversationFunction, input, kernel);
    private static async Task<string> ProcessAsync(KernelFunction func, string input, Kernel kernel)
    {
        List<string> lines = TextChunker.SplitPlainTextLines(input, MaxTokens);
        List<string> paragraphs = TextChunker.SplitPlainTextParagraphs(lines, MaxTokens);

        string[] results = new string[paragraphs.Count];

        for (int i = 0; i < results.Length; i++)
        {
            // The first parameter is the input text.
            results[i] = (await func.InvokeAsync(kernel, new() { ["input"] = paragraphs[i] }).ConfigureAwait(false))
                .GetValue<string>() ?? string.Empty;
        }

        return string.Join("\n", results);
    }

Kernel物件註冊自定義外掛

var customCustomConversationSummaryPlugin = kernel.ImportPluginFromType<CustomConversationSummaryPlugin>();

新跑一邊測試一下:

Generated Summary:在這段對話中,A和B討論了安排下週會議的時間。B建議週三或週四比較合適,因為那時大家的日程相對空閒。A決定透過郵件詢問其他同事的意見,然後做出最終決定 。在郵件中,A提議將會議安排在週四下午兩點,地點是會議室A。然而,D表示週四下午有其他安排,請求將會議改到週三下午。A同意了D的請求,將會議時間調整為週三下午兩點,地點仍然是會議室A。B表示會通知其他同事關於時間變動的情況。

可以看到滿足我們的需求了,根據我們輸入生成的摘要資訊沒有問題了。

這個外掛對於我們的聊天會話也是十分有用,對話歷史記錄隨著不斷聊天,訊息越來越多,那每次對話消耗的 token 也是不斷增加,此時 ConversationSummaryPlugin 外掛的就可以幫助我們對聊天記錄進行摘要總結,提高聊天效率。

提取會話行動項

識別對話記錄中的動作項(action items)是一種重要的溝通技巧,它有助於提高效率、確保任務的完成和促進團隊協作。

使用場景包括:

  • 會議記錄:在會議結束後,快速生成包含所有動作項的摘要,便於團隊成員執行和跟蹤。
  • 專案管理:在專案討論中,識別和記錄關鍵的里程碑和任務,確保專案按時進展。
  • 客戶服務:在客戶溝通中,記錄客戶的請求和需要採取的行動,以提供更好的服務和支援。
  • 團隊協作工具:整合到團隊協作平臺中,幫助團隊成員共享和協調任務。
  • 個人生產力:個人使用該外掛來管理自己的任務和待辦事項,提高個人效率。
  • 法律和合規性:在需要確保對話內容符合特定法規或標準的情況下,識別必要的行動以確保合規。

要完成這個需要用到ConversationSummaryPlugin外掛的GetConversationActionItems方法

Console.WriteLine("======== SamplePlugins - Conversation Summary Plugin - Action Items ========");
{

    FunctionResult summary = await kernel.InvokeAsync(
        conversationSummaryPlugin["GetConversationActionItems"], new() { ["input"] = chatTranscript });

    Console.WriteLine($"Generated Action Items:{summary.ToString()}");
    Console.WriteLine(summary.GetValue<string>());

}

輸出:

{
    "actionItems": [
        {
            "owner": "A",
            "actionItem": "發郵件詢問大家的意見",
            "dueDate": "",
            "status": "Open",
            "notes": "今天會發"
        },
        {
            "owner": "B",
            "actionItem": "在群裡提醒大家留意郵件",
            "dueDate": "",
            "status": "Open",
            "notes": ""
        },
        {
            "owner": "B",
            "actionItem": "通知其他同事時間的變動",
            "dueDate": "",
            "status": "Open",
            "notes": "讓大家知道時間的變動"
        }
    ]
}

提取會話的主題

用於對話摘要的工具或外掛,它的作用是幫助使用者快速識別和總結對話中的主要話題。

使用場景可能包括但不限於:

  • 企業內部會議的快速摘要和資訊整理。
  • 客戶服務對話的分析,以識別服務改進點。
  • 社交媒體或論壇討論的監控和摘要。
  • 教育環境中的課堂討論摘要。
  • 法律諮詢或案件討論的記錄和審查。

要完成這個功能需要用到ConversationSummaryPlugin外掛的GetConversationTopics方法

使用:

    Console.WriteLine("======== SamplePlugins - Conversation Summary Plugin - Topics ========");

    FunctionResult summary = await kernel.InvokeAsync(
        conversationSummaryPlugin["GetConversationTopics"], new() { ["input"] = chatTranscript });

    Console.WriteLine($"Generated Topics:{summary.ToString()}");

輸出:

Generated Topics:
{
  "topics": [
    "Work busy",
    "Schedule meeting",
    "Wednesday or Thursday",
    "Confirm colleagues' availability",
    "Email for opinions",
    "Meeting reschedule",
    "Thursday 2 PM",
    "Change to Wednesday 2 PM",
    "Notify colleagues"
  ]
}

最後

剩下的外掛我們後續章節在講解吧,本章重點講解了ConversationSummaryPlugin 會話總結外掛的使用。

示例程式碼

本文原始碼

相關文章