CareerWise 平行化架構設計 — 讓 AI 從單一觀點升級為團隊分析
13 Jul 2026在上一篇文章中,我們記錄了 CareerWise 如何從一條 straight-through pipeline,逐步導入路由、提示鏈與反思模式。這三種 pattern 解決了意圖分流、多步驟分析與品質把關的問題,但它們有一個共通限制:所有環節都是循序的。
想像一下,使用者問了一句話:「Summer,我該不該從金融轉前端?值得嗎?」
這句話同時涉及轉職決策、薪資評估、技能轉換、市場趨勢。在循序的架構下,系統只能先搜尋「轉職」的知識庫 chunk,等搜尋完成後再餵給 LLM;下一個 chunk 要等前一個處理完畢。就像廚房裡只有一個廚師,必須先做前菜、再做主餐、再做甜點——明明這些事可以同時進行。
這就是平行化(Parallelization)要解決的問題。
三種路線的探索
在決定最終方案前,我們評估了三種可能的平行化方向。
選項一:知識庫多面向平行搜尋
原本的知識庫搜尋是一次撈 top-3 chunks(chunk 是將知識庫文章切成的小段落,top-3 指相似度最高的前 3 段),不分主題,撈回來後全部塞進 LLM 的 context 當作參考資料。改成先判斷使用者問題觸及哪些面向,再平行搜尋不同領域的內容。
「該不該跳槽?」
│
├─ 平行搜尋 ─┬─ 薪資談判相關資料
│ ├─ 轉職策略相關資料
│ └─ 面試準備相關資料
│
└─ 合併後餵給 LLM → 綜合產出
這個作法技術成本極低。原本只需要對使用者的問題做一次 embedding 查詢(把文字轉成向量,再跟知識庫比對相似度),現在雖然變成三個面向各查一次,但 embedding 計算本身就是輕量級的純數學運算,而且 Hugging Face 的 embedding API 支援 batch 一次送三個查詢,延遲與成本跟查一次幾乎沒有差別。效果是 context 更多元,從不分面向的一次 top-3,變成三個面向各取 top-2,總共 6 段 chunks 涵蓋更廣的範圍。
選項二:多 Agent 平行分析
不只在知識庫層面平行,而是讓 LLM 同時以不同角色分析同一個問題,再用一個合併步驟產出最終回答。
「該不該跳槽?」
│
├─ Agent A:分析目前薪資是否低於市場行情
├─ Agent B:分析技能成長空間與職涯發展
└─ Agent C:分析轉職風險與時機
│
└─ 合併代理(⛔ 禁止新增外部知識)→ 最終報告
這就像 Google ADK 的「研究員 A/B/C + 總編輯」模式。三個專家同時工作,最後由總編輯彙整成一份報告。
代價是 LLM 呼叫次數從原本的 2 次變為 4 次——原本是「1 次生成 + 1 次反思」(反思獨立呼叫一次 LLM 來審查),平行化則改用 JSON schema(強制 Agent 輸出固定格式)+ 合併層防禦(⛔ 禁止合併代理新增外部知識)來取代反思,所以是「3 位 Agent 各呼叫 1 次 LLM 生成 + 1 次合併 LLM 呼叫」,共 4 次。token 成本約為原本的 2 倍。而且合併層必須被嚴格限制——他不能自己跑去新增知識,只能根據拿到手的數據寫報告。如果不這樣做,總編輯可能在報告中加入三位 Agent 從未提及的資訊,等於讓未經知識庫驗證的內容繞過所有檢查,直接出現在最終輸出中,讓平行化反而變成幻覺的加速器。
選項三:Router 層平行化(不採用)
還曾想過一個極端作法——把現有的三層路由(Layer 1 規則 → Layer 2 嵌入 → Layer 3 LLM,依序 cascade)改成平行執行。原本的 cascade 是依序檢查:Layer 1 沒命中才進 Layer 2,Layer 2 信心不足才進 Layer 3。平行執行則是三層同時觸發,誰先回傳且信心夠高就用誰的結果:
cascade(現有): 平行執行(設想):
Layer 1 ── 命中 → ✅ Layer 1 ──┐
│ 沒命中 Layer 2 ──┼─ 同時跑 → 取最快+最準
▼ Layer 3 ──┘
Layer 2 ── 命中 → ✅
│ 信心不足 問題:每則訊息都要同時運作三層
▼ 原本 80% 用不到 LLM 的訊息,
Layer 3 ── LLM → ✅ 現在全部都要等 LLM 就緒
但仔細一想,cascade 路由的設計初衷就是為了省成本:先用零成本的規則層過濾掉簡單問題(約 30%),再用低成本的嵌入層處理常見問題(約 50%),最後只有模糊案件(約 20%)才需要呼叫昂貴的 LLM 層。如果改成平行執行,等於每則訊息都要同時觸發三層,白白浪費 embedding 和 LLM 呼叫——原本 80% 的訊息根本不需要 LLM 判斷。很快就放棄了。
最終決定:選項一 + 選項二同時採用
這不是二選一,而是疊加使用。平行搜尋的結果直接作為各 Agent 的 domain context,讓每個 Agent 在分析時擁有該領域的知識庫內容。兩者互補,效果更好。
哪些問題才值得動用平行化?
平行化不是萬靈丹。對於簡單的打招呼或預約,動用三個 Agent 來分析根本是殺雞用牛刀。
因此我們設計了 classifyTier,將問題分為三個層級:
| Tier | 判斷條件 | 走哪條路 |
|---|---|---|
| simple | 短問句(≤10 字)、單一主題、無決策詞 | SSE streaming,200ms 首字即見 |
| medium | 長問句但主題集中 | career chain(3-step 提示鏈) |
| complex | 含多個問句、含決策詞(「該不該」「值不值得」)、長度 >25 字、跨領域 | 平行化 |
用實際使用者輸入來看這些邊界的運作:
- 「面試要怎麼準備」→ simple(10 字以下、無決策詞)
- 「我想轉職前端但沒有相關經驗」→ medium(長問句但單一主題)
- 「該不該離職」→ complex(含「該不該」決策詞)
- 「前端還後端?該不該離職?」→ complex(兩個問句)
平行化只套用在 career 意圖,且只在 complex 時觸發。greeting / booking / resume 維持原有路徑不變。
系統架構
當 classifyTier 回傳 complex 時,系統進入平行化流程:
使用者問「該不該從金融轉前端?」
│
classifyTier → complex
│
┌── 平行知識庫搜尋 ─────────────────────┐
│ │
├─ Query A(薪資/市場行情)→ top-2 │
├─ Query B(技能成長/轉職)→ top-2 │
└─ Query C(面試/實戰準備)→ top-2 │
└──────────────┬─────────────────────────┘
│
┌── 平行 Agent 分析 ─────────────────────┐
│ (Promise.all, 3 × llama-3.1-8b) │
│ │
├─ Agent A:市場分析師 → JSON │
├─ Agent B:成長顧問 → JSON │
└─ Agent C:實戰教練 → JSON │
└──────────────┬─────────────────────────┘
│
┌── 合併代理 ───────────────────────────┐
│ ⛔ 禁止新增外部知識 │
│ temp: 0.5(低溫,抑制幻覺) │
└──────────────┬─────────────────────────┘
│
結構化報告(非串流)
三個 Agent 的分工
Agent A 像一個產業分析師,手上拿著薪資調查報告,分析使用者在市場上的競爭力。Agent B 像職涯教練,看著技能樹思考下一步該往哪走。Agent C 像面試教官,翻著履歷找破綻。
三個角色同時工作,各自有專屬的知識庫領域與角色 prompt:
| Agent | 角色 | 知識庫領域 | temperature |
|---|---|---|---|
| A | 市場分析師 | 薪資談判、市場行情、職缺趨勢 | 0.3 |
| B | 成長顧問 | 學習路線、技能評估、轉職策略 | 0.3 |
| C | 實戰教練 | 面試準備、履歷優化、soft skills | 0.3 |
temperature 是 LLM 的隨機性參數,範圍 0–2。愈低輸出愈固定、愈可預測;愈高愈有創意但也愈容易偏離。Agent 設 0.3(偏低)確保分析穩定不亂講,合併代理設 0.5(中間偏低)保留一點靈活度同時抑制幻覺。
所有 Agent 共用同一套 prompt 模板,僅角色定義與 context 不同:
[角色定義]
你是 {Agent 角色名稱}。{一句話描述核心任務}
[任務說明]
根據以下 {domain} 相關的知識庫內容,分析使用者的狀況。
[輸入規範]
知識庫內容:
{context}
使用者問題:
{userMessage}
[輸出規範]
請回傳嚴格符合以下 JSON schema 的結果:
{
"findings": ["具體發現1", "具體發現2", "具體發現3"],
"insights": ["關鍵洞察1", "關鍵洞察2"],
"risk_level": "low" | "medium" | "high"
}
[行為限制]
- 只能根據上述提供的知識庫內容回答
- findings 每條必須是具體事實,而非空泛描述
- insights 必須是可行動的建議
JSON schema + 低溫(0.3)+ 行為限制,三道防線確保 Agent 輸出穩定且可預測。
合併層:最重要的防禦機制
平行化最危險的地方在於:三個 Agent 各自產出分析後,如果總編輯亂加自己的意見,幻覺就會像雪球一樣滾大。
因此合併代理被賦予一條鐵律:⛔ 禁止新增外部知識,只能基於三位 Agent 的回報撰寫報告。
就像三個研究員分別做完實驗,把數據交給主筆。主筆不能自己跑去再做一次實驗,只能根據拿到的數據寫論文。這樣才能確保資料純度,防止幻覺擴散。
合併代理的參數也特意調低溫度:temp: 0.5,低溫讓輸出更保守、更可預測。
為什麼平行化路徑不需要再加 Reflection?
這是我們在設計過程中認真討論過的問題。最終決定不加,理由有三個:
- 成本考量:平行化已經是 4 次 LLM 呼叫(3 Agent + 1 合併),比原本的 2 次多一倍。再加 reflection 變成 5 次,邊際效益遞減。
- 已有雙重把關:JSON schema 強制輸出格式 + 合併層禁止新增外部知識,已經承擔了主要的幻覺防禦角色。
- 延遲:每多一次串行 LLM 呼叫增加 500ms–1s,loading 時間已經偏長。
串流體驗的權衡
原本 career 問題走 SSE streaming,使用者送出後 200ms 就看到第一個字逐字跳出。平行化必須等三個 Agent + 合併完成才能輸出,無法 streaming。
我們的解法是三階分流 + 前端 loading spinner:
- simple → 維持 SSE streaming,200ms 首字送達(不受影響)
- medium 和 complex → 前端顯示「Summer 正在思考⋯」spinner,完成後 → 一次顯示完整結構化報告
前端在同一個 API endpoint 根據 mode 切換處理方式:mode: "stream" 走既有 SSE,mode: "full" 等完整 JSON body response 後一次 render。
一般簡單問題維持 200ms 首字的串流體驗,複雜問題改用 3–5 秒 loading 換取三面向深度分析。這不是 UX 退步——讓使用者根據問題類型獲得最適合的回應形式,才是好的設計。
Loading 時使用者發送新訊息怎麼辦?
這是平行化會遇到的特殊問題。當系統正在跑三個 Agent(約 3–5 秒)時,使用者如果又發送「算了當我沒問」,該怎麼處理?
評估了三種作法後,我們選擇了最簡單的——阻擋輸入。只在 classifyTier 回傳 medium 或 complex 時 disable input,前端顯示 spinner +「Summer 正在思考⋯」。3–5 秒 loading 在業界是可以接受的(類似 ChatGPT 的思考延遲),完成後恢復 input 並顯示回覆。
從失敗中學習:Timeout 的故事
第一次測試平行化時,我們輸入了一句話:
「該不該從金融業轉職到前端工程師?值得嗎?」
預期應該看到四章節的結構化報告——市場分析、成長建議、實戰準備、行動建議。結果回來的卻是這樣的內容:
轉職的決定是值得考慮的,特別是當你想挑戰新的領域時。
你是否具備必要的技能和經驗?
程式設計基礎:你是否具備基本的程式設計知識和技能?
前端工程師的知識:你是否具備前端工程師的基礎知識?
...
沒有知識庫內容、沒有 Summer 風格、沒有結構化章節——看起來就像直接問 ChatGPT 的回答。
出了什麼問題?
原因是 Groq API 回應時間超過了 10s timeout。三個 Agent 同時被取消,系統一路降級到最弱的通用路徑——只有一句「你是 Summer,請根據專業知識回答」,完全沒有知識庫上下文。
把 timeout 從 10s 調整為 30s 後,第二次測試成功。回覆完全符合設計預期:
## 職涯建議報告
## 市場分析
金融業背景對於財務分析和市場分析的技能轉換可能有一定幫助。
前端工程師的需求量在市場上相對穩定。
轉職前端工程師可能需要額外的培訓和學習。
## 成長建議
考慮轉職前端工程師的同時,應該評估自己的財務分析和市場分析技能是否可轉換。
應該研究前端工程師的培訓和學習需求。
## 實戰準備
建議使用者先發展自己的軟硬技能,例如學習前端程式語言和框架。
## 行動建議
評估自身技能和經驗: 使用者應該評估自己的財務分析和市場分析技能是否可轉換。
研究轉職前端工程師的需求和市場趨勢。
兩次測試的對照:
| 面向 | timeout 10s(失敗) | timeout 30s(成功) |
|---|---|---|
| 路徑 | 降級到通用回答 | 三 Agent + 合併 |
| 輸出格式 | 優點/缺點/評估/建議 | 市場/成長/實戰/行動 |
| 知識庫內容 | ❌ 無 | ✅ 三領域 |
| Summer 風格 | ❌ 像 ChatGPT | ✅ |
這個經驗告訴我們:系統設計不能只看正常流程,還要考慮失敗情境下的降級品質。
為此我們設計了完整的容錯策略:
| 失敗狀況 | 處理方式 |
|---|---|
| 單一 Agent 超時或失敗 | 其餘兩位繼續,合併時註記該面向暫無資料 |
| 全部 Agent 失敗 | 降級回傳統 streaming 路徑 |
| 合併代理失敗 | 降級回傳統 streaming,或直接輸出三位 Agent 的原始回覆 |
三階分流測試結果
在正式上線前,我們針對三種路徑做了完整的測試驗證。
simple → streaming:「嗨」
輸入: 嗨
結果: ✅ 走 streaming 路徑,即時逐字顯示 Summer 風格的簡短回應。
simple → streaming:「面試要怎麼準備」
輸入: 面試要怎麼準備
結果: ✅ classifyTier 判定為 simple(10 字以下、無決策詞),走 SSE streaming,即時顯示。回應內容包含面試類型分析、準備方法與行動建議。
medium → career chain:「我想轉職前端但沒有相關經驗」
輸入: 我想轉職前端但沒有相關經驗
結果: ✅ 判定為 medium,走 career chain(3-step 提示鏈)。前端顯示 loading spinner,完成後一次輸出結構化報告,含背景分析、知識庫比對與個人化建議。
complex → parallelization:「該不該離職」
輸入: 該不該離職
結果: ✅ 判定為 complex,走平行化路徑。三個 Agent 同步分析,前端 loading 後一次顯示三面向結構化報告(市場分析 / 成長建議 / 實戰準備 / 行動建議)。其中市場分析顯示「此面向暫無分析資料」,屬知識庫內容不足的正常現象,不影響其餘兩個 Agent 的分析。
complex → parallelization:「前端還後端?該不該離職?」
輸入: 前端還後端?該不該離職?
結果: ✅ 含兩個問句 + 決策詞「該不該」,判定為 complex。平行化正常觸發,輸出完整的四章節結構化報告。
快速判斷法:從輸出結構分辨路徑
不需看程式碼,只看回覆內容就能判斷走的是哪條路徑:
| 判斷線索 | 平行化(complex) | Career Chain(medium) | Streaming(simple) |
|---|---|---|---|
| 章節標題 | 市場分析 / 成長建議 / 實戰準備 / 行動建議 | 現狀分析 / 關鍵發現 / 具體建議 / 下一步行動 | 無固定格式 |
| 開頭 | ## 職涯建議報告 |
## 面試準備報告 等主題標題 |
「你好!我是 Summer…」 |
| 語氣 | 第三人稱客觀(「使用者應該…」) | 第二人稱建議 | 第一人稱對話 |
| 前端行為 | loading → 一次出現 | loading → 一次出現 | 逐字跳出 |
邊界驗證
classifyTier 的判斷邏輯也經過逐一驗證:
| 輸入 | 結果 | 原因 |
|---|---|---|
| 「嗨」 | simple | 短問句,無決策詞 |
| 「怎麼談薪水」 | simple | 短問句,單一主題 |
| 「面試要怎麼準備」 | simple | 短問句,單一主題 |
| 「我想轉職到前端工程師」 | medium | 長問句但無決策詞 |
| 「該不該離職」 | complex | 含「該不該」決策詞 |
| 「前端還後端?該不該離職?」 | complex | 含多問句 + 決策詞 |
總結
平行化讓 CareerWise 的 AI 從單一觀點升級為團隊分析:
平行化前:單 Agent + 單一知識庫搜尋 → 單面向分析
平行化後:三 Agent + 三領域搜尋 + 合併 → 多面向深度分析
token 成本約 2 倍,但獲得了 3 倍的分析廣度——市場、成長、實戰三個面向同時覆蓋。
從路由到提示鏈、反思、再到平行化,每一步都是在 token 成本、回應品質與使用者體驗之間做取捨。Agentic Design Pattern 提供的不是銀彈,而是一套幫助我們做出這些取捨的思考框架。
下一步的改善方向包括:降低降級發生率(更穩定的 LLM provider、更精準的 timeout 設定)、以及引入 caching 減少重複查詢的成本。