到目前為止,主管派工都是一次決定好:這封信要查訂單、要查 FAQ,或兩個都查。兩位專員拿到同一個問題,各查各的,結果丟給回覆專員。
有一類信這樣處理不了。
A10295 的豆子喝起來太苦了,不太喜歡,想退。
要回答這封信,得知道三件事:這張單什麼時候送達的、退貨期限是幾天、以及今天距離送達過了多久。前兩件分別在訂單表和 FAQ 裡,第三件誰都沒有——它必須把前兩件算在一起才會出現。
今天做接力:W1 先查,中間插一個計算步驟,再把算好的東西交給 W2 和 W3。
先看現況。Day 18 的 Switch 版跑這封信,草稿是這樣:
陳大文 您好
讓您等待確實不好意思。關於訂單 A10295 的哥倫比亞 慧蘭 中深焙 400g,若咖啡豆未開封且外包裝完整,且在到貨 2026-08-23 起 7 天內,您可以申請退貨。但若咖啡豆已開封,則不接受退貨或換貨。
祝您有個美好的一天,
好豆選物 客服小豆
沒有錯誤資訊,到貨日是對的、政策也是對的。但它把判斷丟回給客人:你自己算算看有沒有超過 7 天,自己想想有沒有開封。
實際上系統知道答案。送達日是 8/23,信是 9/14 寄來的,已經過了 22 天,這封信的答案就是不能退。但沒有任何一個環節負責做這個減法:W1 只回報它查到的欄位,W2 只回報 FAQ 原文,W3 被明文禁止使用 facts 以外的資訊——它連「22」這個數字都不該自己算出來。
Day 09 定契約的時候,derived 這個欄位就在裡面了,意思是「主管算出來的結果」。但從 Day 16 到 Day 19,我傳給 W3 的 derived 一直是空陣列,因為沒有人去填它。
今天把流程改成這樣:分類完先派 W1,拿到訂單資料之後進一個 Code 節點算推論,再派 W2,最後合併交給 W3。兩種做法的差別如下圖。

推論節點做三件事:
const today = String(email.received_at ?? new Date().toISOString()).slice(0, 10);
const days = (from, to) => Math.round((Date.parse(to) - Date.parse(from)) / 86400000);
const derived = [];
const delivered = get('delivered_at');
if (delivered && /^\d{4}-\d{2}-\d{2}$/.test(String(delivered.value))) {
const n = days(String(delivered.value), today);
derived.push({ key: 'days_since_delivery', value: String(n), basis: `今天 ${today} 減去到貨日 ${delivered.value}` });
derived.push({
key: 'within_7day_window',
value: n <= 7 ? 'true' : 'false',
basis: `到貨 ${n} 天,退貨期限為到貨 7 天內`,
});
}
const items = get('items');
const EQUIP = ['磨豆機', '濾杯', '手沖壺', '壺', '濾紙', '器材', '聰明'];
if (items) {
const isEquip = EQUIP.some((k) => String(items.value).includes(k));
derived.push({ key: 'item_category', value: isEquip ? 'equipment' : 'beans', basis: `依品項「${items.value}」判斷` });
}
每一筆推論都帶一個 basis,寫清楚是怎麼算出來的。這跟 facts 要有 source 是同一個原則:事實要能追到來源,推論要能追到算式。之後驗收或人工審核時,看到「已超過 7 天」才有辦法確認這個結論對不對。
「今天」刻意取客人來信的時間,不是取執行當下的時間。這樣同一封信重跑一百次,算出來的天數都一樣,考卷才有意義。
既然 W1 已經回報了,那 W2 的問題就不該跟 W1 的一樣。推論節點最後會把問題補上條件:
const parts = [c.clean_question];
if (cat) parts.push(cat.value === 'equipment' ? '(此案的商品是器材類)' : '(此案的商品是咖啡豆類)');
if (win) parts.push(win.value === 'true' ? '(仍在到貨 7 天內)' : '(已超過到貨 7 天)');
原本問「客戶想退貨,詢問是否可以退」,現在問「客戶想退貨,詢問是否可以退(此案的商品是咖啡豆類)(已超過到貨 7 天)」。檢索要找的條目從一堆退貨政策縮到咖啡豆那幾條。
這就是接力的實際意義:不是「依序執行」,是後面的人知道前面的人查到什麼。
推論節點裡那行 get('delivered_at'),要能取到值,前提是 W1 回報的 key 真的叫 delivered_at。
Day 11 那版 W1 的 key 是自由填的,20 張工單生出 16 種寫法,送達日可能叫「送達日」「到貨日期」「訂單 A10295 送達日」。這種情況下沒辦法寫 get('delivered_at'),只能用模糊比對,而模糊比對遲早會比對錯。
所以今天順便把主線的 W1 換成 Day 14 做的 enum 版本:key 限定在 12 個值、多一個 subject 欄位區分多筆訂單、守門員多一條「查不到就不該有事實」。Day 14 那篇最後我說「這筆錢划算」,當時只是推測,今天才看到它買到了什麼——一個可以安全地用 f.key === 'delivered_at' 取值的下游。
契約的價值通常不會在訂契約的那天顯現。
評估集裡退貨類剛好 8 封,涵蓋器材保固、已開封想退、包裝破損、未開封想退、買錯尺寸想換、超過期限想退、問退款何時到帳、退貨想改成換貨。
| 平行派工 | 接力派工 | |
|---|---|---|
| 完整通過 | 7/8 | 7/8 |
| 部分通過 | 1 | 1 |
| 危險錯誤 | 0 | 0 |
| tokens 中位數 | 8912 | 9216 |
| 延遲 p50 | 7.4 秒 | 6.7 秒 |
| 延遲 p95 | 10.3 秒 | 7.6 秒 |
總分一樣。接力修好了它要修的那一封,但弄壞了另一封。
一開始那封「豆子太苦想退」,接力版的草稿變成:
陳大文 您好
讓您等待確實不好意思。關於訂單 A10295 的哥倫比亞 慧蘭 中深焙 400g,咖啡豆退貨條件為未開封且外包裝完整,並在到貨 7 天內申請;已開封的咖啡豆不接受退貨或換貨。由於您的訂單於 2026-08-23 送達,目前已超過 7 天的申請期限,因此無法為您辦理退貨。
祝您有個美好的一天,
好豆選物 客服小豆
從「你自己算算看」變成「已超過期限,無法辦理」。客人收到這封信就知道結果了,不用再寫一封信來問。
A10297 我之前申請退貨了,但我想改成換一包新的就好,可以嗎?
這封的標準答案是「我幫您確認後回覆」,因為 FAQ 裡根本沒寫「退貨能不能改成換貨」。平行版答對了。
接力版的草稿:
關於訂單 A10297 的瓜地馬拉 安提瓜 中焙 200g,目前狀態為退貨處理中。由於咖啡豆的退貨期限為到貨 7 天內,而您的訂單於 2026-09-02 送達,目前已超過此期限。
客人問的是「能不能把退貨改成換貨」,回信在講退貨期限。
原因是推論節點不分青紅皂白,看到有 delivered_at 就算了 within_7day_window = false,然後 W3 看到這個醒目的推論,就拿它來回答一個它根本不適用的問題。這封信的退貨申請早就送出去了,7 天期限跟客人的問題沒有關係。
平行版沒有這個問題,純粹因為它沒算這個推論。不是它比較聰明,是它比較無知。
這件事讓我重新想了一下 derived 的定位。
facts 是查到的,有來源,不會錯。推論是算出來的,而算式本身帶著一個前提——「退貨期限」這個推論的前提是「客人正在問能不能退貨」。前提不成立的時候,推論仍然是正確的計算,但它變成了誤導。
W3 分不出這個差別。它看到的 derived 跟 facts 長得一樣,都是 key、value,而且還多一個看起來很權威的 basis。
兩個方向可以修:推論節點依 intent 決定要算什麼,或是給每一筆推論加一個適用條件讓下游判斷。我傾向前者,因為把判斷留給 W3 等於又把問題丟回給模型。不過這兩個我今天都還沒做,明天做驗收機制的時候,這種「用了不該用的推論」正好是驗收該抓的東西之一。
延遲沒有變差,而且更穩。 我原本以為接力一定比較慢,畢竟 W1 跑完才輪到 W2。實際上 p50 從 7.4 秒變 6.7 秒、p95 從 10.3 秒變 7.6 秒。原因是 n8n 的兩條分支本來就不是真的平行執行,合併還要等兩邊都好;接力的流程反而單純。這個差距在 8 封信的樣本上我不敢說有多可靠,但至少「接力一定比較慢」這個直覺是錯的。
我又犯了一次 Day 18 的錯。 新 workflow 的最後一個節點,我寫成「needs_human 為真就 escalate」,結果 8 封退貨信全部變成不寫草稿,第一次跑出來 0/8。退貨的 needs_human 意思是「草稿要人審」,不是「不要寫草稿」——這件事我兩天前才寫過一篇,換個 workflow 又踩一次。
接力的重點不是執行順序,是中間那個把兩邊資料算在一起的步驟。推論放進 derived,每一筆要帶算式,「今天」要取來信時間而不是執行時間。接力讓那封「超過退貨期限」的信從模稜兩可變成明確答案,但也讓另一封被不相關的推論帶偏,8 封總分打平。推論節點目前不管意圖是什麼都照算,這是它會誤導下游的原因。
明天做驗收與退件:草稿寫好之後,誰來檢查它有沒有用錯資料、漏答問題,以及檢查不過的時候要怎麼退回去重做。