【架構設計】Semantic Kernel 實戰:混合使用 Native Function 與 Semantic Function
1. Overview
隨著生成式 AI 技術的快速發展,將大型語言模型(LLM)的能力整合到傳統應用程式中,已成為現代軟體開發的趨勢。然而,LLM 擅長處理非結構化數據、理解語意和生成創意內容,卻不擅長執行精確的邏輯運算、存取外部系統或處理大量結構化數據。為了解決這個問題,Microsoft 推出了 Semantic Kernel (SK) 框架,它提供了一個輕量級的 SDK,旨在將 LLM 與傳統程式語言(如 C#, Python, Java)無縫結合,讓開發者能夠構建出既具備 AI 智慧又擁有確定性邏輯的應用程式 [1]。
Semantic Kernel 的核心概念之一是將應用程式的能力抽象為 Skills,而每個 Skill 又由一個或多個 Functions 組成。這些 Functions 主要分為兩大類:
- Native Function (原生技能):由傳統程式語言(例如 C#)編寫,負責執行確定性的、邏輯嚴謹的任務,如查詢資料庫、呼叫外部 API、執行複雜計算等。
- Semantic Function (語意技能):由提示詞(Prompt)定義,利用 LLM 的能力來處理語意理解、內容生成、摘要或格式轉換等任務。
本文件將深入探討 Semantic Kernel 在 .NET 9 環境下的核心架構,並透過實戰範例,演示如何巧妙地將 Native Function 與 Semantic Function 串聯(Chain)起來,讓 AI 能夠先透過 Native Function 從資料庫中獲取精確的結構化數據,再利用 Semantic Function 將這些數據轉化為易讀、富有洞察力的報告。這種混合使用模式,將極大提升 AI 應用程式的實用性、精確性和自動化程度。
2. Architecture / Design
Semantic Kernel 的設計哲學是將 LLM 視為一個強大的推理引擎,而 Native Functions 則為這個引擎提供了與真實世界互動的「手腳」。透過將這些能力組織成 Plugins 和 Functions,並由 Kernel 進行協調,我們可以構建出高度智能且可靠的 AI 應用程式 [2]。
2.1 Semantic Kernel 核心組件
Semantic Kernel 的核心架構圍繞著以下幾個關鍵組件:
- Kernel:這是 Semantic Kernel 的核心引擎,負責協調所有 Functions 的執行。它管理 Plugins、Functions、記憶體(Memory)和 LLM 服務的連接。開發者透過
Kernel實例來執行 AI 任務 [3]。 - Plugins (技能集):Plugins 是相關 Functions 的集合。一個 Plugin 可以包含多個 Native Functions 和 Semantic Functions。例如,一個
DatabasePlugin可能包含GetSalesData(Native) 和SummarizeData(Semantic) [4]。 - Native Function (原生技能):
- 定義:使用 C# 等傳統程式語言編寫的函數。它們是確定性的,執行傳統的程式邏輯。
- 職責:主要用於執行需要精確控制、外部系統互動或大量數據處理的任務,例如:
- 查詢 SQL 資料庫或 NoSQL 資料庫。
- 呼叫 RESTful API 或 gRPC 服務。
- 執行檔案讀寫操作。
- 進行複雜的數學計算或數據轉換。
- 優點:結果確定、效能高、可進行完整的單元測試和整合測試。
- Semantic Function (語意技能):
- 定義:由一個或多個提示詞模板(Prompt Template)定義,利用 LLM 的能力來完成任務。它們是非確定性的,結果會因 LLM 的生成能力而異。
- 職責:主要用於處理需要語意理解、內容生成或格式轉換的任務,例如:
- 總結長篇文本。
- 根據數據生成自然語言報告。
- 將 JSON 數據轉換為易讀的 Markdown 格式。
- 進行情感分析或意圖識別。
- 優點:處理模糊需求能力強、開發速度快、可快速迭代。
- KernelArguments:這是一個用於在 Functions 之間傳遞狀態和參數的物件。它類似於一個字典,可以儲存多個鍵值對,供不同的 Functions 讀取和寫入 [5]。
2.2 混合工作流設計:Native + Semantic Function 串聯
本文件的核心目標是演示如何將 Native Function 與 Semantic Function 串聯起來,構建一個從資料庫查詢到報告生成的自動化工作流。以下是一個典型的串聯邏輯:
- 使用者輸入 (User Input):使用者向 AI 應用程式提出一個高層次的需求,例如:「請生成上週的銷售數據週報。」
- AI 意圖識別與規劃 (AI Intent Recognition & Planning):如果應用程式使用了 Planner(規劃器),AI 會分析使用者意圖,並自動規劃出一個執行計畫,決定需要呼叫哪些 Functions 以及它們的順序。即使沒有 Planner,開發者也可以手動編排 Functions 的執行順序。
- Native Function 執行 (Data Retrieval):
- 首先,Kernel 會呼叫一個 Native Function,例如
SalesDataPlugin.GetWeeklySalesData(startDate, endDate)。這個 Native Function 會連接到後端 SQL Server 資料庫,執行一個精確的 SQL 查詢,獲取上週的銷售數據。數據通常以結構化的 JSON 格式返回。 - Native Function 將查詢到的 JSON 數據儲存到
KernelArguments中,作為後續 Functions 的輸入。
- 首先,Kernel 會呼叫一個 Native Function,例如
- Semantic Function 執行 (Report Generation):
- 接著,Kernel 會呼叫一個 Semantic Function,例如
ReportPlugin.GenerateWeeklyReport(salesDataJson)。這個 Semantic Function 的提示詞模板會被設計成接收 JSON 格式的銷售數據,並指示 LLM 將其轉化為一份易讀、摘要性的週報(例如 Markdown 格式)。 - LLM 根據提示詞和
KernelArguments中提供的銷售數據,生成最終的週報內容。
- 接著,Kernel 會呼叫一個 Semantic Function,例如
- 輸出呈現 (Output Presentation):應用程式將 LLM 生成的週報內容呈現給使用者。
這種串聯模式充分利用了 Native Function 的精確性和 Semantic Function 的語意理解能力,實現了數據驅動的智能內容生成。
3. Prerequisites
要實作 Semantic Kernel 應用程式,特別是結合 Native Function 與 Semantic Function,您需要具備以下環境和知識:
- .NET 9 SDK:確保您的開發環境已安裝 .NET 9 SDK 或更高版本。Semantic Kernel 與 .NET 生態系統緊密整合。
- C# 程式設計知識:熟悉 C# 語言特性、非同步程式設計(
async/await)和物件導向程式設計。 - LLM 服務:需要一個可用的 LLM 服務,例如 Azure OpenAI Service、OpenAI API 或其他支援的 LLM 提供商。您需要相應的 API 金鑰和端點。
- Semantic Kernel NuGet 套件:安裝
Microsoft.SemanticKernel及其相關套件。 - 資料庫知識:如果 Native Function 需要與資料庫互動,您需要了解 SQL Server 或其他資料庫的基本操作。
- Visual Studio 或 VS Code:推薦使用這些 IDE 進行 .NET 開發。
4. Implementation / Code Example
本節將提供一個使用 C# 和 .NET 9 實作 Semantic Kernel 混合 Native Function 與 Semantic Function 的範例。我們將模擬一個從資料庫獲取銷售數據,然後生成週報的場景。
4.1 專案初始化
首先,創建一個新的 .NET Console 專案並安裝必要的 NuGet 套件:
dotnet new console -n SemanticKernelHybridDemo
cd SemanticKernelHybridDemo
dotnet add package Microsoft.SemanticKernel
dotnet add package Microsoft.SemanticKernel.Connectors.OpenAI # 根據您使用的 LLM 選擇連接器
dotnet add package Microsoft.Extensions.Configuration.Json # 用於配置管理
dotnet add package Microsoft.Extensions.Configuration.EnvironmentVariables
dotnet add package System.Data.SqlClient # 模擬 SQL Server 連接
4.2 配置 LLM 服務
創建 appsettings.json 檔案來儲存您的 LLM 服務配置:
{
"OpenAI": {
"ModelId": "gpt-4o",
"ApiKey": "YOUR_OPENAI_API_KEY"
}
}
或者使用環境變數來儲存 OPENAI_API_KEY。
4.3 定義 Native Function (SalesDataPlugin.cs)
這個 Native Function 模擬從 SQL Server 獲取銷售數據。在實際應用中,這裡會使用 Entity Framework Core 或 ADO.NET 進行真實的資料庫操作。
using Microsoft.SemanticKernel;
using System.ComponentModel;
using System.Text.Json;
public class SalesDataPlugin
{
[KernelFunction, Description("從資料庫獲取指定日期範圍內的銷售數據。")]
public async Task<string> GetWeeklySalesData(
[Description("開始日期,格式為 YYYY-MM-DD")] string startDate,
[Description("結束日期,格式為 YYYY-MM-DD")] string endDate)
{
// 模擬從 SQL Server 查詢數據
// 在實際應用中,這裡會是真實的資料庫查詢邏輯
Console.WriteLine($"\n[Native Function] 正在從資料庫獲取 {startDate} 到 {endDate} 的銷售數據...");
// 模擬數據庫查詢結果
var salesData = new List<object>
{
new { Product = "筆記型電腦", Quantity = 150, Revenue = 150000.00, Date = "2026-02-03" },
new { Product = "智慧型手機", Quantity = 200, Revenue = 120000.00, Date = "2026-02-04" },
new { Product = "平板電腦", Quantity = 80, Revenue = 40000.00, Date = "2026-02-05" },
new { Product = "智慧手錶", Quantity = 300, Revenue = 60000.00, Date = "2026-02-06" },
new { Product = "耳機", Quantity = 500, Revenue = 25000.00, Date = "2026-02-07" }
};
await Task.Delay(500); // 模擬網路延遲
var jsonOptions = new JsonSerializerOptions { WriteIndented = true };
string jsonResult = JsonSerializer.Serialize(salesData, jsonOptions);
Console.WriteLine("[Native Function] 銷售數據獲取完成。");
return jsonResult;
}
}
4.4 定義 Semantic Function (ReportPlugin)
Semantic Function 通常由一個提示詞模板文件 (skprompt.txt) 和一個配置文件 (config.json) 組成。為了簡化範例,我們直接在程式碼中定義提示詞。
// ReportPlugin.cs (或直接在 Program.cs 中定義)
public static class ReportPlugin
{
public static string GenerateWeeklyReportPrompt = @"
你是一個專業的商業分析師,請根據提供的 JSON 格式銷售數據,生成一份簡潔、易讀的週報。
週報應包含以下部分:
1. 總銷售額和總銷售量。
2. 表現最佳的產品(銷售額最高)。
3. 任何值得注意的趨勢或觀察。
請以 Markdown 格式輸出報告。
銷售數據 (JSON 格式):
{{$input}}
週報:
";
}
4.5 串聯 Native 與 Semantic Function (Program.cs)
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using Microsoft.Extensions.Configuration;
using System.Text.Json;
public class Program
{
public static async Task Main(string[] args)
{
// 1. 配置 Kernel
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var builder = Kernel.CreateBuilder();
// 根據配置添加 LLM 服務
if (configuration["OpenAI:ApiKey"] is string openAIApiKey && !string.IsNullOrEmpty(openAIApiKey))
{
builder.AddOpenAIChatCompletion(
modelId: configuration["OpenAI:ModelId"] ?? "gpt-4o",
apiKey: openAIApiKey);
}
else
{
Console.WriteLine("請在 appsettings.json 或環境變數中配置 OpenAI:ApiKey。");
return;
}
Kernel kernel = builder.Build();
// 2. 導入 Native Function Plugin
kernel.ImportPluginFromObject(new SalesDataPlugin(), "SalesDataPlugin");
Console.WriteLine("Native Function Plugin 'SalesDataPlugin' 導入成功。");
// 3. 創建 Semantic Function
var generateReportFunction = kernel.CreateFunctionFromPrompt(
ReportPlugin.GenerateWeeklyReportPrompt,
functionName: "GenerateWeeklyReport",
description: "根據銷售數據生成週報"
);
Console.WriteLine("Semantic Function 'GenerateWeeklyReport' 創建成功。");
// 4. 定義日期範圍
string startDate = "2026-02-03";
string endDate = "2026-02-07";
// 5. 執行工作流:Native Function -> Semantic Function
Console.WriteLine("\n--- 開始生成週報工作流 ---");
// 步驟 1: 呼叫 Native Function 獲取銷售數據
var salesDataJson = await kernel.InvokeAsync<string>(
"SalesDataPlugin",
"GetWeeklySalesData",
new KernelArguments { { "startDate", startDate }, { "endDate", endDate } }
);
Console.WriteLine("\n[Main] 獲取到的銷售數據 (JSON):\n" + salesDataJson);
// 步驟 2: 呼叫 Semantic Function 生成報告
var report = await kernel.InvokeAsync<string>(
generateReportFunction,
new KernelArguments { { "input", salesDataJson } } // 將 Native Function 的輸出作為 Semantic Function 的輸入
);
Console.WriteLine("\n--- 生成的週報 ---");
Console.WriteLine(report);
Console.WriteLine("\n--- 週報生成工作流完成 ---");
}
}
4.6 運行專案
確保您的 appsettings.json 中的 OpenAI:ApiKey 已正確配置,然後運行:
dotnet run
您將看到 Native Function 模擬查詢數據,然後 Semantic Function 利用這些數據生成一份格式化的週報。
5. Parameters / API Reference
Semantic Kernel 的核心 API 圍繞著 Kernel 類別及其相關方法,以及用於定義 Functions 的屬性。
5.1 Kernel 類別核心方法
| 方法/屬性 | 描述 |
|---|---|
Kernel.CreateBuilder() | 創建一個 KernelBuilder 實例,用於配置和構建 Kernel。 |
AddOpenAIChatCompletion() | 添加 OpenAI Chat Completion 服務。 |
ImportPluginFromObject() | 從一個 C# 物件導入 Native Functions 作為 Plugin。 |
CreateFunctionFromPrompt() | 從提示詞模板創建一個 Semantic Function。 |
InvokeAsync<T>() | 異步執行一個 Function,並返回指定類型 T 的結果。 |
5.2 Native Function 相關屬性
| 屬性 | 描述 |
|---|---|
[KernelFunction] | 標記一個 C# 方法為 Kernel 可調用的 Native Function。 |
[Description(string)] | 為 Function 或其參數提供描述,有助於 AI Planner 理解其用途。 |
5.3 KernelArguments 類別
| 方法/屬性 | 描述 |
|---|---|
new KernelArguments() | 創建一個新的參數集合。 |
Add(key, value) | 添加一個鍵值對,用於在 Functions 之間傳遞數據。 |
6. Notes & Best Practices
- 安全性:
- 輸入驗證:對所有傳入 Native Function 的參數進行嚴格的輸入驗證,防止 SQL 注入、路徑遍歷等攻擊。不要盲目信任 AI 生成的參數 [6]。
- 最小權限原則:Native Function 存取外部系統(如資料庫、API)時,應使用具有最小必要權限的憑證。
- 敏感資訊處理:避免在 Semantic Function 的提示詞中直接包含敏感資訊。如果需要處理敏感數據,應在 Native Function 中進行處理或脫敏。
- 錯誤處理:
- 在 Native Function 中實作健壯的錯誤處理機制,捕獲資料庫連接失敗、API 調用錯誤等異常,並向 Kernel 返回清晰的錯誤訊息。
- 考慮在 Semantic Function 的提示詞中加入錯誤處理指令,指導 LLM 在遇到無效數據時如何回應。
- 提示詞工程:
- 清晰明確:Semantic Function 的提示詞應清晰、明確地指示 LLM 的任務和預期輸出格式(例如 Markdown、JSON)。
- Few-shot 範例:對於複雜的 Semantic Function,提供 Few-shot 範例可以顯著提高 LLM 的輸出品質和一致性。
- 變數命名:在提示詞中使用有意義的變數名(例如
{{$input}}而非{{$data}}),以提高可讀性。
- 性能優化:
- 非同步操作:Native Function 中涉及 I/O 操作(如資料庫查詢、網路請求)應使用非同步方法,以避免阻塞主執行緒。
- 快取機制:對於頻繁查詢且數據變化不大的 Native Function,考慮實作快取機制,減少對後端系統的負載。
- 模組化與可擴展性:
- 將相關的 Native Functions 和 Semantic Functions 組織成獨立的 Plugins,提高程式碼的模組化和可重用性。
- 利用 .NET 的依賴注入(Dependency Injection)機制來管理 Kernel 和 Plugins 的生命週期,方便測試和維護。
7. 為什麼選擇這種方式?
在構建企業級 AI 應用程式時,混合使用 Semantic Kernel 的 Native Function 與 Semantic Function 是一種極其強大且高效的架構模式,其優勢顯而易見:
- 實現 AI 的「精確」與「智慧」結合:Native Function 提供了傳統程式碼的精確性、確定性和對外部系統的控制能力,確保了數據獲取和邏輯執行的可靠性。而 Semantic Function 則賦予了應用程式 LLM 的語意理解、內容生成和靈活推理的智慧。兩者結合,使得 AI 應用既能處理複雜的結構化數據,又能生成富有洞察力的自然語言內容 [1]。
- 提升開發效率與靈活性:開發者可以將重複性高、邏輯固定的任務封裝為 Native Function,而將需要 LLM 參與的模糊、創意性任務定義為 Semantic Function。這種分工使得開發過程更加高效,同時也提供了極大的靈活性,可以根據業務需求快速調整或替換 Functions [2]。
- 優化資源利用與成本控制:將確定性任務交由 Native Function 處理,可以減少對 LLM 的不必要調用,從而降低 LLM API 的使用成本。只有在真正需要語意理解或內容生成時才呼叫 Semantic Function,實現資源的優化配置。
- 構建可擴展且可維護的 AI 應用:Semantic Kernel 的 Plugin 和 Function 抽象層,鼓勵模組化設計。這使得應用程式的各個部分可以獨立開發、測試和部署,提高了整體的可擴展性和可維護性。當底層 LLM 模型或資料庫結構發生變化時,只需更新相應的 Function 或 Plugin,而不會影響整個系統 [4]。
- 充分利用 .NET 生態系統優勢:對於 .NET 開發者而言,Semantic Kernel 提供了與 C# 和 .NET 9 的深度整合。這意味著可以利用現有的 .NET 工具、庫和開發經驗,快速構建企業級 AI 應用,無需學習全新的技術棧,降低了學習曲線和開發門檻 [7]。
參考資料
- [1] Microsoft Learn. (2024, November 8). How to quickly start with Semantic Kernel. Retrieved from https://learn.microsoft.com/en-us/semantic-kernel/get-started/quick-start-guide
- [2] Microsoft Learn. (2024, December 10). Plugins in Semantic Kernel. Retrieved from https://learn.microsoft.com/en-us/semantic-kernel/concepts/plugins/
- [3] NuGet. (n.d.). Microsoft.SemanticKernel 1.70.0. Retrieved from https://www.nuget.org/packages/Microsoft.SemanticKernel
- [4] jamiemaguire.net. (2024, July 5). Semantic Kernel: Implementing Native Functions and Plugins. Retrieved from https://jamiemaguire.net/index.php/2024/07/05/semantic-kernel-implementing-native-functions-and-plugins/
- [5] GitHub. (n.d.). Chaining native & semantic functions using MS Semantic Kernel. Retrieved from https://github.com/RobertEichenseer/OpenAI.SemanticNativeFunctions
- [6] YouTube. (2024, March 5). OpenAI Function Calling with Semantic Kernel, C#, & Entity Framework Core. Retrieved from https://www.youtube.com/watch?v=4sKRwflEyHk
- [7] Medium. (2025, February 27). Building AI-Powered .NET Applications with Semantic Kernel. Retrieved from https://medium.com/@aschultzme/building-ai-powered-net-applications-with-semantic-kernel-0cacd0c43877