明天開始要實作三位專員。我們需要把設計原則想清楚,特別是一個一定會遇到的問題:一位專員應該管多寬?
最直覺的拆法是:訂單資料一位專員、客戶資料一位專員,畢竟它們是兩張不同的表。
拿 Day 07 的四個判準來檢查:兩位的目標都是「查出正確事實」;失敗的方式都是「查無此筆」或「資訊不足」;權限都是讀 Data table;考卷也可以出在同一份裡,一題問「A10293 是什麼狀態」,一題問「這位客人是什麼會員等級」。
四個判準都一樣,代表它們是同一位專員的兩個工具,而不是兩個角色。
合併還有額外的好處。Day 06 的 T04,王小明沒給編號只問「我的訂單到了嗎」,需要先用 email 查出他名下的訂單。如果「用編號查」和「用 email 查」兩個工具在同一位專員手上,它就能自己決定該用哪一個。
另一個誘人的想法是開一位「情緒判斷專員」,專門判斷一封信算不算客訴。
試著幫它出考卷就會卡住。例如「不好意思,我的包裹顯示 9/12 會到,今天都 14 號了還沒收到,想請你們幫忙看一下。」這封算客訴嗎?客人明顯擔心,但語氣很客氣,也沒有要求賠償。連人都很難給出一致的答案,自然也寫不出標準答案。
考卷寫不出來,通常代表這件事的定義還不清楚,多開一個 Agent 並不能解決。我的做法是把它變成主管分類輸出裡的 sentiment 和 needs_human 兩個欄位,在分類時一起判斷。
| 專員 | 成功定義 |
|---|---|
| W1 查單專員 | 回報的每一筆事實都正確,而且附上 source |
| W2 知識專員 | 答案有 FAQ 條目的出處,否則明確回報 not_found |
| W3 回覆專員 | 草稿只使用給定的事實,並且符合語氣規範 |
寫不出一句話的成功定義,通常代表這位專員的職責太雜。
W1 的 prompt 完全不提 W2 和 W3,它也不知道自己的回報會交給誰。
這樣做有幾個好處。W1 不依賴上下游,可以單獨拿去考試;之後要換掉 W3 的模型,W1 一行都不用改;也能避免 W1 心血來潮「順便幫你問一下知識專員」,讓架構悄悄變成難以控制的網狀結構。
W1 查不到訂單時,回報 status: not_found 就好。至於「請客人確認編號」還是「轉給人處理」,那是主管的決定。
界線很簡單:事實歸專員,怎麼辦歸主管。唯一的彈性是 notes 欄位,專員可以寫「這位客人有兩筆進行中的訂單,可能需要確認是哪一筆」,這是提供資訊,不是做決定。
W1 只有三個工具:
| 工具 | 用途 |
|---|---|
| get_order_by_id | 用訂單編號查一筆 |
| list_orders_by_email | 用 email 查這位客人的所有訂單 |
| get_customer | 用 email 查會員資料 |
沒有「列出全部訂單」,也沒有「執行任意查詢」。這其實就是資安的最小權限原則:工具能做到的事,就是這位專員被惡意信件騙到時,攻擊者能拿到的東西。Day 29 做攻擊測試時,這條原則會派上用場。
每位專員的回報都必須有 status,並允許 not_found 和 insufficient_info。prompt 裡也要明確寫出:
查不到資料時回報 status = not_found,這是正確的行為,不是失敗。
模型有很強的「一定要交出答案」的傾向。Day 07 那 13 封「我幫您確認後回覆」卻沒轉人工的信,就是單兵 Agent 沒有合法出口時的表現。明確告訴它「交白卷是被允許的」,它才不會硬擠出一個答案。
直覺上,做判斷的用大模型,做轉換的用小模型。W1 的工作幾乎沒有推理成分:拿到編號、呼叫工具、把 shipped 翻成「已出貨」、填進 JSON。
不過直覺歸直覺,要不要換成小模型還是得看考試成績。第二週先全部用 gemma4:cloud 把專員做穩,Day 26 再拿同一份考卷測試小模型,看分數會掉多少。
拆得太細也會出問題。假設把查單再拆成「到貨日專員」和「物流單號專員」,一封查單信就得派兩個任務,成本和延遲都翻倍;兩位專員還會各自查一次同一張訂單。更麻煩的是,客人只問「到了嗎」的時候,主管反而不知道該派給誰。
專員拆得越細,主管的判斷就越難。難題只是從專員搬到主管身上,總難度沒有降低,還多付了溝通成本。
我用來劃界線的方法是:一位專員對應「去同一個資料來源、一次取回所有相關資料」的範圍。查一次訂單表就能同時拿到狀態、物流、單號、預計到貨日,這些就都屬於同一位專員。
| 情況 | 建議 |
|---|---|
| 兩件事的目標、失敗方式、權限都一樣 | 合併 |
| 兩件事來自同一次查詢 | 合併 |
| 兩件事失敗時的處理方式不同 | 拆開 |
| 需要不同的資料權限 | 拆開 |
| 寫不出考卷 | 先不拆,改成上層的欄位 |
| 拆完之後主管更難判斷 | 拆過頭了,合回去 |
| 項目 | W1 查單專員 | W2 知識專員 | W3 回覆專員 |
|---|---|---|---|
| worker 名稱 | order_lookup | knowledge | reply_writer |
| 工具 | get_order_by_id、list_orders_by_email、get_customer | faq_search | 無 |
| 輸入 | 問題、訂單編號、客人 email、需要的欄位 | 問題 | 事實、查不到的事項、客人姓名、情緒 |
| 輸出 | 共用回報格式 | 共用回報格式 | 草稿、用到的事實、疑慮 |
| 常見失敗 | not_found、insufficient_info | not_found | 超過字數、禁用詞、自己加料 |
| 模型 | gemma4:cloud | gemma4:cloud | gemma4:cloud |
| Memory | 不接 | 不接 | 不接 |
| 放在哪裡 | 獨立 workflow | 獨立 workflow | 獨立 workflow |
三位專員都不接 Memory。專員收到的是主管整理好的工單,做完、回報就結束。如果接了記憶,同一張工單第一次和第二次跑出來的結果可能不一樣,考試就失去意義。這個決定在 Day 23 會實測。
每位專員都做成獨立的 workflow,用 Execute Workflow Trigger 當入口,這樣可以單獨執行測試,也能被不同的主流程呼叫。這邊先提醒一個實作細節:n8n 2.x 新增這個觸發節點時,預設名稱是「When Executed by Another Workflow」。Code 節點裡用 $('節點名稱') 取值時,名稱必須完全一致,否則會直接報錯。照著舊版教學寫 Execute Workflow Trigger,在 2.x 就會對不上。
拆專員要看目標和失敗方式,不要看資料來源;寫不出考卷的判斷,比較適合做成上層的欄位。專員的寬度,以「一次查詢能涵蓋的範圍」為準,拆太細只會把難題丟給主管。
明天開始實作 W1 查單專員,包含三個工具、回報格式,以及一個用程式把關的守門員節點。