我怎麼用 7 個 AI 模型協作,成本砍 70%——完整路由設定公開
從「全部丟 GPT」演化成 7 個模型協作的架構。路由邏輯、成本結構、踩過的坑,附可直接跑的 Python router。
大多數人用 AI 的方式是:打開 ChatGPT,什麼都丟進去。進階一點的人會挑模型——「這個任務用 Claude 比較好」「那個用 Gemini」。但很少人認真想過:能不能建一套系統,讓任務自動流向最適合的模型?
我花了半年時間,從「全部丟 GPT」演化成 7 個模型協作的架構。這篇文章把完整設定攤開來,包括路由邏輯、成本結構、踩過的坑。可以直接抄。
先講結論:為什麼不是一個模型打天下
一個模型打天下有三個問題:
成本失控。 簡單的格式轉換、摘要、制式回覆佔了八成請求,全丟旗艦模型等於用賓士載垃圾。
延遲不均。 旗艦模型推理慢,簡單任務等 3-5 秒是不必要的。本地模型 200ms 就能回。
單點故障。 API 掛了就全掛。多模型意味著 fallback 路徑。
七位一體:每個模型為什麼在這裡
不是為了湊數。每個模型進來都是因為有一個具體缺口:
角色 模型 為什麼是它
─────────────────────────────────────────────────────
Architect Claude Opus 推理天花板,不可逆決策
Engineer Codex sandbox 可跑測試驗證,
code review cost/quality 甜蜜點
Analyst Gemini 100 萬 token context,
整個 repo 丟進去分析
Local Brain Qwen 3.6 35B (A3B) 自家 DGX Spark 跑,
批次 / 隱私 / 投票,零 API 費
Writer Gemma4 31B 本地跑,中文寫作品質高,
文章生成 / 改寫
Scout-1 Perplexity Max 即時搜尋 + 自動帶引用
Scout-2 SuperGrok X / 社群即時情報
為什麼 Scout 要兩個?
Perplexity 擅長結構化 research(學術、技術文件、帶引用),SuperGrok 擅長即時社群(X 趨勢、輿論風向)。兩者搜的東西不重疊,不是冗餘。
為什麼本地模型要兩個?
Qwen 3.6 是 MoE 架構(35B 參數但只啟動 3B),推理極快、記憶體佔用低,適合大量批次和投票。Gemma4 31B 是 dense 架構,寫作品質更好但推理較慢,只在需要中文寫作時才叫它。不是同時跑。
路由決策樹:什麼任務派給誰
這是我實際在用的判斷邏輯:
收到任務
│
├─ 單檔、≤3 行、明確可逆?
│ └─ YES → fast-path:Claude 直接做,不路由
│
├─ 有時效性需求?(今天的新聞、現在的趨勢)
│ └─ YES → Scout-1 或 Scout-2
│ (學術/技術 → Perplexity,社群/X → Grok)
│
├─ 碰到不認識的專有名詞?
│ └─ YES → 先查個人知識庫(4200+ 書籤語意搜尋)
│ 查到 → 直接用,不花 API
│ 查不到 → 再派 Scout
│
├─ 寫 code / refactor?
│ └─ YES → Codex(有 sandbox,能跑測試驗證)
│
├─ 程式碼分析 / 多方案比較?
│ └─ YES → Gemini(大 context 窗口吃整包)
│
├─ 簡單任務 / 隱私資料 / 批次處理 / Council 投票?
│ └─ YES → Qwen 本地跑
│
└─ 架構設計 / 不可逆決策 / 最終裁判?
└─ YES → Claude Opus
四個預設模式
不是每次都跑完整棵樹。我把常見情境打包成四個 preset:
| Preset | 條件 | 誰參與 |
|---|---|---|
| fast-path | 單檔 ≤3 行、明確、可逆 | Claude 自己做 |
| solo+review | 一般實作 | Claude 做 + Codex review |
| council-lite | 跨系統、中風險 | Claude + Codex + 1 analyst |
| full-council | 不可逆、治理、戰略 | 全員投票 |
90% 的時間在 fast-path 和 solo+review。full-council 是 escalation,不是常態。
實際怎麼呼叫:CLI 指令
每個模型都透過 CLI 操作,統一用 file I/O 傳遞(寫 briefing 檔、讀 answer 檔):
# Codex — sandbox 內執行,可寫檔但隔離
codex exec --sandbox workspace-write \
--skip-git-repo-check < briefing.md > answer.md
# Gemini — pipe 模式
gemini -p "Execute the briefing on stdin." \
< briefing.md > answer.md
# Grok — 關掉 web search 避免干擾
grok -p "$(cat briefing.md)" \
--disable-web-search 2>/dev/null > answer.md
# Qwen(本地 DGX Spark)
ollama run qwen3.6 < briefing.md > answer.md
# Gemma4(本地 MBP 或 DGX)
ollama run gemma4:31b < briefing.md > answer.md
Briefing 檔格式固定四段:TASK / CONTEXT(貼真 code)/ CONSTRAINTS / VERIFY。不給完整 context 的 briefing 等於叫人矇眼做事。
成本結構:真實數字
以一週工作日估算:
任務分佈 模型 單位成本 週成本估算
────────────────────────────────────────────────────────────
60% 日常/批次/投票 Qwen 本地 $0 $0
15% Code review Codex ~$0.003/req ~$2
10% 分析/比較 Gemini ~$0.005/req ~$2
10% Scout research Perplexity/ ~$0.01/req ~$3
Grok
5% 架構/裁判 Claude Opus ~$0.05/req ~$5
────────────────────────────────────────────────────────────
合計 ~$12/週
如果全部用 Claude Opus:同樣的量大約 $35-40/週。差距是 3 倍。
如果你沒有 DGX / 本地 GPU,把 Qwen 換成 Gemini Flash 或 GPT-4o-mini,成本會從 $0 變成 $1-2/週,整體還是比全用旗艦便宜很多。
踩過的坑(省你踩一次)
坑 1:模型切換後 payload 不相容
Ollama 換模型版本後,max_tokens 變成 max_completion_tokens、response_format 格式變了。結果 scoring pipeline 靜默失敗,跑了兩天才發現分數全是 0。
教訓:模型更換必須與 bug 修復分開做。同時改會互相掩蓋失敗。換模型三步驟:① 檢查 payload schema ② 確認 VRAM ③ 跑測試比對結果。
坑 2:本地模型 context 太長吃光記憶體
DGX Spark 128GB unified memory。Ollama 對 ≥48GB 設備自動拉 context 到 256K,結果 swap thrashing,第一次 LLM call 卡了 28 分鐘。
教訓:一定要手動設 num_ctx=8192。不要信任預設值。
坑 3:把 code review 也交給最強模型
一開始覺得 review 需要最強的推理。後來發現 Codex 的 review 品質不差,而且它有 sandbox 可以真的跑程式驗證,Claude 反而只能看。
教訓:最強 ≠ 最適合。Codex 在 code review 這個特定任務上,因為工具能力(sandbox)反而比 Claude 更合適。
坑 4:三家共識不等於正確答案
Council 投票時,Codex / Gemini / Grok 三方都說「方案 A 好」。後來 Perplexity 帶著 GitHub issues 和 benchmark 數據說「方案 A 在你的硬體上會失敗」,結果是對的。
教訓:一個帶完整 evidence 的反對意見,可以推翻三方共識。投票制度要保護 dissent,不能靠多數決壓過去。
從兩層開始就好
看到這裡可能覺得太複雜。其實不用一開始就搞七個模型。我的建議:
第一步:把你現在的任務分成「需要推理」和「不需要推理」兩堆。
第二步:不需要推理的,換一個便宜模型(Gemini Flash / GPT-4o-mini / 本地 Ollama)。光這步就能砍一半成本。
第三步:觀察哪些任務便宜模型做不好。那些就是你第三層的候選。
架構是長出來的,不是第一天設計出來的。但前提是你要開始分流。
附錄:最小可執行路由器(單檔,15 分鐘跑通)
光講概念不夠,這裡附一個可以直接跑的 Python router。單檔、無外部依賴、120 行。
"""router.py — 最小多模型路由器"""
import shutil, subprocess, sys, time
# ── 路由規則:keyword → model ──
RULES = [
("local", ["翻譯","摘要","格式化","列出","整理","簡單","分類"]),
("codex", ["寫 code","寫程式","refactor","debug","bug","code review",
"review","decorator","api","endpoint"]),
("gemini", ["分析","比較","整個 repo","大量","讀完這份","對比"]),
("scout", ["最近","趨勢","新聞","現在","即時","今天"]),
("opus", ["架構","不可逆","戰略","決策","migrate","治理"]),
]
# ── 模型指令 ──
MODELS = {
"local": {"cmd": ["ollama","run","qwen3.6"], "stdin": True},
"codex": {"cmd": ["codex","exec","--sandbox","workspace-write",
"--skip-git-repo-check"], "stdin": False},
"gemini": {"cmd": ["gemini","-p"], "stdin": False},
"scout": {"cmd": ["gemini","-p"], "stdin": False},
"opus": {"cmd": ["claude","-p"], "stdin": False},
}
# ── Fallback 鏈 ──
FALLBACK = {
"codex": ["gemini","opus"], "gemini": ["opus","local"],
"scout": ["gemini","local"], "opus": ["gemini"],
"local": ["gemini"],
}
def classify(task):
for key, keywords in RULES:
if any(kw in task.lower() for kw in keywords):
return key
return "local"
def resolve(key):
if shutil.which(MODELS[key]["cmd"][0]):
return key
for fb in FALLBACK.get(key, []):
if shutil.which(MODELS[fb]["cmd"][0]):
print(f" fallback → {fb}", file=sys.stderr)
return fb
return "local"
def call(key, prompt):
cfg = MODELS[key]
cmd = cfg["cmd"] if cfg["stdin"] else cfg["cmd"] + [prompt]
inp = prompt if cfg["stdin"] else None
t = time.monotonic()
r = subprocess.run(cmd, capture_output=True, text=True,
timeout=120, input=inp)
return r.stdout.strip(), time.monotonic() - t
if __name__ == "__main__":
task = " ".join(sys.argv[1:]) or input("Task: ")
route = classify(task)
final = resolve(route)
print(f"[{route} → {final}]", file=sys.stderr)
output, elapsed = call(final, task)
print(output)
print(f"[{elapsed:.1f}s]", file=sys.stderr)
跑法
# 安裝(你可能已經有了)
# brew install ollama # 或 https://ollama.com
# ollama pull qwen3.6
# 基本用法
python router.py "翻譯成英文:今天天氣很好"
# → [local → local] → Qwen 本地處理,0 成本
python router.py "幫我寫一個 rate limiter"
# → [codex → codex] → Codex sandbox 執行
python router.py "分析這兩個方案的優缺點"
# → [gemini → gemini] → Gemini 大 context 分析
python router.py "最近 AI agent 有什麼新方向"
# → [scout → gemini] → Scout 不可用時 fallback 到 Gemini
# Dry run(只看路由不執行)
python router.py "這個架構怎麼設計" --dry-run
關鍵設計
為什麼用 keyword 而不是 LLM 分類? 因為路由本身不該吃 token。keyword 匹配 0 成本、<1ms。等你的任務量大到 keyword 不夠用了,再換成小模型分類也不遲。
Fallback 鏈怎麼設計? 每個模型有一條降級路徑。Codex 掛了走 Gemini,Gemini 掛了走 Opus,最後都能 fallback 到本地。生產環境不能有「某個 API 掛了就整個停」的情況。
為什麼不做 retry? 簡單。retry 加上指數退避、最大次數、circuit breaker,會把這 120 行膨脹到 400 行。先跑通,再加防禦。
踩坑補充:觸發條件和解法
四家評審說踩坑細節不夠深,這裡補完:
坑 1 詳解:Payload 不相容
觸發條件:Ollama 從 qwen2.5 升級到 qwen3 時發生。max_tokens 參數被重新命名為 max_completion_tokens,response_format: {"type": "json"} 在新版需要改成 format: "json"。
症狀:不會報錯。模型照樣回覆,但 JSON 輸出格式壞了,下游 parsing 全拿到空值。我的 scoring pipeline 靜默跑了兩天才發現分數全是 0。
解法:模型更換三步驟(鐵律)——① 先在測試環境比對 API schema diff ② 確認 VRAM 夠 ③ 跑 benchmark 比對輸出。三步分開做,不與 bug fix 混在同一個 commit。
坑 2 詳解:Context 吃光記憶體
觸發條件:DGX Spark 128GB unified memory。Ollama 偵測到 ≥48GB RAM 的設備,會自動把 context window 拉到 256K token。一個 8K 的 prompt 就足以觸發——因為 KV cache 是跟著 context window 配置的,不是跟著實際 prompt 長度。
症狀:不是 OOM crash。Linux kernel 開始 swap,表面上 Ollama 還活著,但第一次推論卡了 28 分鐘。free -h 看到 available < 5GB,ollama ps 看到 CONTEXT 欄位顯示 256K。
解法:API 呼叫一定帶 "options": {"num_ctx": 8192}。不要信任 Ollama 的預設值。如果用 native /api/chat(推薦),直接塞在 options 裡;如果走 OpenAI-compatible endpoint,要注意 num_ctx 不是 OpenAI 標準參數,Ollama 會靜默忽略。
我是江中喬,一位具有 TPM 與產品管理背景的 AI 系統建構者,目前專注於 AI 認知增強系統與多 Agent 協作架構的設計與實踐。