可能很多人不知道,OpenAI 有一個 Assistant Agent 的功能,它是由 OpenAI 平台託管的 AI Agent,能夠在雲端長期運作,並與使用者或其他系統進行互動,它本身內建多項工具,例如:程式碼解譯器、文件檢索等功能,並且對話狀態是由 OpenAI 平台自動維護,讓開發者省去自行管理上下文的麻煩。實作上是基於 OpenAI Assistant API ,相較於傳統 ChatCompletion Agent,OpenAI Assistant Agent 是屬於更高階的實作。
更多人可能以為 Semantic Kernel 只能撰寫本地端的 Agent,事實上,Semantic Kernel 也支援與 OpenAI Assistant Agent 進行整合,這篇文章會示範如何使用 Semantic Kernel 建立 OpenAI Assistant Agent,並且實作一個智能客服系統,支援查詢訂單狀態與產品資訊咨詢。
首先,請確保你的專案已經安裝了以下 NuGet 套件:
dotnet add package Microsoft.SemanticKernel.Agents.OpenAI --prerelease
AssistantClient
物件,這裡需要提供 API Key。AssistantClient assistantClient = new AssistantClient(new ApiKeyCredential(Config.OpenAI_ApiKey));
Assistant
物件,並且指定使用的 LLM Model ID。Assistant assistant =
await assistantClient.CreateAssistantAsync(
Config.ModelId,
name: "SupportAgent",
instructions: @"你是一位專業且有禮貌的 AI 客服專員,負責協助顧客查詢訂單狀態與產品資訊。請依照以下規則提供回覆:
1. 如果顧客詢問訂單狀態,請引導顧客提供訂單編號,並使用已提供的查詢工具(如:GetOrderStatus)查詢對應資料。
2. 如果顧客詢問產品資訊,請根據產品名稱,並使用產品查詢工具(如:GetProductInfo)提供詳細資訊。
3. 回覆時要友善、清楚,避免使用過於技術化的語言。
4. 若問題無法直接回答,請告知顧客「我會轉交給專人協助」,並避免捏造答案。
5. 僅限提供查詢訂單狀態與產品資訊,若超出職責範圍,請禮貌回覆並引導顧客聯繫客服專線0800-888-888。");
private static readonly Dictionary<string, OrderInfo> Orders = new(StringComparer.OrdinalIgnoreCase)
{
["A12345"] = new OrderInfo("A12345", "已出貨", new DateTime(2025, 10, 7, 14, 35, 0), "黑貓宅急便", "TRK123456789"),
["B77788"] = new OrderInfo("B77788", "備貨中", new DateTime(2025, 10, 8, 9, 20, 0), "店到店", "TRK987654321"),
["C00999"] = new OrderInfo("C00999", "已送達", new DateTime(2025, 10, 5, 17, 5, 0), "郵局", "TRK555666777")
};
private static readonly Dictionary<string, ProductInfo> Products = new(StringComparer.OrdinalIgnoreCase)
{
["智慧溫控杯"] = new ProductInfo("智慧溫控杯", 599, "恆溫保溫 6 小時,附智慧提醒。", 24, new[] { "Type-C 快速充電", "可自訂 40-60°C", "食品級不鏽鋼" }),
["降噪藍牙耳機"] = new ProductInfo("降噪藍牙耳機", 2590, "雙晶片主動降噪,支援多裝置切換。", 12, new[] { "通話降噪 AI 演算法", "連續播放 30 小時", "IPX4 防潑水" }),
["極輕登山背包"] = new ProductInfo("極輕登山背包", 1890, "容量 32L,輕量透氣背負系統。", 5, new[] { "超纖維耐磨面料", "快取側袋", "附防雨罩" })
};
[KernelFunction]
[Description("Get the status of an order.")]
public static string GetOrderStatus(
[Description("Order ID")] string orderId)
{
if (!Orders.TryGetValue(orderId, out var order))
{
return $"目前查無訂單 {orderId},請確認訂單編號是否正確。";
}
return $"訂單 {order.OrderId} 狀態:{order.Status}\n最近更新時間:{order.LastUpdate:yyyy-MM-dd HH:mm}\n配送方式:{order.ShippingMethod}\n貨運追蹤碼:{order.TrackingNumber}";
}
[KernelFunction]
[Description("Get the product information.")]
public static string GetProductInfo(
[Description("Product Name")] string productName)
{
if (!Products.TryGetValue(productName, out var product))
{
return $"目前查無產品「{productName}」,您可以提供更完整的名稱嗎?";
}
var highlights = product.Highlights.Length == 0
? string.Empty
: "\n特色亮點:\n - " + string.Join("\n - ", product.Highlights);
return $"{product.Name}\n建議售價:NT${product.Price:N0}\n現貨庫存:{product.Stock} 件\n商品描述:{product.Description}{highlights}";
}
private sealed record OrderInfo(string OrderId, string Status, DateTime LastUpdate, string ShippingMethod, string TrackingNumber);
private sealed record ProductInfo(string Name, int Price, string Description, int Stock, string[] Highlights);
KernelPlugin plugin = KernelPluginFactory.CreateFromType<CustomerSupportPlugin>();
// 建立 OpenAI Assistant Agent並加入Plugin
var agent = new OpenAIAssistantAgent(assistant, assistantClient, [plugin]);
// Create a thread for the agent conversation.
OpenAIAssistantAgentThread agentThread = new(assistantClient);
Console.Write("User > ");
string? userInput;
while ((userInput = Console.ReadLine()) is not null)
{
if (string.IsNullOrWhiteSpace(userInput) || userInput.Equals("exit", StringComparison.OrdinalIgnoreCase))
break;
ChatMessageContent message = new(AuthorRole.User, userInput);
bool isFirst = false;
await foreach (StreamingChatMessageContent response in agent.InvokeStreamingAsync(message, agentThread))
{
//追蹤 function 調用
if (string.IsNullOrEmpty(response.Content))
{
StreamingFunctionCallUpdateContent? functionCall = response.Items.OfType<StreamingFunctionCallUpdateContent>().SingleOrDefault();
if (!string.IsNullOrEmpty(functionCall?.Name))
{
Console.WriteLine($"\n# trace {response.Role} - {response.AuthorName ?? "*"}: FUNCTION CALL - {functionCall.Name}");
}
continue;
}
if (!isFirst)
{
Console.Write($"{response.Role} - {response.AuthorName ?? "*"} > ");
isFirst = true;
}
Console.Write($"{response.Content}");
}
Console.WriteLine();
Console.WriteLine($"\n# trace chat thread with agent: {agent.Name} - {agent.Description},threadId: {agentThread.Id} \n");
Console.Write("User > ");
}
User > 我想詢問關於訂單的問題
assistant - SupportAgent > 很感謝您的來訊!為了協助您查詢訂單狀態,請提供您的訂單編號。收到後我會立即為您查詢相關資料。
# trace chat thread with agent: SupportAgent - ,threadId: thread_fkGsqKWVQicG40EcvrdTU4KU
User > C00999
# trace assistant - SupportAgent: FUNCTION CALL - CustomerSupportPlugin-GetOrderStatus
assistant - SupportAgent > 您的訂單(編號:C00999)目前的狀態為:已送達。
最近的更新時間是 2025-10-05 17:05。配送方式是郵局,貨運追蹤碼為 TRK555666777。
如需查看包裹詳細運送進度,歡迎使用上述追蹤碼至郵局網站查詢。如果有其他問題,也歡迎再隨時告訴我!
# trace chat thread with agent: SupportAgent - ,threadId: thread_fkGsqKWVQicG40EcvrdTU4KU
User > 那另一件 C00988 呢
# trace assistant - SupportAgent: FUNCTION CALL - CustomerSupportPlugin-GetOrderStatus
assistant - SupportAgent > 目前查無訂單編號 C00988,請您確認一下訂單編號是否正確。如果有需要,也可以重新提供訂單編號,我會很樂意再次為您查詢!
# trace chat thread with agent: SupportAgent - ,threadId: thread_fkGsqKWVQicG40EcvrdTU4KU
User > 我想問一下降噪藍牙耳機這個產品
# trace assistant - SupportAgent: FUNCTION CALL - CustomerSupportPlugin-GetProductInfo
assistant - SupportAgent > 降噪藍牙耳機目前建議售價為 NT$2,590,現貨庫存有 12 件。
產品特色如下:
- 採用雙晶片主動降噪技術,能有效隔絕環境雜音
- 支援多裝置切換,讓使用更加彈性
- 具備通話降噪 AI 演算法,通話品質更清晰
- 連續播放最長可達 30 小時
- 擁有 IPX4 防潑水等級,日常使用更放心
如需進一步了解或有購買需求,歡迎隨時詢問!
透過這個範例,我們展示了如何使用 Semantic Kernel 整合 OpenAI Assistant Agent,並且實作一個智能客服系統,支援查詢訂單狀態與產品資訊咨詢。因此可以具體的理解到 Semantic Kernel 不僅能夠建立自已打造的 Agent,也可以整合雲端的 Agent 服務,讓開發者可以依照需求選擇最適合的方案來實作 AI 應用。
下一篇文章會介紹如何使用 Semantic Kernel 整合 Azure AI Foundry 的 Agent 服務,敬請期待!