哈囉,各位饕客!歡迎來到我們的「AI 全餐」鐵人賽,不知不覺已經來到第 15 天了,旅程也過了一半。這幾天我們學會了如何用 AI 聊天、讓它動起來、甚至給它記憶,這些都像是在用普通的廚具做家常菜。
但今天,我們要解鎖一道主廚的私房菜!當你發現基本的提示詞模板(就像我們前幾天用的 {{$input}}
)不夠靈活,無法滿足你的複雜需求時,那就像廚師只會用鹽巴調味,卻想做出滿漢全席一樣。這時,我們就需要更強大的武器:Handlebars 提示詞模板引擎。
簡單來說,如果說 Semantic Kernel 內建的提示詞模板是「白話文」,那 Handlebars 就是「文言文」。它是一種強大的模板語言,讓你可以在提示詞中加入邏輯判斷、迴圈、甚至自訂函式,讓你的「食譜」變得更智慧、更動態。
想像一下,你不再只是簡單地告訴 AI「幫我總結這段文字」,而是可以說:「如果這段文字長度超過 500 字,請幫我總結,並標註關鍵字;否則,請直接回覆原文。」這就是 Handlebars 帶來的魔力。
在開始料理之前,我們需要先安裝這個新的工具。跟之前一樣,只要一行指令就行:
dotnet add package Microsoft.SemanticKernel.PromptTemplates.Handlebars
Handlebars 的語法非常直觀,它使用雙大括號 {{}}
來標記變數或邏輯區塊。以下幾個是我們最常用到的:
{{#if}}
/ {{#else}}
這就像是廚師在判斷「如果客人點了牛排,那就煎三分熟;否則就照菜單做」。這在 AI 應用中非常實用,例如根據不同的使用者輸入或變數值,動態調整 AI 的行為。
情境: 根據輸入的國家,給出不同的打招呼語。
{{#if country '==' 'Taiwan'}}
哈囉!歡迎來到台灣!
{{else if country '==' 'Japan'}}
こんにちは!日本へようこそ!
{{else}}
Hello! Welcome to our AI kitchen!
{{/if}}
{{#each}}
想像一下,你需要將一段對話歷史、一串商品清單或一個檔案列表,逐一交給 AI 處理。{{#each}}
就像是廚師在批量處理食材,一個一個來,不漏掉任何一個。
情境: 歷遍對話歷史,並以結構化的方式呈現給 AI。
SYSTEM: You are a helpful assistant in a conversation. Here is the chat history:
{{#each history}}
{{#ifEquals role 'user'}}
USER: {{content}}
{{else ifEquals role 'assistant'}}
ASSISTANT: {{content}}
{{/ifEquals}}
{{/each}}
這個範例中,我們迭代了 history
這個變數(通常是 ChatHistory
物件),並根據每個訊息的 role
(角色)動態地加上前綴,讓 AI 更清楚地知道誰說了什麼話。
我們來個稍微複雜一點的範例。假設我們想打造一個客服 AI,它需要根據對話歷史的長度,動態調整其「記憶」。當對話變長時,它需要一個機制來提醒自己「只關注最近的對話」。
首先,你需要設定一個 HandlebarsPromptTemplateFactory
並傳入你的 Kernel:
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;
var kernel = Kernel.CreateBuilder()
// 這裡加上你的 AI 服務設定
.AddOpenAIChatCompletion("gpt-4o", Environment.GetEnvironmentVariable("OPENAI_API_KEY")!)
.Build();
var chatHistory = new ChatHistory();
var templateFactory = new HandlebarsPromptTemplateFactory();
接下來,定義我們的 Handlebars 模板,並把它存在一個字串或檔案裡:
SYSTEM: You are a helpful assistant.
{{#if isLongConversation}}
You are in a long conversation. Summarize the user's intent from the last 3 messages and ignore older messages.
{{/if}}
{{#each history}}
{{#if (equals role 'user')}}
USER: {{content}}
{{else}}
ASSISTANT: {{content}}
{{/if}}
{{/each}}
這個模板非常有趣:
history
的長度大於或等於 5,它就會插入一段額外的指令,要求 AI 專注於最近的 3 則訊息。最後,讓我們把這個模板變成一個 KernelFunction
並執行它:
var templateConfig = new PromptTemplateConfig()
{
Template = promptTemplate,
TemplateFormat = "handlebars",
AllowDangerouslySetContent = true
};
var function = kernel.CreateFunctionFromPrompt(templateConfig, templateFactory);
// 第一次對話(少於 5 則)
chatHistory.AddUserMessage("今天天氣如何?");
chatHistory.AddAssistantMessage("今天天氣很好!");
chatHistory.AddUserMessage("我週末想出去玩,有什麼建議?");
// 將 ChatHistory 轉換為範本可以處理的格式,並加入數量檢查
var historyData = chatHistory.Select(message => new
{
role = message.Role.ToString().ToLower(),
content = message.Content
}).ToList();
var result = await kernel.InvokeAsync(function, new()
{
["history"] = historyData,
["isLongConversation"] = historyData.Count >= 5
});
Console.WriteLine("第一次對話結果:");
Console.WriteLine(result);
// 接著,我們新增更多對話,觸發 `{{#if}}` 條件
chatHistory.AddAssistantMessage("建議去海邊踏浪!");
chatHistory.AddUserMessage("好的,那需要帶什麼東西?");
chatHistory.AddAssistantMessage("防曬乳、帽子、泳衣等等。");
// 再次轉換更新後的 ChatHistory
historyData = chatHistory.Select(message => new
{
role = message.Role.ToString().ToLower(),
content = message.Content
}).ToList();
result = await kernel.InvokeAsync(function, new()
{
["history"] = historyData,
["isLongConversation"] = historyData.Count >= 5
});
Console.WriteLine("第二次對話結果:");
Console.WriteLine(result);
當你運行這個程式碼時,你會發現第二次的 InvokeAsync
執行時,AI 接收到的提示詞會多了一段「濃縮記憶」的指令。這就是 Handlebars 的強大之處:它讓你的 AI 應用變得更智慧,能根據不同的情境做出動態調整,而不僅僅是重複固定的模式。
今天的 Handlebars 模板,就像是給你的 AI 廚師裝上了「食譜生成器」。它不僅能讀懂你的基本指令,還能根據當下廚房(應用程式)的狀況,動態生成最合適的料理步驟。
有了這個工具,你將能處理更複雜、更具挑戰性的 AI 應用場景。在接下來的幾天裡,我們將把這些核心功能串聯起來,真正做出屬於你自己的滿漢全席!