iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0

經過了前幾天的開胃菜,我們已經成功地把頂級廚房建立起來,也讓我們的 Semantic Kernel (SK) 大廚說出了第一句話。但是,如果我們的大廚跟金魚一樣只有七秒的記憶,那這頓「AI 全餐」恐怕會吃得相當辛苦。

想像一下,你走進一家高級餐廳,服務生親切地問候你。

你:「你好,我想要一份菲力牛排。」
服務生:「好的,一份菲力牛排。」

(服務生轉身,又轉回來)

服務生: 「您好,請問需要什麼服務?」
:「...我剛剛不是點了嗎?」
服務生:「不好意思,請問您是誰?」

這也太讓人崩潰了吧!一個好的服務生,絕對會記得客人的喜好和剛剛的對話。同樣地,一個聰明的 AI 應用,也必須要有「記憶」,才能提供有溫度、有連貫性的服務。

今天,我們就要來介紹 AI 的記憶本——ChatHistory


服務生的記憶法寶:ChatHistory

在 Semantic Kernel 的世界裡,ChatHistory 物件就是那位服務生的神奇筆記本,它負責記錄下顧客(使用者)與廚師(AI)之間的每一句對話。少了它,每一輪的互動都是一次全新的開始,AI 無法理解上下文,自然也無法提供真正有幫助的回應。

ChatHistory 本質上是一個列表(List),專門用來存放 ChatMessageContent 物件,讓新增和管理對話紀錄變得非常簡單 。

筆記本上的四種角色

在我們的對話紀錄中,不是只有你一言我一語,還需要區分是誰說了什麼話。

ChatHistory 定義了幾種不同的角色(AuthorRole),讓我們的筆記本更加清晰 :

  1. System (系統):這是給 AI 的「聖旨」或「人設」。在對話開始前,你可以先用系統訊息告訴 AI 它的角色是什麼,例如「你是一位樂於助人的小幫手」或「你是一位說話帶點嘲諷的詩人」。這為整個對話定下了基調。
  2. User (使用者):這就是你我,也就是顧客所說的話。
  3. Assistant (助理):這是 AI 的回覆。
  4. Tool (工具):當 AI 使用了外部工具(也就是我們後面會提到的 Plugins)後,工具回傳的結果會用這個角色記錄下來。

讓我們來看看怎麼把這些對話加進 ChatHistory 裡:

using Microsoft.SemanticKernel.ChatCompletion;

// 建立一個新的對話紀錄
ChatHistory chatHistory = [];

// 1. 加入系統訊息,為 AI 設定角色
chatHistory.AddSystemMessage("你是一位樂於助人的餐廳點餐助理。");

// 2. 加入使用者的問題
chatHistory.AddUserMessage("菜單上有什麼推薦的嗎?");

// 3. 加入 AI 的回覆
chatHistory.AddAssistantMessage("我們有披薩、義大利麵和沙拉。請問您想點什麼呢?");

// 4. 使用者繼續對話
chatHistory.AddUserMessage("我想要第一個選項,謝謝。");

程式碼範例改寫自

就是這麼簡單!當你把這個 chatHistory 物件傳遞給 AI 服務時,它就能看到完整的對話歷史,並理解「第一個選項」指的就是「披薩」。這就是上下文連貫性的魔力!

主廚的秘密武器:手動模擬函式呼叫

有時候,我們不希望浪費時間讓 AI 去問一些我們已經知道的資訊。例如,在點餐系統中,我們可能已經透過登入系統知道了顧客的過敏原。這時候,我們可以在 AI 還沒開口問之前,就主動把這些資訊「塞」進對話紀錄裡,引導 AI 做出更精準的回應。

這就像是服務生在把訂單交給廚師前,先在筆記本上加註:「注意!此顧客對花生和麩質過敏!」

我們可以使用

FunctionCallContentFunctionResultContent 來手動模擬這個過程 。

  • FunctionCallContent:模擬 AI 決定要去呼叫某個工具。
  • FunctionResultContent:模擬工具回傳的結果

讓我們看看如何手動將「查詢使用者過敏原」這個動作加入歷史紀錄中:

// 模擬 AI 助理決定呼叫工具來查詢過敏原
chatHistory.Add(
    new() {
        Role = AuthorRole.Assistant,
        Items = [
            new FunctionCallContent(
                functionName: "get_user_allergies",
                pluginName: "UserPlugin",
                callId: "call_001" // 這個 id 很重要,用來配對結果
            )
        ]
    }
);

// 模擬工具回傳了查詢結果
chatHistory.Add(
    new() {
        Role = AuthorRole.Tool,
        Items = [
            new FunctionResultContent(
                functionName: "get_user_allergies",
                pluginName: "UserPlugin",
                callId: "call_001", // 必須和 FunctionCallContent 的 id 相對應
                result: "{ \"allergies\": [\"peanuts\", \"gluten\"] }"
            )
        ]
    }
);

程式碼範例改寫自

透過這段「預寫」的對話,當使用者點了含有花生的餐點時,AI 就會立刻知道並提醒:「先生,這道菜含有花生,根據您的過敏紀錄,建議您更換餐點。」是不是非常貼心?這項技巧對於在對話中注入重要的外部資訊特別有用,能讓 AI 的反應更即時、更個人化 。


今天我們學會了如何使用 ChatHistory 這個強大的記憶工具,讓我們的 AI 應用從一個健忘的服務生,蛻變成一位能記住顧客大小事的貼心管家。透過記錄不同角色的訊息,並善用模擬函式呼叫的技巧,我們能打造出更有溫度、更懂人心的對話體驗。

有了記憶,我們的大廚才算是真正準備好了。明天,我們將端上這場 AI 滿漢全席的重頭戲之一:Function Calling!我們將會看到,這位擁有記憶的大廚,是如何真正「動」起來,開始使用他的刀具(Plugins)來為我們料理真正的美味佳餚!敬請期待!


完整程式碼範例


上一篇
Day 6: 廚師的刀具組:初探 Plugins,讓你的 AI 不再空談
下一篇
Day 8: 主廚的招牌絕活:Function Calling 讓 AI 動起來!
系列文
AI 全餐,好吃嗎?用 Semantic Kernel 打造你的客製化滿漢全席!8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言