利用 Agentic Design Pattern 重構 CareerWise — 從單一流程到路由、提示鏈與反思模式
12 Jul 2026CareerWise 是一個提供履歷健檢、職涯建議與預約諮詢的網站。最初它的 AI 對話功能很單純:不管使用者說什麼,全部丟進同一條生產線處理。這種作法雖然很快就上線了,卻也帶來了三個問題——對簡單的提問浪費 token、對複雜的需求深度不足、以及預約諮詢這種能直接給表單的需求卻還要經過 LLM 流程。
這篇文章記錄 CareerWise 如何逐步導入三種 Agentic Design Pattern - 路由(Routing,含意圖分類與混合路由)、提示鏈(Prompt Chaining)與反思模式(Reflection),將單一的 LLM 呼叫流程,重構成一個具備智慧分流、多步驟分析與品質把關的代理系統。

階段一:意圖路由(Intent Routing)
原始系統架構
在導入任何設計模式之前,CareerWise 的 AI 對話就是一支單純的 route.ts:
POST /api/chat
└─ 使用者問題
│
├─ 關鍵字搜尋,有出現在知識庫條目中就算匹配
│
├─ 把知識庫內容用字串取代的方式直接嵌入 system prompt,讓 LLM 在生成回覆時「看到」這些參考資料
│
├─ LLM 收到 system prompt + 使用者問題後,一次推理完畢才回傳完整回答
│
└─ 回傳 JSON { reply: "..." } 給前端(非串流,HTTP response 只在 LLM 完全生成完畢後才回傳一次)
整個流程是線性的:使用者送出 → API 開始處理 → LLM 花 2-5 秒生成 → 回傳完整 JSON → 前端一次顯示全部文字。
這樣的設計有幾個問題:
- 沒有路由分類,所有問題走同一條路,造成簡單問題也要經過完整的 system prompt,浪費 token 與時間。不分 greeting / resume / booking,一律用同一組 system prompt,舉例來說,不管使用者說「嗨」、「幫我改履歷」、「我想預約」還是「薪水怎麼談」——全部都用同一組 prompt、同一個 max_tokens、同一個 temperature。以實際數字來看,「嗨」 → system prompt 700 tokens + 知識庫 500 tokens + 回覆 200 tokens = 1,400 tokens,但實際上只需要一個「嗨你好~有什麼職涯問題想問的嗎?」(20 tokens)。用同一組參數處理所有問題,等同於叫米其林主廚用煎牛排的方式去煮泡麵——不是不行,但浪費而且不好吃。
- 沒有 streaming,使用者要等 LLM (在這裡用 llama-3.3-70b-versatile) 完全生成完畢才看到回覆,體驗不佳。使用者必須坐在螢幕前,乾等 LLM 把幾百字的履歷報告或職涯建議「完全生成完畢」後,畫面才會一口氣全部跳出來。在講求即時互動的 chat 介面裡,這種長達數秒甚至十秒的空白停頓,會讓使用者以為系統當機,體驗極差。
- 沒有 embedding 語意搜尋,對於複雜問題的深度理解不足,容易產生幻覺。embedding 語意搜尋可以把文字轉成向量座標,用數學距離來比對意義,而非比對關鍵字,這樣可以更精準地找到知識庫中與使用者問題最相關的內容。如果使用者問得比較模糊,系統只能死板地用「關鍵字字面」去比對,導致深度理解不足、容易抓錯文本,進而引發模型幻覺。我們需要的是語意搜尋——把文字轉成向量座標,用數學距離來比對背後的「真實文意」,而不是玩文字接龍,這樣才能在 Phase 2 的改善真正精準地幫使用者對齊知識。
Phase 1 — Routing
原先這樣的設計會讓所有提問(嗨、改履歷、預約)都走同一條路:知識庫搜尋 → 完整 system prompt → LLM。不需要知識庫的簡單問題,卻還要經過完整的 system prompt,造成了資源浪費,例如:打招呼浪費 1,400 tokens、預約浪費 1,400 tokens,且無法針對不同問題深度處理。
使用者輸入 → 知識庫搜尋 → 同一組 system prompt → LLM → 回覆
改善的方式是加入一個 router,先判斷使用者意圖,再分流到對應的 handler。這樣可以針對不同意圖使用不同的 system prompt、max_tokens 與知識庫搜尋策略。用意圖分流把「嗨 / 預約 / 職涯 / 履歷」分開處理,輕的輕、重的重,不浪費 token。
我們加入了一個 router,先判斷使用者意圖,再分流到對應的 handler:
使用者輸入
│
▼
┌──────────────┐
│ 意圖分類器 │ ← 用 LLM 分類,只回傳一個詞
└──────┬───────┘
│
├── greeting → 不搜知識庫,簡短歡迎,引導提問
├── career → 知識庫搜尋 + 職涯建議專用 prompt
├── resume → 知識庫搜尋 + 履歷分析專用 prompt
└── booking → 不呼叫 LLM,直接回傳預約表單連結
分類 prompt 非常簡潔,每次呼叫約 150 ~ 200 tokens:
你是一個分類器。根據使用者訊息只能回應一個詞:greeting / career / resume / booking
- greeting: 打招呼、問候、自我介紹
- career: 職涯問題、轉職建議、學習路線、薪資談判(不含履歷)
- resume: 履歷檢查、履歷修改、履歷建議
- booking: 預約、諮詢、想進一步討論
各 handler 的行為差異如下:
| 意圖 | 知識庫搜尋 | max_tokens | 說明 |
|---|---|---|---|
greeting |
❌ 無 | 80 | 不搜知識庫,簡短歡迎,引導提問 |
career |
✅ 有 | 512 | 知識庫搜尋 + 職涯建議專用 prompt |
resume |
✅ 有 | 512 | 知識庫搜尋 + 履歷分析專用 prompt |
booking |
❌ 無 | 0 | 不呼叫 LLM,直接回傳預約表單連結 |
單是這個改動,打招呼省下了 80% 以上的 token,預約諮詢更是直接歸零。但只有四種意圖的 LLM 分類還不夠穩定,而且每次路由都要等 LLM 回傳,於是進入了第二階段。
階段二:混合路由(Hybrid Router)
前面的改善會經由 LLM 進行意圖分類,這純 LLM 路由雖然比原本的單一流程好,但仍有兩個痛點:一是每次分類約 200ms 的延遲,二是當 LLM 遇到 rate limit 時,整個路由層直接癱瘓。
CareerWise 參考了 Agentic Design Pattern 中的 Routing 模式,將單一 LLM 判斷改為三層遞迴過濾、三層層層把關。需注意,這仍然是 Routing 模式的一種實作演進,而非全新的設計模式——LLM-based、Rules-based、Embedding-based 並列為路由的四種實作方式,CareerWise 將三者組合為三層架構,以兼顧速度、彈性與容錯:
使用者輸入
│
▼
┌──────────────┐
│ Layer 1 │ ← 規則路由,正則比對
│ Rules │ 零成本、零延遲
└──────┬───────┘
│ 無匹配
▼
┌──────────────┐
│ Layer 2 │ ← 嵌入路由,embedding 比對
│ Embedding │ ~50ms,語意理解強
└──────┬───────┘
│ 信心 < 0.7
▼
┌──────────────┐
│ Layer 3 │ ← LLM 路由,LLM 判斷
│ LLM │ 模糊案件才呼叫
└──────┬───────┘
│
▼
Handler
Layer 1:規則路由
用正則表達式比對高確定性的關鍵字。零 token 成本、延遲可忽略:
| 規則 | 意圖 |
|---|---|
/預約|諮詢|約時間/ |
booking |
/嗨|你好|早安|晚安|哈囉/ |
greeting |
| 其餘 | 交給 Layer 2 |
規則層只處理最明確的 case。履歷相關問題不放這裡,因為「幫我看 CV」和「我的 resume 有什麼問題」寫法太多樣,交給嵌入層更準確。
Layer 2:嵌入路由 (Embedding Based Routing)
預先為每個意圖定義 3–5 個範例句,並在系統初始化時先算好它們的向量。當使用者提問時,計算輸入與所有範例的 embedding 餘弦相似度,並取最高分:
greeting: ["你好", "嗨", "早安", "哈囉", "嗨 Summer"]
career: ["我想轉職", "面試怎麼準備", "怎麼談薪水", "該不該離職", "學習路線規劃"]
resume: ["幫我看履歷", "履歷怎麼改", "CV 要怎麼寫", "幫我檢查履歷", "我的 resume"]
booking: ["我想預約", "可以約諮詢嗎", "想進一步討論", "預約一對一"]
- 最高分 ≥ 0.7 → 直接命中該意圖
- 最高分 < 0.7 → 信心不足,交給 Layer 3
這層的優勢在於不需要 LLM 進行沉重的文字生成推理,僅需將輸入向量化後進行純數學計算,大約 50ms 即可完成,且成本極低。此外,它比單純的關鍵字比對更具語意彈性——例如使用者輸入「我想聊聊」,雖然字面上完全沒有「預約」二字,但在語意向量空間中,它會被精準地分類到 booking 意圖。
Layer 3:LLM 路由
當前兩層都無法確定時才呼叫 LLM。這在一般對話中占比不到 20%,比之前每次都要 LLM 分類節省約 80% 的 LLM 路由成本。而且當 LLM 掛掉時,Layer 1 + 2 仍然可以正常運作。
實際流程
「嗨 Summer」
→ Layer 1:規則 /^嗨/ → greeting ✅(零 token,1ms)
「幫我檢查履歷」
→ Layer 1:無匹配
→ Layer 2:嵌入比對 → resume 0.89 ✅(~50ms,零 token)
「我想問關於職涯發展的問題」
→ Layer 1:無匹配
→ Layer 2:嵌入比對 career 0.65 < 0.7(信心度不足)
→ Layer 3:LLM 判斷 → career ✅(~150 - 200 tokens)
層層把關的綜合延遲與成本,遠低於每次提問都直接盲目呼叫單層 LLM。
| Layer | 方法 | 延遲 | Token 成本 | 估計命中率 | 說明 |
|---|---|---|---|---|---|
| Layer 1 | 規則 | ~1ms | 零 | 30% | 純 Regex 字串匹配 |
| Layer 2 | 嵌入 | ~50ms | 零(Hugging Face API) | 50% | 向量相似度純數學計算 |
| Layer 3 | LLM | ~200ms | ~150 - 200 | 20% | 僅用於處理模糊極端案件 |
而且有了 graceful degradation——應對災難的情境模擬:當 LLM 遭遇 Outage 或 Rate Limit 時,Layer 1 + 2 仍然可以正常運作,只有模糊問題才會受到影響。Layer 2 是呼叫線上的 Hugging Face API,當 LLM 和 HF API 同時掛掉時,系統的最後防線是直接退回到最不依賴網路的 Layer 1 規則路由。
情境:LLM 掛掉(rate limit / outage)
│
Layer 1 ── 規則路由 ── ✅ 正常運作(純 Regex,不依賴任何外部 API)
│
Layer 2 ── 嵌入路由 ── ✅ 正常運作(Hugging Face API 呼叫)
│
Layer 3 ── LLM 路由 ── ❌ 服務掛了
│
▼
Handler(無法使用 LLM)
-
打招呼(/^嗨 ^你好/)→ Layer 1 命中 → 正常回覆,體驗完全不受影響 -
預約(/預約 諮詢/)→ Layer 1 命中 → 正常回覆,體驗完全不受影響 - 常見職涯問題 → Layer 2 嵌入比對 → 正常回覆,體驗完全不受影響
- 只有模糊問題(~20%)才會在 LLM 掛掉時被影響
階段三:提示鏈(Prompt Chaining)
履歷健檢與職涯諮詢是較為複雜的功能。原本的做法是單一步驟:使用者貼上履歷 → AI 一次給出建議。這就像請一個人同時做「閱讀理解 + 知識庫搜尋 + 文案撰寫」三份工作,AI 容易在某個環節產生幻覺,而且建議的結構也很不固定。
要怎麼解決呢?分解為三步驟提示鏈。提示鏈的概念是把一個任務拆成多個步驟,每個步驟專注一件事,步驟之間用 JSON 強制交接。這阻斷了錯誤的放大效應——第一步驟的輸出是結構化的 JSON,第二步驟的模型必須在這個結構上工作,無法隨意發散。
單一步骤:
使用者問 → AI 一次回答 → 容易幻覺、結構鬆散
提示鏈:
Step 1 (結構化提取) → JSON → Step 2 (知識庫搜尋) → JSON → Step 3 (產出報告)
Step 1 — 結構化提取
不做任何主觀分析,只把履歷原始文字轉成固定的 JSON 格式。這個步驟確保 AI 真的有讀懂履歷結構:
{
"summary": "一句話總結背景與目標",
"experience": [
{ "company": "公司名稱", "title": "職稱", "period": "期間", "highlights": ["成就1", "成就2"] }
],
"education": [
{ "school": "學校名稱", "degree": "學位", "major": "主修" }
],
"skills": ["技能1", "技能2"],
"portfolio": "網址或 null",
"github": "網址或 null",
"blog": "網址或 null"
}
Step 2 — 知識庫搜尋
接著,Step 2 會拿 Step 1 的 JSON 去比對知識庫,找出優點與可改善之處,並輸出結構化的分析結果:
{
"strengths": ["量化成果明確", "技術符合市場需求"],
"improvements": [
{ "field": "experience.0.highlights", "suggestion": "缺少 STAR 原則描述" }
],
"knowledge_matches": ["引用知識庫中關於履歷撰寫的建議"]
}
Step 3 — 產出報告
根據 Step 2 的 JSON 產出自然語言報告。這個步驟不再進行額外的邏輯推理,只專注於文案渲染,幻覺風險最低。報告結構固定為:優點 → 可改善的地方 → 具體建議 → 下一步行動。
挑戰
目前 career 走的是 SSE streaming——送出後 200ms 就看到第一個字。chain 是 3 次串行 LLM 呼叫,無法 streaming,要等全部跑完(約 3-5 秒的首字延遲(TTFT))才一次顯示。所以實作 chain 後,所有 career 問題都會失去串流體驗,因此做了以下取捨:
career handler 內部微路由
├─ 簡單問題 (短問句、單一主題) ──→ 🚀 直走單一 Prompt 流程 + 支援回應速度快、體驗佳 SSE streaming
├─ 中等問題 (需結合背景深度分析) → ⛓️ 走三階段提示鏈 (犧牲前段串流,換取更好的品質)
└─ 複雜問題 (多面向、含決策詞) ─→ 🔀 走 Parallelization 平行化處理 (後續改善計畫)
並不是所有職涯提問都需要使用沉重的提示鏈。使用者如果只是問一句「前端推薦學 Vue 還是 React?」,直接走單一層 Prompt 檢索並串流輸出,體感速度最快;只有當使用者貼入長篇大論、涉及「背景與痛點交叉分析」的中等複雜問題時,系統才會切換到提示鏈軌道。這種分級防禦的思維,讓 CareerWise 在「回應速度快、體驗佳」與「高價值諮詢的嚴謹度」之間,找到了最佳的平衡點。
職涯諮詢提示鏈
類似的思路也可以套用在職涯諮詢上。與履歷檢查相同,步驟之間以 JSON 強制交接,確保結構一致性:
Step 1 — 背景分析(輸出 JSON)
{ "current_role": "前端工程師", "years": 5, "pain_points": ["薪資成長停滯", "不確定該轉管理職還是繼續鑽研技術"] }
Step 2 — 知識庫比對(輸出 JSON)
{
"analysis_tags": ["轉職策略", "薪資談判"],
"gaps": ["缺乏跨團隊協調經驗的論述"],
"strategic_advice": ["建議先在現職爭取架構主導權,累積筹碼再談判"]
}
Step 3 — 個人化建議(產出自然語言報告)
結合背景 JSON + 知識庫比對結果,產出個人化建議
提示鏈的代價是 token 成本變為三倍,回應時間也變長。但對於履歷檢查與職涯諮詢這種高價值場景,品質的提升遠大於成本的增加。
階段四:反思模式(Reflection)
即使是三步驟提示鏈,最後的輸出仍有可能有幻覺。我們需要一道最後的品質防線 - 反思審查器。
反思模式分為兩種實作方式:
- Self-Reflection(自我反思):同一個 AI 在產出後以「審查員」角色檢查自己的輸出,優點是實作簡單,但缺點是 AI 可能難以跳出自己的思維盲點。
- Producer-Critic 模型:產出者(Producer)與審查者(Critic)分屬不同的角色,使用各自的 system prompt。審查者以全新的視角檢查產出,不被先前的生成邏輯影響,結果更客觀可靠。
CareerWise 採用第二種 Producer-Critic 模型,審查者使用獨立的審查 prompt,與產出者的 system prompt 完全分開。
舉例來說,履歷報告或職涯建議產出後,Critic AI 以「審查員」角色檢查一次輸出。檢查標準有四項:
- 是否有事實錯誤或幻覺
- 是否給出具體可行的建議,而非空泛的鼓勵
- 語氣是否恰當(直接但不失溫暖)
- 是否根據使用者的具體情況來回答
Step 1 → JSON → Step 2 → JSON → Step 3 → JSON → 反思 → 輸出
│
└─ 有問題就修正,沒問題才放行
反思模式每次約 100 tokens,只對履歷與職涯等複雜度高的問題觸發。打招呼和預約則直接跳過。
總結
回顧整個重構歷程,架構的演進可以用一張圖總結:
Before(原始系統) After(重構後)
┌──────────────┐ ┌──────────────┐
│ 使用者輸入 │ │ 使用者輸入 │
└──────┬───────┘ └──────┬───────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ 知識庫關鍵字搜尋 │ │ Layer 1 規則路由 │ ← 1ms,零 token
└──────┬──────----─┘ └─----────┬─-──────┘
│ │ 無匹配
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ system prompt │ │ Layer 2 嵌入路由 │ ← 50ms,零 token
│ (萬用同一組) │ └─────---─┬─────-──┘
└──────┬─────----──┘ │ 信心 < 0.7
│ ▼
▼ ┌──────────────────┐
┌──────────────────┐ │ Layer 3 LLM 路由 │ ← ~200ms,~150 - 200 tokens
│ LLM 一次生成 │ └────────┬──────────┘
│ (max 1024 tok) │ │
└──────┬─────----──┘ ▼
│ ┌──────────────────┐
▼ │ Handler 分流 │
┌──────────────┐ ├──────────────────┤
│ JSON 回傳 │ │ greeting ✂️ 80t │
└──────────────┘ │ career 📚 512t │
│ resume 📚 512t │
── 一條路走到底 ── │ booking 🔗 0t │
不分意圖、浪費 token └──────────────────┘
❌ 無 streaming(等 LLM 全部跑完) │
易幻覺 ▼
┌──────────────────┐
│ 簡單職涯問題 ────│ 🚀 SSE streaming
│ (單一 Prompt) │ 200ms 首字
└──────────────────┘
▼
┌──────────────────┐
│ 提示鏈(履歷用) │
│ Step 1 → JSON │
│ Step 2 → JSON │
│ Step 3 → 報告 │
└────────┬─────────┘
▼
┌──────────────────┐
│ 反思(Producer- │
│ Critic 審查) │
└────────┬─────────┘
▼
┌──────────────┐
│ 最終輸出 │
└──────────────┘
── 三層路由 + 提示鏈 + 反思 ──
意圖分流、JSON 交接、品質把關
最後總結各階段的實作優先級,按效益與難度排序:
| 優先級 | 項目 | 預期效益 | 實作難度 |
|---|---|---|---|
| 1 | 混合路由(三層) | 降低 LLM 呼叫次數,省 token,graceful degradation | 中 |
| 2 | 提示鏈(履歷檢查) | 提升分析品質,減少幻覺 | 中 |
| 3 | 反思模式 | 降低高風險回應的幻覺 | 低 |
從單一流程到多層路由、提示鏈與反思模式,每一步的改進都是在 token 成本、回應品質與延遲之間做取捨。這也正是 Agentic Design Pattern 的核心精神——不是盲目地「讓 AI 做更多事」,而是有策略地引導 AI 在正確的地方做正確的事,在資源與效果之間找到最佳的平衡點。