All checks were successful
CD Pipeline / deploy (push) Successful in 2m45s
Operation Ollama-First v5.0 / Phase 21 — 動態路由治理 services/llm_model_router.py (160+ 行) - 純規則引擎,零 LLM 成本(Python lambda predicate) - 6 caller × 12 條路由規則: • sales_copy: 短文 < 100 字 → gemma3:4b / 長文 → llama3.1:8b • hermes_analyst: gap > 20% 或銷量 < -50% → qwen3:14b / 預設 hermes3 • aider_heal: diff > 200 行 → qwen2.5-coder:32b / 預設 7b • openclaw_qa: query > 200 字或 multi_turn → qwen3:14b / 預設 qwen2.5:7b-instruct • ppt_vision: minicpm 不健康 → llava / 預設 minicpm-v • ea_engine: require_chain_of_thought → deepseek-r1:14b / 預設 Gemini - feature flag MODEL_ROUTER_ENABLED 預設 OFF(向下相容) - 失敗安全:predicate 例外 skip 到下一條 tests/test_llm_model_router.py (18 tests 全綠) - T1 flag OFF 不路由 - T2 sales_copy 短/長文路由 - T3 hermes 簡單/複雜 SKU - T4 aider_heal 簡單/重構 - T5 ppt_vision 主備援 - T6 ea_engine CoT 路由 - T7 predicate 例外容錯 - T8 utility 函數 ADR-034 — Caller × Context 動態 Model Router - 6 caller 路由規則對應表 - 5 段否決方案(LLM-based / hardcode / 配置檔 / 統一升級) - Phase 21.2-21.6 戰略性遷移計畫 - V1-V3 驗收 SQL(caller 整合後 model 分布觀察) 關聯:Primary + Secondary 兩台 GCP 已備齊 10 模型(67GB 對稱)支援所有 路由規則;caller 整合可分階段進行(Phase 21.2-21.5)。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.3 KiB
6.3 KiB
ADR-034: Caller × Context 動態 Model Router
- Status: Accepted (待整合到 caller 後 Active)
- Date: 2026-05-04
- Decision Maker: 統帥
- Author: Operation Ollama-First v5.0 / Phase 21
- Related: ADR-028(LLM 路由)、ADR-029(雙塔分工)、ADR-030(多供應商)
Context
戰役 v5.0 累積完成 Primary + Secondary 兩台 GCP × 各 10 個 Ollama 模型(~67GB)。但既有 caller 多用單一寫死 model(如 sales_copy 永遠用 llama3.1:8b),無法動態根據 context 選最佳 model。
痛點:
- 資源浪費:sales_copy 短文(< 100 字)也用 8B 模型 → 應走
gemma3:4b(4GB vs 5GB,延遲 -50%) - 品質瓶頸:Hermes 競價遇複雜 SKU(gap > 20%)仍用
hermes3:latest(8B)→ 應升qwen3:14b - 重構斷層:AiderHeal 大型重構(diff > 200 行)用
qwen2.5-coder:7b不夠 → 應升qwen2.5-coder:32b - 推理空缺:EA HITL 需 chain-of-thought 時無 deepseek-r1 路徑
前置已完成:
- Primary + Secondary 各 10 模型完整對稱
services/llm_caller_registry.py30+ caller 集中services/cost_throttle_service.py成本守門
本 ADR 鎖定動態路由規則設計。
Decision
1. 純規則引擎,零 LLM 成本
# services/llm_model_router.py
ROUTING_RULES: Dict[str, list] = {
'sales_copy': [
(lambda ctx: ctx.get('expected_length', 0) < 100, 'gemma3:4b'),
(lambda ctx: True, 'llama3.1:8b'),
],
'hermes_analyst': [
(lambda ctx: ctx['max_gap_pct'] > 20 or ctx['min_sales_delta'] < -50,
'qwen3:14b'),
(lambda ctx: True,
'hermes3:latest'),
],
# ... 6 個 caller 共 12 條規則
}
2. 路由規則對應表
| Caller | Context 觸發條件 | 升級 Model | 預設 Model |
|---|---|---|---|
sales_copy |
expected_length < 100 字 | gemma3:4b |
llama3.1:8b |
hermes_analyst |
max_gap_pct > 20% 或 銷量 < -50% | qwen3:14b |
hermes3:latest |
aider_heal |
diff_lines > 200 | qwen2.5-coder:32b |
qwen2.5-coder:7b |
openclaw_qa |
query_length > 200 或 multi_turn | qwen3:14b |
qwen2.5:7b-instruct |
ppt_vision |
minicpm_unhealthy | llava:latest |
minicpm-v:latest |
ea_engine |
require_chain_of_thought | deepseek-r1:14b |
(回 default = Gemini) |
3. Feature Flag 灰度
MODEL_ROUTER_ENABLED預設 OFF- caller 端
select_model(caller, context, default='既有 model') - flag OFF → 直接回 default(不評估規則)→ 行為與戰前完全相同
4. 失敗安全
- predicate 拋例外 → log warning + skip 到下一條
- caller 不在 ROUTING_RULES → 回 default
- 所有規則都不命中 → 回 default
5. 整合方式(建議分階段)
# Caller 範例(如 ollama_service.generate_sales_copy):
from services.llm_model_router import select_model
def generate_sales_copy(self, product_name, ...):
model = select_model(
caller='sales_copy',
context={'expected_length': len(product_name) * 3},
default='llama3.1:8b',
)
return self.generate(prompt=..., model=model, ...)
戰略性遷移:
- Phase 21.1: model_router service + test 落地(本 commit)✅
- Phase 21.2: sales_copy 整合(低風險示範)⏳
- Phase 21.3: aider_heal 整合(中風險,需 diff_lines 取得)
- Phase 21.4: hermes_analyst 整合(高風險,動戰術主流程)
- Phase 21.5: 全 caller 遷移完成 → MODEL_ROUTER_ENABLED 預設 ON
Alternatives Considered
| 方案 | 否決理由 |
|---|---|
| A. LLM-based routing(用 LLM 決定用哪個 model) | 循環燒錢 + 引入新延遲 |
| B. caller 各自 hardcode 多 model(不集中) | 規則漂移無 single source of truth |
| C. 直接統一升級到大模型(如全用 qwen3:14b) | 浪費資源,短文不需 14B |
| D. 配置檔 YAML/JSON(運行時讀檔) | 過度工程;Python lambda 已夠彈性 |
Consequences
正面(5)
- 資源節省:短文 sales_copy 用 4GB gemma3 vs 5GB llama3.1,延遲 -50%
- 品質提升:複雜場景自動升大模型(hermes 14B / aider 32B)
- 零 LLM 成本:純 Python lambda 規則
- 失敗安全:規則例外不阻擋主流程
- 集中治理:規則改動只需 PR
llm_model_router.py,不動 caller
負面(3)
- 規則維護成本:新 caller / 新 context 條件需更新 rules(但這正是 ADR 治理目標)
- context 取得負擔:caller 必須先計算 context(如 diff_lines)才能呼叫 router
- debug 複雜度:路由命中哪條規則需看 logger.debug
風險(3)
- 規則設計失誤:閾值(20% / 200 lines)可能不準 → mitigate by Phase 21.2-21.5 灰度觀察
- GCP 主機沒拉到對應 model:select 回的 model 不存在 → mitigate by 拉模型前提(已完成 10 模型對稱)
- caller 整合不完整:部分 caller 仍 hardcode → 文件化遷移計畫
Verification
V1:unit test
pytest tests/test_llm_model_router.py -v
# 預期 18 tests 全綠
V2:caller 整合後 ai_calls 觀察
SELECT model, COUNT(*), AVG(duration_ms)
FROM ai_calls
WHERE caller = 'sales_copy' AND called_at > NOW() - INTERVAL '7 days'
GROUP BY model;
-- 期望:gemma3:4b 短文佔 60%+,llama3.1:8b 長文佔 40%-
-- 平均 duration: gemma3 < llama3.1 約 50%
V3:cost throttle 整合
# Phase 22 規劃:cost_throttle 觸發時自動切便宜 model
# 例:claude throttled → select_model 改回 default Gemini Flash
Migration Plan
| Phase | 工作 | 狀態 |
|---|---|---|
| 21.1 | services/llm_model_router.py + 18 tests | ✅ 本 commit |
| 21.2 | sales_copy 整合(generate_sales_copy 加 select_model) | ⏳ |
| 21.3 | aider_heal 整合(需 diff_lines context) | ⏳ |
| 21.4 | hermes_analyst 整合(需 max_gap_pct context) | ⏳ |
| 21.5 | openclaw_qa / ppt_vision / ea_engine | ⏳ |
| 21.6 | MODEL_ROUTER_ENABLED 預設 ON(觀察 1 週後) | ⏳ |
References
services/llm_model_router.py(本 commit)tests/test_llm_model_router.py(18 tests)docs/llm_model_full_evaluation_20260504.md路由優化建議- ADR-028(LLM 路由統一準則)
- ADR-029(Hermes-First 雙塔分工)
- ADR-030(Frontier 多供應商策略)