iT邦幫忙

2026 iThome 鐵人賽

DAY 5
0
AI 自動化

從單一 Agent 到 Supervisor 架構:用 n8n 實作會自己分工的 Multi-Agent AI 部門系列 第 5

Day 05|給 Agent 記憶:Simple Memory 的 Session ID 該怎麼設

  • 分享至 

  • xImage
  •  

昨天的單兵 Agent 接了 Simple Memory,Session ID 直接填了 thread_id,沒有多解釋。今天用實驗把這個選擇講清楚,順便分享一個我在做實驗時被記憶坑到的經驗。

Memory 實際上做了什麼

n8n 的 Memory 插槽做的事其實很單純:每次呼叫模型之前,把同一個 Session 過去幾輪的對話,照順序插在這次的訊息前面。

以 Context Window Length 設 5 為例,送給模型的內容大概是這樣排的:

[system]    System Message
[human]     第 1 輪客人的信
[ai]        第 1 輪 Agent 的回信
...         最多保留最近 5 輪
[human]     這一次客人的信

它不會做摘要,也不會判斷哪些內容重要,只是把歷史對話原樣貼上去。所以有幾件事是必然的:記得越多輪,每次呼叫的 prompt 就越長;歷史對話裡如果有錯,模型會把它當成前提繼續用。

Simple Memory 的資料存在 n8n 的記憶體裡,n8n 重啟就清空。正式上線通常會換成 Postgres Chat Memory 或 Redis Chat Memory 這類外部儲存,Session ID 的設計邏輯是一樣的。

實驗:三種 Session ID 設定

在 Simple Memory 裡,Session ID 選 Define below,Key 可以填不同的值。我比較了三種:

設定 Key 效果
thread {{ $json.thread_id }} 同一串信共用記憶
email {{ $json.from_email }} 同一位客人的所有信共用記憶
none 不接 Memory 每封信都從零開始

測試用 6 封信,分成三個情境,每個情境都是兩封信連續寄進來:

情境 第一封 第二封
M1 換話題 王小明:A10293 什麼時候會到? 王小明另開一封新信:你們的器材保固多久?
M2 補資料 王小明:我上禮拜買的東西什麼時候會到? 同一串信回覆:A10293
M3 追問 李小美:A10294 出貨了嗎? 同一串信回覆:那貨號是多少?

M1 想看舊話題會不會混進新信,M2 想看客人補上編號之後接不接得上,M3 想看它懂不懂「那」指的是哪張訂單。

結果

設定 M1 換話題 M2 補資料 M3 追問
thread 新信沒有提到舊訂單 正確查到 A10293 A10294 備貨中,尚未產生物流編號
email 新信沒有提到舊訂單 正確查到 A10293 A10294 備貨中,尚未產生物流編號
none 新信沒有提到舊訂單 正確查到 A10293 請客人提供訂單編號

M3 才是真正需要記憶的情境。第二封信只有一句「那貨號是多少?」,沒有記憶的版本根本不知道客人在問哪一張單。有記憶的兩個版本不但接上了,還正確回答「尚未產生物流編號」,沒有自己編一個貨號出來。

M2 反而用不到記憶,因為客人第二封信直接寫了 A10293,光看這封信就能查單。

用 email 當 Key 的問題不在內容,在成本

我原本以為用 email 當 Key,客人問保固的新信會冒出「關於您先前詢問的訂單 A10293」這種話。實際跑下來,gemma4:cloud 並沒有把舊話題混進來。

問題出在 prompt 的長度。下表是王小明那 4 封信,每封信所有模型呼叫合計送出的 prompt tokens:

thread email none
M1 第一封 1,974 1,974 1,973
M1 第二封(新信) 852 1,250 852
M2 第一封(新信) 854 1,349 854
M2 第二封(同一串) 2,172 3,155 1,966

以 email 為 Key 時,這位客人所有的信都累積在同一段記憶裡。明明是一封毫不相關的新信,也得把前面幾輪對話一起送出去。同樣是 M2 的第二封,email 版本比 thread 版本多了將近 1,000 個 tokens。客人寫信越勤快,這個數字就越大。

還有一個小地方:M1 第二封問保固,thread 版本回「您好」,email 版本回「王先生您好」,把前一封信的稱呼帶過來了。

我在實驗時被記憶坑了一次

做 Day 04 的比較實驗時,我先用 50 封評估信跑了一輪單兵 Agent,接著又挑其中 5 封做另一組對照。結果第二組的 prompt 莫名多出 100 到 500 個 tokens。

查了 Execution 才發現,兩次實驗用的是同一個 workflow、同樣的 thread_id。Simple Memory 會跨執行保留對話,所以第二次跑的時候,模型看得到第一次實驗裡自己寫過的回信。

這次運氣好,兩次問的是同樣的問題,回信內容看不出差異。但如果第一次的回答是錯的,第二次就會帶著錯誤前提繼續回答,而且從輸出上完全看不出來。後來我把測試腳本改成每一輪自動換新的 thread_id,那組實驗也重跑了。

這次也順便確認了一件事:Simple Memory 是以 workflow 為單位隔開的。換一個 workflow,就算 Key 一樣,也讀不到另一個 workflow 的記憶。

長期記憶該放哪

「王小明是 VIP、偏好淺焙、之前抱怨過包裝」這種資訊,不適合塞進 Memory 插槽。比較好的做法是做成一張可以查詢的表,再包成工具。

做法跟 Day 04 的訂單一樣:建一張 haodou_customers Data table,欄位是 email、name、tier、preference、notes、updated_at,再接一個 Data table Tool 用 email 查詢。Day 11 的查單專員會用到它。

用表格而不是讓 AI 自己寫客人摘要,原因很實際:表格內容看得到、查得到,寫錯了可以手動改。

客服情境的建議

客服信件我會用 thread_id。它剛好對應人對「一件事」的直覺:同一串往來是同一件事,客人另開新信,記憶就自然重新開始。

email 當 Key 在客服情境下我不建議,成本會跟著客人寄信的次數一路累積。至於完全不接 Memory,適合每次輸入就包含完整資訊的任務;只要客人會用「那個」「另外那筆」追問,就需要記憶。

到了 Multi-Agent 架構,情況又不太一樣。專員收到的是主管整理好的工單,大多數專員根本不需要記憶,這在 Day 23 會實測。

小結

Memory 就是把歷史對話貼在 prompt 前面。三種 Session ID 設定裡,只有追問的情境真正需要記憶;用 email 當 Key 雖然沒有混淆話題,但 prompt 會隨著來信次數一路變長。另外,重跑測試記得換 Session ID,不然模型會看到上一次的回答。

第一週的最主要的部分在明天:10 封刻意設計的壓力測試信,每封跑 3 輪,看單兵 Agent 到底在哪裡出問題。


上一篇
Day 04|第一版單兵 Agent:一個 Agent 處理所有客服信件
下一篇
Day 06|壓力測試:10 封刻意設計的信,單兵 Agent 其實比想像中強
系列文
從單一 Agent 到 Supervisor 架構:用 n8n 實作會自己分工的 Multi-Agent AI 部門9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言