iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
AI 自動化

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

Day 04|第一版單兵 Agent:一個 Agent 處理所有客服信件

  • 分享至 

  • xImage
  •  

今天做出第一版客服 Agent:一個 AI Agent 負責讀信、查訂單、寫回信。它是整個系列的對照組,之後所有的改良都要跟它比。

訂單資料放哪裡:Data table

教學常用 Google Sheets 當資料庫,但要先做 OAuth 授權,對於想快速試做的人來說門檻有點高。n8n 2.x 內建的 Data table 可以直接在 workflow 裡建表、查詢,資料跟 n8n 放在一起,測試起來方便很多。

我先建一個一次性的 workflow 把訂單寫進去,節點順序是 Manual Trigger、Data table、Code、Data table。

第一個 Data table 節點用來建表:

欄位 設定
Resource Table
Operation Create
Table Name haodou_orders
Reuse Existing Tables 開啟(重跑時不會因為表已存在而報錯)

欄位定義如下,除了 amount 是 Number,其餘都是 String:

order_id, email, name, status, carrier, tracking_no,
items, amount, ordered_at, shipped_at, eta, delivered_at, note

接著用 Code 節點輸出要寫入的訂單。我的實驗資料共有 24 筆,這裡列出後面文章會一直用到的 6 筆:

// 一筆訂單輸出成一個 item,下一個節點會逐筆寫進 Data table
const orders = [
  { order_id: 'A10293', email: 'wang@example.com', name: '王小明', status: 'shipped', carrier: '黑貓宅急便', tracking_no: '8891234567', items: '衣索比亞 耶加雪菲 水洗 淺焙 200g x2', amount: 1180, ordered_at: '2026-09-08', shipped_at: '2026-09-10', eta: '2026-09-15', delivered_at: '', note: '' },
  { order_id: 'A10294', email: 'lee@example.com', name: '李小美', status: 'packing', carrier: '', tracking_no: '', items: 'V60 濾杯 01 + 濾紙 100入', amount: 890, ordered_at: '2026-09-12', shipped_at: '', eta: '2026-09-17', delivered_at: '', note: '' },
  { order_id: 'A10295', email: 'chen@example.com', name: '陳大文', status: 'delivered', carrier: '黑貓宅急便', tracking_no: '8891234599', items: '哥倫比亞 慧蘭 中深焙 400g', amount: 1360, ordered_at: '2026-08-20', shipped_at: '2026-08-21', eta: '2026-08-23', delivered_at: '2026-08-23', note: '' },
  { order_id: 'A10296', email: 'wang@example.com', name: '王小明', status: 'pending_payment', carrier: '', tracking_no: '', items: '手搖磨豆機 C2', amount: 2480, ordered_at: '2026-09-13', shipped_at: '', eta: '', delivered_at: '', note: '' },
  { order_id: 'A10297', email: 'liu@example.com', name: '劉志強', status: 'returning', carrier: '黑貓宅急便', tracking_no: '8891234610', items: '瓜地馬拉 安提瓜 中焙 200g', amount: 620, ordered_at: '2026-08-30', shipped_at: '2026-08-31', eta: '2026-09-02', delivered_at: '2026-09-02', note: '' },
  { order_id: 'A10300', email: 'lin@example.com', name: '林佳穎', status: 'shipped', carrier: '新竹物流', tracking_no: 'HC20260912017', items: '肯亞 AA 中淺焙 200g x3', amount: 1650, ordered_at: '2026-09-11', shipped_at: '2026-09-12', eta: '2026-09-15', delivered_at: '', note: '' },
];
return orders.map(o => ({ json: o }));

最後一個 Data table 節點選 Resource: Row、Operation: Insert、資料表選 haodou_orders,欄位對應用自動對應,執行一次就完成了。

資料裡我刻意放了一個陷阱:王小明同時有 A10293(已出貨)和 A10296(待付款)兩筆訂單。客人只寫「我的訂單到了嗎」的時候,系統要怎麼處理,後面會一直碰到。

status 只會出現這六種值:

pending_payment  待付款
packing          備貨中
shipped          已出貨
delivered        已送達
returning        退貨處理中
cancelled        已取消

單兵 Agent 的 workflow

整個 Agent 的結構如下圖:一個 AI Agent 底下接模型、記憶、查訂單的工具。

https://ithelp.ithome.com.tw/upload/images/20260918/20183868dbVMe9aXvP.png

教學先用 Manual Trigger 加一封寫死的測試信,比較好操作。我自己批次跑實驗時,是把入口換成 Webhook,其餘設定完全相同。

測試信

// 固定的測試信。欄位名稱先訂好,之後換成 Webhook 或 Gmail 時就不用改後面的節點
return [{
  json: {
    from_email: 'wang@example.com',
    from_name: '王小明',
    subject: '訂單什麼時候到',
    body: '你好,我的訂單 A10293 想問一下什麼時候會到?謝謝',
    thread_id: 'th_demo_001',
    received_at: '2026-09-14T09:05:00+08:00',
  },
}];

AI Agent 節點

Source for Prompt (User Message) 選 Define below,Prompt 切成 Expression 貼上:

寄件人:{{ $json.from_name }} <{{ $json.from_email }}>
主旨:{{ $json.subject }}
內文:
{{ $json.body }}

Options 加入 System Message。這份 prompt 是很多人會寫出來的「第一版」,把角色、工具、FAQ、處理規則、語氣規範全部寫在一起:

你是「好豆選物」的客服專員小豆。好豆選物是線上咖啡豆與手沖器材電商。
你的工作是閱讀客戶來信,判斷需求,查詢必要資料,然後寫一封回信。

## 你可以使用的工具
- get_order:用訂單編號查訂單資料。

## 訂單狀態對照
pending_payment=待付款, packing=備貨中, shipped=已出貨,
delivered=已送達, returning=退貨處理中, cancelled=已取消

## 常見問題(FAQ)
- 一般訂單於付款完成後 1–3 個工作日內出貨,例假日不計。
- 運費 NT$80,單筆滿 NT$1500 免運。
- 目前配送台灣本島與離島,不提供海外配送。
- 咖啡豆建議於烘焙日後 30 天內飲用完畢,開封後請密封,放陰涼處或冷藏。
- 淺焙豆適合手沖、冰滴與冷萃;中深焙適合義式與加奶飲品。
- 商品到貨後 7 天內可申請退貨,但已開封的咖啡豆不接受退貨。
- 器材類商品保固一年,人為損壞不在保固範圍。

## 處理規則
1. 先判斷客戶的意圖,可能是:查訂單、問商品知識、退換貨、客訴、問優惠、其他。
2. 如果是查訂單,必須使用 get_order 工具,不可以憑印象回答。
3. 如果客戶沒給訂單編號,要在回信中禮貌地請他提供。
4. 如果是問商品知識,從上面的 FAQ 找答案。FAQ 沒寫的,不要自己編。
5. 如果是退換貨,說明政策,並告知會有專人聯繫。
6. 如果是客訴(語氣明顯不滿、要求賠償、提到消保官),不要自己回,
   在回信開頭標註 [需人工處理]。
7. 如果是問優惠折扣碼,請他參考官網首頁公告,不要自己給折扣。
8. 一封信可能同時有多個問題,要全部回答到。

## 回信語氣規範
- 稱呼客人用「您」,署名固定為「好豆選物 客服小豆」。
- 開頭先同理,再回答。不要用「根據我們的政策」開頭。
- 不使用:「親愛的客戶」、「敬請見諒」、「如有疑問歡迎來信」。
- 不確定的事一律寫「我這邊幫您確認後,最晚 24 小時內回覆您」,不要猜。
- 全信控制在 150 字以內。
- 結尾固定一句:「祝您有個美好的一天,好豆選物 客服小豆」。

## 輸出
直接輸出回信內容,不要加任何說明文字。

模型與記憶

Chat Model 接 Ollama Chat Model,選 gemma4:cloud,Options 加入 Context Length 16384、Enable Thinking 關閉、Sampling Temperature 0.2,理由在 Day 03 說明過。

Memory 接 Simple Memory,Session ID 選 Define below,Key 填 {{ $json.thread_id }},Context Window Length 設 5。為什麼選 thread_id,明天會用實驗說明。

get_order 工具

Tool 插槽新增一個 Data table Tool,把節點名稱改成 get_order。模型看到的工具名稱就是節點名稱,取個清楚的名字對模型判斷很有幫助。

欄位 設定
Tool Description Set Manually
Resource Row
Operation Get
Data table haodou_orders
Must Match All Conditions
條件 order_id 等於下方 Expression
Return All 關閉,Limit 設 1

條件的值:

{{ $fromAI('order_id', '客戶信件中提到的訂單編號,格式如 A10293', 'string') }}

$fromAI() 的三個參數分別是參數名稱、描述、型別。n8n 會把工具裡所有的 $fromAI() 整理成一份參數定義交給模型,模型決定呼叫工具時就依照這份定義填值。所以描述寫得越清楚,模型填錯的機會越低。

Description 填:

用訂單編號查詢一筆訂單的完整資料。
當客戶詢問訂單狀態、出貨進度、物流編號、預計到貨時間時使用。
輸入必須是訂單編號(格式:A + 5 位數字)。若客戶沒有提供訂單編號,不要使用此工具。

第一次執行

輸出的回信:

王先生您好,很抱歉讓您久候。

您的訂單 A10293 已於 9 月 10 日出貨,由黑貓宅急便配送(單號:8891234567),預計 9 月 15 日送達。

祝您有個美好的一天,好豆選物 客服小豆

打開 Execution 看細節,AI Agent 對模型呼叫了兩次。第一次模型回傳 get_order({"order_id":"A10293"}),第二次拿到訂單資料後寫出回信,兩次加起來約 2,000 個 prompt tokens。

出貨日、物流商、單號、預計到貨日全部正確。挑毛病的話有兩個:一是資料裡只有姓名,「王先生」是它自己猜的性別;二是客人並沒有在等,「很抱歉讓您久候」有點多餘。

一個被推翻的預期

寫這篇之前我以為,Tool Description 如果寫得太簡略,模型遇到沒給編號的信,就會自己編一個訂單編號去查。

為了驗證,我把描述縮成三個字「查訂單」,$fromAI 的描述也縮成「訂單編號」,拿 5 封信(4 封沒提供編號、1 封有提供)跟完整描述的版本比較:

描述版本 沒給編號的 4 封 有給編號的 1 封
完整描述 都沒呼叫工具,請客人提供編號 正確查單
只寫「查訂單」 都沒呼叫工具,請客人提供編號 正確查單

在 gemma4:cloud 上,兩個版本的行為一模一樣,沒有亂編編號。

我還是會寫完整的描述,因為這份描述是模型判斷「該不該用這個工具」的唯一依據。現在只有一個工具,選擇很單純;等工具變多,描述含糊就容易選錯。成本也很低,完整描述只讓 prompt 多了約 80 個 tokens。

現在已經看得到的問題

第一封信跑得很順,但從設計上已經可以預見幾件事:

  • 工具只能用訂單編號查。王小明問「我的訂單到了嗎」時,沒辦法用 email 查出他有兩筆訂單。
  • FAQ 只有 7 條直接寫在 prompt 裡,客人問到沒寫的事,它會怎麼回?
  • 規則 5 要它告知「會有專人聯繫」,但這個 workflow 裡根本沒有人會收到通知。

這些明天先放著,Day 06 會用 10 封刻意設計的信實際驗證。

小結

用 Data table、AI Agent、Ollama Chat Model、Simple Memory 四個節點就能做出會查單的客服 Agent,第一封信事實全對。Tool Description 寫簡略在這個模型上沒造成問題,它只是模型選工具的依據,不過還是會詳細解釋。

明天來講今天帶過的 Memory:Session ID 用 email、用 thread_id,或乾脆不給記憶,實際差在哪裡。


上一篇
Day 03|n8n 快速上手:用 Docker 架好 n8n、接上 Ollama,與 AI Agent 節點的三個插槽
下一篇
Day 05|給 Agent 記憶:Simple Memory 的 Session ID 該怎麼設
系列文
從單一 Agent 到 Supervisor 架構:用 n8n 實作會自己分工的 Multi-Agent AI 部門9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言