Stories

Detail Return Return

深入學習Semantic Kernel:創建和配置prompts functions - Stories Detail

引言

上一章我們熟悉了一下 Semantic Kernel 的理論知識,Kernel 創建以及簡單的Sample熟悉了一下 SK 的基本使用。在Semantic Kernel中的 kernel functions由兩部分組成第一部分是prompts functions(提示函數),第二部分Native function(原生函數), kernel functions是構成插件(Plugins)的核心,一個插件代表一個或者多個的kernel functions,今天我們重點了解一下第一部分prompts functions(提示函數)。

kernel functions 基礎

我們知道跟大語言模型(LLM)交互靠的是提示(prompts),有效的提示設計對於使用 LLM AI 模型實現預期結果至關重要。提示工程,也稱為提示設計,是一個新興領域,需要創造力和對細節的關注。它涉及選擇正確的單詞、短語、符號和格式,以指導模型生成高質量和相關的文本。

提示工程的深入學習是用好大語言模型的關鍵。

創建提示函數

Semantic Kernel中提供了多種通過Prompts創建KernelFunction的擴展方法,底層本質上都是調用KernelFunctionFromPromptCreate方法來完成提示函數的創建。

    public static KernelFunction Create(
        IPromptTemplate promptTemplate,
        PromptTemplateConfig promptConfig,
        ILoggerFactory? loggerFactory = null)
    {
        Verify.NotNull(promptTemplate);
        Verify.NotNull(promptConfig);

        return new KernelFunctionFromPrompt(
            template: promptTemplate,
            promptConfig: promptConfig,
            logger: loggerFactory?.CreateLogger(typeof(KernelFunctionFactory)) ?? NullLogger.Instance);
    }

這裏面其實只有第二個參數PromptTemplateConfig是我們需要關心的,第一個參數promptTemplate是根據第二個參數 promptConfigTemplate屬性來構造的,接下來我們重點了解一下PromptTemplateConfig的配置。

PromptTemplateConfig的屬性

public sealed class PromptTemplateConfig
{
    private string? _templateFormat;
    private string _template = string.Empty;

    [JsonPropertyName("name")]
    public string? Name { get; set; }

    [JsonPropertyName("description")]
    public string? Description { get; set; }
    public static string SemanticKernelTemplateFormat => "semantic-kernel";

    [JsonPropertyName("template_format")]
    [AllowNull]
    public string TemplateFormat
    {
        get => this._templateFormat ?? SemanticKernelTemplateFormat;
        set => this._templateFormat = value;
    }

    [JsonPropertyName("template")]
    public string Template
    {
        get => this._template;
        set
        {
            Verify.NotNull(value);
            this._template = value;
        }
    }

    [JsonPropertyName("input_variables")]
    public List<InputVariable> InputVariables
    {
        get => this._inputVariables ??= [];
        set
        {
            Verify.NotNull(value);
            this._inputVariables = value;
        }
    }

    [JsonPropertyName("output_variable")]
    public OutputVariable? OutputVariable { get; set; }


    [JsonPropertyName("execution_settings")]
    public Dictionary<string, PromptExecutionSettings> ExecutionSettings
    {
        get => this._executionSettings ??= [];
        set
        {
            Verify.NotNull(value);
            this._executionSettings = value;
        }
    }

    [Experimental("SKEXP0001")]
    [JsonPropertyName("allow_unsafe_content")]
    public bool AllowUnsafeContent { get; set; } = false;
}

為了方便展示我們只保留PromptTemplateConfig的核心屬性,這個類非常的重要,包括我們要定義配置模版也是基於此類的字段來配置。

下面我們對PromptTemplateConfig的屬性進行簡單的講解

我們可以把PromptTemplateConfig可以看做是對一個函數的表述,帶着這個理解來解讀這個配置類更容易理解,如用 C#定義一個函數

[Description("無參無返回值的靜態函數")]
static void SampleFunction()
{
    Console.Write("無參無返回值函數");
}

Name屬性

Name屬性是在PromptTemplateConfig中用來獲取或設置在使用此配置創建提示函數(Prompts functions)時使用的默認函數名稱。
類型可空,如果不設置創建函數時將動態生成一個隨機名稱。命名規則:利用 GUID 生成一個不含連字符的隨機字符串,並將其格式化為以"func"為前綴的函數名稱

    private static string CreateRandomFunctionName() => $"func{Guid.NewGuid():N}";

Name 類似與我們在 C#中的函數名

Description 屬性

Description屬性是用於表示一個函數的描述信息,如果在創建prompts functions時候沒有顯示指定函數描述信息,那會採用Description 屬性的描述。

結合我們定義的 C#自定義函數中Description來理解這個屬性,其實就是給方法配置一個描述信息,提示方法也是一種特殊的方法。

TemplateFormat屬性

TemplateFormat屬性用於對prompts提示模板的格式配置,默認值為 "semantic-kernel"
對於prompts的模版格式化引擎 用的有兩種,第一種就是 Semantic Kernel 自帶的處理格式"semantic-kernel";第二種則是handlebars

Template屬性

Template 屬性用於存儲和管理用於定義prompts模板字符串。在設置模板字符串時,會進行空值驗證,以確保模板字符串不為 null,從而保證在生成prompts提示時模板內容有有效可用。

InputVariables 屬性

InputVariables屬性用於prompts提示模板中使用的輸入變量集合。

InputVariable 對象包含的屬性:

  • Name:變量的名稱,用於標識輸入變量。
  • Description:變量的描述,提供關於輸入變量的説明。
  • Default:變量的默認值。
  • IsRequired:指示變量是否為必需的,默認為 true。
  • JsonSchema:描述變量的 JSON 模式。
  • AllowUnsafeContent:指示是否允許不安全內容,默認為 false。

OutputVariable 屬性

OutputVariable 屬性用於定義和管理prompts提示模板中的輸出變量。

參考我們c#定義函數 這個我理解的就是對我們函數返回值參數的一個描述

ExecutionSettings屬性

ExecutionSettings屬性用於獲取或設置提示模板使用的執行設置集合;類型為Dictionary<string, PromptExecutionSettings>,表示一個鍵值對集合,其中鍵為服務 ID,值為執行設置。

      ExecutionSettings =
      {
          {
            OpenAIPromptExecutionSettings.DefaultServiceId,
              new OpenAIPromptExecutionSettings()
              {
                  MaxTokens = 1000,
                  Temperature = 0
              }
          },
          {
              "gpt-3.5-turbo", new OpenAIPromptExecutionSettings()
              {
                  ModelId = "gpt-3.5-turbo-0613",
                  MaxTokens = 4000,
                  Temperature = 0.2
              }
          }
      }

執行的配置為 PromptExecutionSettings.DefaultServiceId默認值是"default",因為Semantic Kernel都是基於.Net 8 的鍵值依賴注入Keyed,所以 default 就是獲取的上面默認的執行配置,

kernel.GetRequiredService<ITextGenerationService>();
kernel.GetRequiredService<ITextGenerationService>("gpt-3.5-turbo");

從代碼處理解就容易多了,可以通過ServiceKey去獲取不同大模型的實例。

OpenAIPromptExecutionSettings 配置

這個配置是大模型的進行請求時的參數配置,是PromptExecutionSettings提示執行設置的子類,OpenAI 的配置就是OpenAIPromptExecutionSettings,Google的大模型有自己的實現比如GeminiPromptExecutionSettings 核心參數其實都差不多,現在我們用OpenAI的提示詞執行設置熟悉下配置的參數。

  • MaxTokens:指定在生成文本或完成請求時允許生成的最大標記數,大多數模型的上下文長度為 2048 個標記(支持 4096 的 davinci-codex 除外)。
  • Temperature: 控制完成結果的隨機性。默認是 1.0,通常取值範圍在0-1.0之間。較高的温度會增加生成文本的隨機性,使得生成的文本更加多樣化和創新性,而較低的温度則會減少隨機性,使得生成的文本更加穩定和可預測。

    對於更具創意的應用程序,請嘗試 0.9,對於具有明確答案的應用程序,請嘗試 0(argmax 採樣)。

  • TopP: 用於控制完成結果的多樣性。默認是1.0。通過設置不同的值可以調整生成文本的多樣性程度。較高的 TopP 值會導致生成的文本更加多樣化,而較低的值則可能使生成的文本更加穩定和集中。

    使用温度進行採樣的替代方法,稱為核採樣,其中模型考慮具有 top_p 概率質量的標記的結果。因此,0.1 表示僅考慮包含前 10% 概率質量的代幣。我們通常建議改變這個或温度,但不要同時改變兩者。

  • PresencePenalty: 屬性接受介於-2.0和2.0之間的數字。默認是0。正值將根據新標記是否在文本中出現來對其進行懲罰,從而增加模型談論新主題的可能性。

    新標記:模型嘗試引入新的內容或概念,以增加生成文本的多樣性和創新性

  • FrequencyPenalty:屬性用於控制模型生成文本時對重複內容的處理方式。默認是0。它接受介於-2.0 和 2.0 之間的數字,其中正值表示根據標記在文本中的現有頻率對其進行懲罰,以降低模型直接重複相同內容的可能性。通過設置較高的 FrequencyPenalty 值,模型更有可能避免直接重複相同內容,從而降低生成文本中重複內容的頻率.

  • StopSequences:屬性用於指定一個字符串列表,其中包含模型在生成文本時遇到指定序列時應停止生成進一步標記。

    例如,如果設置 StopSequences 為[""],則當模型生成文本時遇到"<END>"序列時,生成過程將停止。

  • ChatSystemPrompt:屬性用於指定在使用聊天模型生成文本時要使用的系統提示。默認值是:"Assistant is a large language model."。這個系統提示可以影響生成文本的方向和內容,幫助模型更好地理解生成任務的背景和要求。通過合理設置 ChatSystemPrompt 屬性,可以定製生成文本時使用的系統提示,以獲得符合預期的生成結果。

  • ToolCallBehavior:屬性用於獲取或設置如何處理工具調用的行為。

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

    1.EnableKernelFunctions:會向模型提供內核的插件函數信息,但不會自動處理函數調用請求。模型需要顯式發起函數調用請求,並系統會傳播這些請求給適當的處理程序來執行。

     OpenAIPromptExecutionSettings settings = new() { ToolCallBehavior = ToolCallBehavior.EnableKernelFunctions };
     var chatHistory = new ChatHistory();
     ChatMessageContent result = await chat.GetChatMessageContentAsync(chatHistory, settings, kernel);
     //手動調用
     IEnumerable<FunctionCallContent> functionCalls = FunctionCallContent.GetFunctionCalls(result);
    

    EnableKernelFunctions:需要通過 FunctionCallContent 手動調用

    2.AutoInvokeKernelFunctions:除了向模型提供內核的插件函數信息外,還會嘗試自動處理任何函數調用請求。模型發起函數調用請求後,系統會自動執行相應的操作,並將結果返回給模型,而無需模型顯式處理函數調用的過程。

ResponseFormat屬性

ResponseFormat的屬性,用於獲取或設置用於完成操作的響應格式。可選值包括:"json_object""text",以及 ChatCompletionsResponseFormat 對象,可以選擇不同的值來指定響應的格式類型,例如使用 JSON 對象、純文本等不同的響應格式

ResultsPerPrompt屬性

ResultsPerPrompt 屬性用於確定每個提示生成的完成次數。默認值:默認為 1,即每個提示只生成一個完成結果。在自然語言處理中,一個提示(prompt)是輸入給模型的文本或問題,而完成(completion)是模型生成的對應輸出。通過設置 ResultsPerPrompt 屬性,您可以指定每個提示應該生成多少個完成結果。

Seed屬性

Seed屬性的作用是為了控制採樣的確定性,通過指定種子值,可以在一定程度上確保相同種子和參數下的重複請求返回相同的結果。然而,由於確定性並不是完全保證的,結果仍可能有一定程度的變化

User屬性

通過為每個最終用户分配一個唯一的標識符,OpenAI 可以更好地跟蹤和管理用户的行為,同時也可以更有效地監控系統是否受到濫用。

prompts functions 實戰

Semantic Kernel 有幾個 Kernel 對象的擴展方法用於prompts提示詞模版來創建KernelFunction,總的來説可以有三類:

我們繼續用我們上一章的 OneApi 代理星火訊飛 V3.5 方式來對接 Semantic Kernel 具體配置可以找我上一篇文章

基於 String 字符串創建 prompts functions

實戰

//基於String模版創建kernel functions
Console.WriteLine("====>基於String模版創建kernel functions<=====");
{
    string prompt = "What is the intent of this request? {{$request}}";
    var kernel = Kernel.CreateBuilder().AddOpenAIChatCompletion(modelId: config.ModelId,
        apiKey: config.ApiKey,
        httpClient: client).Build();
    var kernelFunction = kernel.CreateFunctionFromPrompt(prompt);

    string request = "I want to send an email to the marketing team celebrating their recent milestone.";

    // Create a kernel arguments object and add the  request
    var kernelArguments = new KernelArguments
            {
                { "request", request }
            };
    var functionResult = await kernelFunction.InvokeAsync(kernel, kernelArguments);

    Console.WriteLine(functionResult?.ToString() ?? string.Empty);
}

當然 SK 也提供了更加簡單的方法,直接傳prompts string 模版

    var functionResult = await kernel.InvokePromptAsync(prompt, kernelArguments);

這個方法內部實際上就是調用了CreateFunctionFromPrompt創建了kernel functions,目的是簡化提示函數創建的過程

基於PromptTemplateConfig對象創建 prompts functions

經過上面的介紹我們已經對PromptTemplateConfig的參數設置已經有了一個大致的認識,要實現這個要求需要藉助到我們的 kernel.CreateFunctionFromPrompt這個擴展方法,下面我們來實操一下:

實戰

string request = "I want to send an email to the marketing team celebrating their recent milestone.";
{
    var kernel = Kernel.CreateBuilder().AddOpenAIChatCompletion(modelId: config.ModelId,
    apiKey: config.ApiKey,
    httpClient: client).Build();

    var kernelFunctions = kernel.CreateFunctionFromPrompt(new PromptTemplateConfig()
    {
        Name = "intent",
        Description = "use assistant to understand user input intent.",
        TemplateFormat = PromptTemplateConfig.SemanticKernelTemplateFormat,//此處可以省略默認就是"semantic-kernel"
        Template = "What is the intent of this request? {{$request}}",
        InputVariables = [new() { Name = "request", Description = "The user's request.", IsRequired = true }],
        ExecutionSettings = new Dictionary<string, PromptExecutionSettings>() {
               {
                      OpenAIPromptExecutionSettings.DefaultServiceId ,//"default"
                        new OpenAIPromptExecutionSettings()
                        {
                            MaxTokens = 1024,
                            Temperature = 0
                        }
                    },
        }
    });
    var kernelArguments = new KernelArguments
    {
                { "request", request }
            };
    var functionResult = await kernelFunctions.InvokeAsync(kernel, kernelArguments);

    Console.WriteLine(functionResult?.ToString() ?? string.Empty);
}

運行效果

image

基於pluginDirectory從指定的插件目錄中創建插件

此方法是創建插件的方法之一,之前有介紹過插件就是一組kernel functions的集合,通過定義文件夾模版可以生成prompts functions,這部分內容等學習到Semantic KernelPlugins在着重講解吧。

最後

在本章中,我們深入探討了 Semantic Kernel 中的 kernel functions,重點關注了第一部分的 prompts functions(提示函數)。我們學習瞭如何基於不同方法創建這些提示函數,包括基於字符串模板和 PromptTemplateConfig 對象的創建方式,以及如何從指定的插件目錄中創建插件。

通過詳細講解 PromptTemplateConfig 的屬性,我們理解了如何配置和管理提示模板,以及如何調整執行設置來影響提示函數的生成結果。我們還實際操作了創建 kernel functions 的過程,加深了對提示工程的實際運用。

最後,我們展望了未來的學習方向,即 Semantic KernelPlugins 部分,這將為我們提供更多關於插件的創建和應用方法,進一步擴展我們的知識和應用領域。

通過本章的學習,相信您對 prompts functions 的創建和配置有了更深入的瞭解,為進一步探索和應用 Semantic Kernel 打下了堅實的基礎。如果您有任何疑問或需要進一步幫助,請隨時向我提問。感謝閲讀!🚀

參考資料

configure-prompts

本文示例源代碼

本文源代碼

😄歡迎關注筆者公眾號一起學習交流,獲取更多有用的知識~
image

Add a new Comments

Some HTML is okay.