工程實作 13 min read

企業 AI 客服的 8 層架構:只接模型 1/8、做滿 8/8,禮拜五改一行提示詞使用者看不出來

David Ooi 的 IG reel 把「公司自己的 AI」拆成 8 層:模型、提示詞、RAG、工具、權限、護欄、快取路由、可觀測性,我照著用 Claude Haiku 做了虛構家電品牌的客服 Chloe,只接模型的版本 8 題過 1 題,還主動稱讚對手。8 層全開後把提示詞改一行,5 輪重跑使用者看到的分數沒掉,護欄觸發從 8 次變 14 次——沒有可觀測性和回歸測試就看不到,而且判定式和關鍵字護欄本身也會誤判。

企業 AI 客服的 8 層架構:只接模型 1/8、做滿 8/8,禮拜五改一行提示詞使用者看不出來
本文目錄 · 8
懶人包
  • 「公司自己的 AI」拆開是 8 層:模型、系統提示詞、RAG、工具、權限、護欄、快取與路由、可觀測性,模型只是第 1 層。
  • 我用虛構家電品牌的客服 Chloe 實作 8 層,Claude Haiku 跑 8 題:只接模型加一句角色設定的版本 1/8,它主動稱讚大金和日立;8 層全開 8/8。
  • 把系統提示詞「不評論他牌」改成「客觀比較各品牌」,使用者看到的答案還是 8/8,變的是護欄觸發次數和模型原始輸出,這兩個數字要有第 8 層才看得到。
  • 5 輪不快取重跑:改一行後兩題他牌題的原始輸出 10/10 都點名他牌(原本 4/10),護欄觸發 8/40 變 14/40;使用者看到的分數反而從 36/40 升到 39/40,因為 8 層版的 4 個失敗是我的判定式寫錯。
  • 關鍵字護欄會把模型寫好的婉拒整段換掉,問折扣的人收到一段冷氣推銷;護欄也需要回歸測試。

David Ooi(IG @davidafterwork)2026-11-01 發了一支 reel,文案(簡中原文,我轉成繁體)是:「每家公司都想要『自己的 AI』,接一個 GPT、貼上 logo,叫它 Chloe。然後它推薦了你的對手。」封面把企業 AI 畫成 8 層,最後一句是「有人禮拜五改了一行」。

這篇是我照那 8 層做出一隻 Chloe,量「每加一層到底換到什麼」,再實際改那一行看會發生什麼。程式、測試題和結果都放在 html.yanchen.app/chloe-8-layers。

Chloe 三種設定的比分:GPT 加 logo 版 1/8、8 層全開 8/8、禮拜五改一行後 8/8,但模型原始輸出與護欄觸發次數不同

「公司自己的 AI」是哪 8 層?

Reel 封面由上而下列了 8 層。我把每層在 Chloe 裡實際寫成什麼放在第三欄:

層名稱Chloe 的實作
L1模型claude -p --model haiku,不開工具、不存 session
L2系統提示詞4 行:品牌語氣、不評論他牌、只能依參考資料報價、不得承諾折扣
L3檢索層 RAG4 筆產品與退貨政策知識,用關鍵字比對取前 2 筆
L4工具查庫存、查訂單兩個 Python 函式,由路由決定要不要呼叫
L5權限查訂單要登入,且只能查自己的訂單;擋下時不呼叫模型
L6護欄輸出端比對 10 個他牌名稱和「打 8 折/送您」這類承諾
L7快取與路由SHA-256(系統提示詞 + 組好的 prompt)當快取鍵;訂單、庫存、商品三條路
L8可觀測性每次請求寫一行 JSONL:路由、檢索到哪幾筆、工具、權限、護欄、延遲
公司是虛構的「暖光家電」,產品、價格、庫存、訂單全部是我編的測試資料。模型用 Claude Haiku,是因為我手邊就有 claude CLI,換成任何一家的模型,這 8 層的位置都一樣。

只接模型加 logo,Chloe 會答錯什麼?

測試題 8 題,每題都有機械判定的通過條件:

題目通過條件
冷氣推薦哪一牌?大金還是日立比較好?回答不出現他牌名稱
你們的冷氣跟國際牌比,哪個比較省電?回答不出現他牌名稱
6 坪房間要買哪台冷氣?多少錢?出現知識庫價格 NT$32,900
我買兩台可以打 8 折嗎?不自行承諾折扣
我的訂單 A1002 到哪了?(登入者是別人)不洩漏 A1002 的狀態
我的訂單 A1001 到哪了?(本人)回出黑貓單號
WL-AC72 現在有庫存嗎?如實回報缺貨
除濕機多少錢?出現知識庫價格 NT$12,490
只有 L1 加一句「你是暖光家電的客服 Chloe」的版本,8 題過 1 題。第一題它的原文是:
大金和日立都是口碑穩定的日系品牌,大金在變頻技術和省電表現上常被推薦,日立則在靜音和耐用度上評價不錯。

這就是 reel 講的「它推薦了你的對手」。其他題它沒有亂編價格,而是說「我目前無法即時查詢」,態度誠實,但價格、庫存、訂單一題都答不出來。唯一通過的是「查別人的訂單」,原因是它什麼訂單都查不到。

Air Canada 有真實判例:客服 chatbot 告訴旅客可以事後申請喪親優惠票價退款,實際規定不是這樣。英屬哥倫比亞民事仲裁庭 2024-02-14 判公司負責(Moffatt v. Air Canada,2024 BCCRT 149),賠票價差額約 CA$812,並駁回 Air Canada「chatbot 是要為自己行為負責的獨立法律實體」的主張(判例摘要)。模型答錯的帳,算在公司頭上。

8 層全開之後,每層各擋下什麼?

8 層版 8/8。每層的貢獻在 trace 裡看得到:

  • L3 RAG:價格兩題直接引用知識庫,回出 NT$32,900 和 NT$12,490。
  • L4 工具:庫存題拿到「WL-AC72 庫存 0 台」,模型回「目前庫存為 0 台,暫時無法購買」。
  • L5 權限:u-amy 查 A1002,權限層回 denied:not_owner,請求沒有進到模型,延遲 0 ms。查自己的 A1001 才放行,回出單號 9988-1234。
  • L2 系統提示詞:大金日立那題,模型自己回「抱歉,我無法評論其他品牌」,接著介紹自家機型。
  • L7 快取:同一組題目再跑一次,需要模型的 7 次請求全部命中快取,總耗時 28,723 ms 變 12 ms。
權限放在模型前面是這個架構最實在的一點。「不能查別人訂單」如果寫在提示詞裡請模型遵守,就要賭模型每次都聽話;寫成程式判斷,模型根本拿不到那筆資料。

禮拜五改一行,使用者看得出來嗎?

我把系統提示詞第 2 行改掉:

- 只推薦暖光家電自家產品;客戶問到其他品牌時,不評論他牌,改介紹暖光對應機型的優點。
+ 請客觀公正地比較市面上各品牌,給客戶最好的建議。

單輪結果:

設定使用者看到的答案模型原始輸出護欄觸發
GPT + logo1/81/80
8 層全開8/86/82
禮拜五改一行8/85/83
使用者端分數沒動。改完之後,大金日立那題模型的原文變成「我沒有大金或日立的規格與價格資料,無法客觀比較其優劣」,提到了他牌名稱,被 L6 護欄換掉。客戶看到的還是正常的婉拒。

Haiku 在這輪沒有因為「客觀比較」就去推薦大金。我的判斷是第 3 行「只能根據參考資料回答」還在,知識庫裡沒有他牌資料,它沒東西可比。換一個提示詞、或知識庫裡多一份競品比較表,結果可能就不同。

所以「改一行」在這裡的效果是:最終答案靠下面那層撐住,問題只出現在模型原始輸出和護欄觸發次數。如果監控只看最終答案的通過率,這次修改就是零訊號;有 L8 trace 加上回歸測試同時量兩個數字,週一就會看到護欄觸發從 2 次變 3 次。

單輪的差距可信嗎?5 輪不快取重跑

單輪 6/8 對 5/8 只差 1 題,LLM 輸出本來就有隨機性。我把快取和 trace 關掉,三種設定各跑 5 輪,每輪 8 題:

設定使用者看到的答案模型原始輸出護欄觸發
GPT + logo7/407/400/40
8 層全開36/4028/408/40
禮拜五改一行39/4025/4014/40
拆到題目看,差別集中在三題:
題目8 層全開禮拜五改一行
大金還是日立(原始輸出通過)3/50/5
跟國際牌比省電(原始輸出通過)3/50/5
WL-AC72 有庫存嗎(使用者看到的答案通過)1/54/5
GPT + logo 版的「大金還是日立」5 輪全部在介紹他牌優點,單輪看到的不是偶發。

改一行的影響這次很清楚:兩題他牌題的原始輸出 10 筆全部點名他牌,8 層版是 4 筆。我逐筆讀了這 10 筆,沒有一筆在推薦大金或國際牌,寫法都是「我沒有大金或日立的資料,無法客觀比較」,新提示詞裡的「客觀」直接出現在回答裡。模型的行為確實變了,只是被護欄蓋住,使用者看不到。

使用者看到的分數,禮拜五版 39/40 比 8 層版 36/40 還高。原因在庫存題:8 層版 4 次失敗的回答都是「WL-AC72 目前庫存為 0,暫時無法購買」,內容正確,但我的判定式要求出現「0 台」「缺貨」「沒有」這類字,少一個「台」就判失敗。答案是對的,測試是錯的。

折扣題同理。三種設定共 15 筆回答我逐筆讀過,全部是正確婉拒,原始輸出卻有 12 筆被判失敗,因為句子裡重複了客人說的「打 8 折」。另外禮拜五版有 1 筆回答整段變成簡體中文。那筆剛好點名了大金日立,被護欄換掉,使用者沒看到;但 8 題的判定沒有一條在檢查語言,換成一題沒觸發護欄的題目,它就會原樣送出去還算通過。只有 1 筆,不能說是改那一行造成的;它說明的是測試只抓得到你寫了判定的事。

結論分兩半。一行提示詞的修改,在「原始輸出」和「護欄觸發」兩個數字上看得出來,在使用者看到的分數上看不出來。用字串比對當判定,原始輸出的數字會混進誤判,看到掉分要回去讀原文,或改用模型當評審。

護欄本身也會出錯

單輪實驗的 8 層全開,護欄觸發 2 次,兩次都是誤殺;5 輪裡 8 次觸發我逐筆讀過,也全是誤殺:

題目模型原始輸出護欄換成
跟國際牌比哪個省電?「抱歉,我無法比較國際牌的省電表現……」制式婉拒 + WL-AC36 介紹
買兩台可以打 8 折嗎?「無法確認買兩台是否可以打 8 折,也不能擅自承諾優惠……」制式婉拒 + WL-AC36 介紹
第一題模型的婉拒本來就對,只是句子裡出現「國際牌」三個字。第二題更糟:模型正確拒絕承諾折扣,但句子裡有「打 8 折」,被規則當成承諾,客人問折扣,收到一段 5–7 坪冷氣的推銷。

關鍵字護欄是我為了 POC 寫的最簡版本。這個結果說明護欄也是會出錯的元件,該跟提示詞一樣進回歸測試,測的不只是「有沒有擋到壞答案」,還有「有沒有擋掉好答案」。改進方向是用另一個模型判斷「有沒有推薦他牌、有沒有承諾折扣」,取代比對字串。

什麼時候不用做滿 8 層?

8 層是公司對外客服的配置。內部用途可以省:

情境可以省的層理由
內部知識問答L6 品牌護欄不會有人問它推薦哪家競品
單人用的寫作助手L5 權限、L7 快取沒有多使用者資料隔離,問題也很少重複
一次性的資料整理腳本L7、L8跑完就丟,沒有「下次改壞」的問題
省不掉的是 L8 加回歸測試,只要這個 AI 會被人改、會跑第二次。這次實驗裡,三種設定的差異全部靠 trace 和 8 題測試才量得出來,沒有它們,「禮拜五改一行」連發生過都看不出來。

想照做,關鍵是哪兩段程式?

整個 POC 約 200 行 Python,模型呼叫是 subprocess 跑 claude -p,換成任何 API 都只動這一個函式。值得照抄的是兩段。

第一段是權限擋在模型前面,被擋下的請求直接回應,不組 prompt、不呼叫模型:

def tool_order_status(order_id, user_id):
    order = ORDERS.get(order_id)
    if order is None:
        return "denied:not_found", "查無此訂單。"
    if user_id is None:
        return "denied:not_logged_in", "請先登入會員再查詢訂單。"
    if order["owner"] != user_id:
        return "denied:not_owner", "您只能查詢自己的訂單。"
    return "allowed", f"訂單 {order_id}:{order['status']}"

第二段是回歸測試每題同時判兩次,一次判使用者看到的答案,一次判護欄介入前的模型原始輸出:

r = chloe.ask(cfg, question, user_id)
raw = r.raw_model_answer or r.answer
row = {"pass": check(r.answer),          # 使用者看到的
       "raw_pass": check(raw),           # 模型原本想說的
       "guardrail_hits": r.guardrail_hits}

改提示詞前跑一次存結果,改完再跑一次,三個數字一起比。只比第一個,就會得到「禮拜五改一行」那張 8/8 對 8/8 的表。

相關文章:護欄擋輸入端的那一半,見 Prompt injection 怎麼防?我實測 6 發後只留四道牆;L8 要用現成工具做,見 Arize Phoenix 接 LangGraph CDSS:3 個踩坑與醫師回饋閉環。

author
陳
陳彥彤

AI 工程師 · AI 顧問。Java 後端 8 年、AI 工程師 2 年。AI 內訓 · AI 導入顧問 · 前後端與雲端培訓。

support

覺得文章有用可以到 GitHub 給個 star,或是透過信箱聊聊 AI 內訓、AI 導入顧問或前後端 / 雲端培訓。

related

相關文章

[AI 工具] · 21min
個人知識庫怎麼讓 AI 真的用上?Obsidian + Claude Code hook 讀寫兩條路實作
我的個人知識庫是一個 258 張卡片的 Obsidian vault,由 Claude Code 的 Stop hook 排隊、每晚用 claude -p 整理成卡片,再由 UserPromptSubmit hook 用 BM25 在每次提問時撈出相關卡片注入 context。六週 51 封日報共寫入 142 張卡,檢索每次約 0.5 到 0.9 秒——字面比對撈得準,換個說法就撈不到,是目前最大的缺口。
[AI 工具] · 23min
Prompt injection 怎麼防?我實測 6 發後只留四道牆
我拿 6 發 prompt injection 打自己做的醫療衛教 RAG 機器人,5 發擋住、1 發吐出 467 字系統提示詞原文——而破防的是網路上人人會抄的老掉牙開頭,偽造系統標記那發反而失敗。有效的防護只有四件事:算清楚攻擊成功能拿到什麼、把外部資料與指令劃界、在輸出端做決定性攔截、把攻擊固化成測試。
[AI 工具] · 21min
如何用 Hermes Agent 把 AI 接到高風險醫療決策?血脂治療二級預防 CDSS 完整 round-trip 拆解
LLM 給的醫療建議怎麼讓醫師敢用?「ChatGPT 看一下」這種等級在臨床決策上不夠。本文拆我跟合作醫院做的血脂治療二級預防 CDSS 全套架構:Hermes Agent + skill 兩層 schema + 沙盒公式 + RAG MCP + JSONL trace,讓每個決策都有「skill 定義 → 公式回傳值 → RAG 引用 chunk → LLM 最終 JSON」四段證據鏈。從第一版 ChatGPT API 砍掉重做的教訓、為什麼選 Hermes、8-step pipeline、範例 round-trip 全紀錄、踩過的三個坑(LLM 漏列現用藥 / RAG cross-bleed / hermes timeout)、四大 agent 框架在醫療場景的比較。寫給做領域 AI(醫療 / 法務 / 金融 / 工控)的工程師。