連續二十五天,我們用 Semantic Kernel (SK) 這位頂級廚師烹飪出了一道道令人驚豔的 AI 佳餚。然而,在正式將這些菜餚端給客人之前,我們還差最關鍵的一步:食品安全檢查!
在 AI 的世界裡,這不只是避免 AI 說錯話,更是要確保 AI 執行動作時是安全、負責任,並且符合我們企業規範的。如果我們讓 AI 擁有呼叫 API (Plugins) 的能力,它就擁有改變真實世界的能力(例如:建立訂單、發送郵件、轉帳)。一旦失控,後果不堪設想。
這時候,**Semantic Kernel 的 Filters(篩選器)**就像是我們廚房的「安全檢查閘門」,讓你在 AI 的執行流程中,任何關鍵步驟都能介入、檢查、甚至修改或阻止。
Filters 提供了三個主要的檢查點,讓我們能實現 Human-in-the-loop (人在迴路中) 的安全機制。
Filter 類型 | 檢查點比喻 | 發生時機 | 主要用途 |
---|---|---|---|
PromptRenderFilter |
食材備料檢查 | 提示詞送給 LLM 之前 | 檢查或修改要送給模型的提示詞,可用於防範提示詞注入 (Prompt Injection)。 |
FunctionInvocationFilter |
廚師動手檢查 | 執行 Plugin 函式 之前/之後 | 這是最關鍵的安全點,用於檢查函式參數、執行前確認、記錄呼叫日誌。 |
AutoFunctionInvocationFilter |
AI 自主呼叫檢查 | LLM 決定要呼叫函式、並由 SK 自動執行之前 | 專門用於 Function Calling 情境,在 SK 自動執行 LLM 建議的函式前進行最後確認。 |
匯出到試算表
其中,FunctionInvocationFilter
是我們執行敏感操作時最常使用的利器。今天,我們就來實戰這個 Filter,實現一個企業級的常見需求:在執行改變數據的動作前,要求開發者或使用者確認 (Human-in-the-loop)。
想像我們有一個名為 OrderPlugin
的工具,其中包含一個函式 CreateOrder
,用來在企業系統中建立一筆正式訂單。這是個高敏感度的操作。我們需要 SK 在呼叫這個函式前,暫停並要求人工確認。
我們將實作一個 ApprovalFilter
類別,並將它註冊到 Kernel 中。
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.Text.Json;
using static Microsoft.Extensions.AI.FunctionInvokingChatClient;
var config = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.Build();
var kernelBuilder = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(
modelId: "gpt-5",
apiKey: config["OpenAI:ApiKey"]!);
// 註冊我們的 OrderPlugin
kernelBuilder.Plugins.AddFromObject(new OrderPlugin());
// 註冊我們的 ApprovalFilter 到 Kernel
kernelBuilder.Services.AddSingleton<ApprovalFilter>();
Kernel kernel = kernelBuilder.Build();
Console.WriteLine("✅ 已註冊 OrderPlugin 及 ApprovalFilter。");
// 讓 AI 決定呼叫函式
var prompt = "請為客戶 ID 888 訂購 10 份『豪華套餐』。";
var settings = new OpenAIPromptExecutionSettings
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions
};
Console.WriteLine($"\n🧑 客戶請求:{prompt}");
// 執行請求
var result = await kernel.InvokePromptAsync(prompt, new(settings));
// 輸出結果
Console.WriteLine("\n🤖 AI 最終回覆:");
Console.WriteLine(result.GetValue<string>());
// 建立一個模擬的敏感 Plugin:訂單系統
public class OrderPlugin
{
[KernelFunction("CreateOrder")]
[System.ComponentModel.Description("在系統中建立一筆新的客戶訂單。")]
public string CreateOrder(string customerId, string product, int quantity)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"\n[系統通知] 正在為客戶 {customerId} 建立訂單:{quantity} 個 {product}...");
Console.ResetColor();
// 實際業務邏輯 (例如呼叫 CRM 或 ERP API)kmljn
return $"訂單已成功建立。訂單編號:ORD-{DateTime.Now.Ticks % 10000}";
}
}
// 實作我們的安全檢查閘門:FunctionInvocationFilter
public class ApprovalFilter : IFunctionInvocationFilter
{
public async Task OnFunctionInvocationAsync(FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next)
{
// 檢查:只有當函式名稱是 'CreateOrder' 時才需要介入
if (context.Function.Name.Equals("CreateOrder", StringComparison.OrdinalIgnoreCase))
{
Console.WriteLine("\n🚨🚨 [FunctionInvocationFilter 啟動] 🚨🚨");
Console.WriteLine($"AI 建議呼叫的函式: {context.Function.Name}");
Console.WriteLine($"參數: {JsonSerializer.Serialize(context.Arguments)}");
// 實現 Human-in-the-loop
Console.Write("這是敏感操作!您是否確定要執行?(y/n): ");
var response = Console.ReadLine();
if (response?.ToLower() != "y")
{
Console.WriteLine("🛑 操作已取消。");
return; // 直接返回,不會執行實際的 CreateOrder 函式
}
}
// 如果不是敏感函式,或使用者已確認,則繼續執行下一個步驟 (執行函式)
await next(context);
}
}
當我們執行上述程式碼時,即使 AI 判斷它需要呼叫 CreateOrder
函式,程式也不會直接執行。
OrderPlugin.CreateOrder(customerId: 888, product: "豪華套餐", quantity: 10)
。AutoFunctionInvocationFilter
會先讓 SK 準備執行,但緊接著 FunctionInvocationFilter
(也就是我們的 ApprovalFilter
) 會被觸發。ApprovalFilter
檢查到是敏感操作,暫停流程並在 Console 要求使用者輸入 y
或 n
。y
,Filter 呼叫 next(context)
,實際的 CreateOrder
函式會被執行。n
,Filter 不呼叫 next(context)
,而是直接在 context.Result
中設置一個「操作取消」的結果,這個結果將被回傳給 LLM,讓 LLM 知道操作失敗。透過這個機制,我們成功地在 AI 的智慧和現實世界的執行之間,建立了一個不可或缺的安全與責任邊界,確保每一筆敏感操作都在人工的監督之下。