CareerWise 平行化架構設計 — 讓 AI 從單一觀點升級為團隊分析

上一篇文章中,我們記錄了 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 字、跨領域 平行化

用實際使用者輸入來看這些邊界的運作:

平行化只套用在 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?

這是我們在設計過程中認真討論過的問題。最終決定不加,理由有三個:

  1. 成本考量:平行化已經是 4 次 LLM 呼叫(3 Agent + 1 合併),比原本的 2 次多一倍。再加 reflection 變成 5 次,邊際效益遞減。
  2. 已有雙重把關:JSON schema 強制輸出格式 + 合併層禁止新增外部知識,已經承擔了主要的幻覺防禦角色。
  3. 延遲:每多一次串行 LLM 呼叫增加 500ms–1s,loading 時間已經偏長。

串流體驗的權衡

原本 career 問題走 SSE streaming,使用者送出後 200ms 就看到第一個字逐字跳出。平行化必須等三個 Agent + 合併完成才能輸出,無法 streaming。

我們的解法是三階分流 + 前端 loading 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 減少重複查詢的成本。


參考資料


CareerWise


Agentic Design Pattern Parallelization CareerWise AI Agentic AI LLM Design Pattern Architecture