Files
ewoooc/docs/adr/ADR-034-dynamic-model-router.md
OoO 390c32b05d
All checks were successful
CD Pipeline / deploy (push) Successful in 2m45s
feat(p21): Caller × Context 動態 Model Router + ADR-034
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>
2026-05-04 10:54:12 +08:00

6.3 KiB
Raw Permalink Blame History

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-028LLM 路由、ADR-029雙塔分工、ADR-030多供應商

Context

戰役 v5.0 累積完成 Primary + Secondary 兩台 GCP × 各 10 個 Ollama 模型(~67GB。但既有 caller 多用單一寫死 model如 sales_copy 永遠用 llama3.1:8b),無法動態根據 context 選最佳 model。

痛點

  1. 資源浪費sales_copy 短文(< 100 字)也用 8B 模型 → 應走 gemma3:4b4GB vs 5GB延遲 -50%
  2. 品質瓶頸Hermes 競價遇複雜 SKUgap > 20%)仍用 hermes3:latest8B→ 應升 qwen3:14b
  3. 重構斷層AiderHeal 大型重構diff > 200 行)用 qwen2.5-coder:7b 不夠 → 應升 qwen2.5-coder:32b
  4. 推理空缺EA HITL 需 chain-of-thought 時無 deepseek-r1 路徑

前置已完成

  • Primary + Secondary 各 10 模型完整對稱
  • services/llm_caller_registry.py 30+ 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

  1. 資源節省:短文 sales_copy 用 4GB gemma3 vs 5GB llama3.1,延遲 -50%
  2. 品質提升複雜場景自動升大模型hermes 14B / aider 32B
  3. 零 LLM 成本:純 Python lambda 規則
  4. 失敗安全:規則例外不阻擋主流程
  5. 集中治理:規則改動只需 PR llm_model_router.py,不動 caller

負面3

  1. 規則維護成本:新 caller / 新 context 條件需更新 rules但這正是 ADR 治理目標)
  2. context 取得負擔caller 必須先計算 context如 diff_lines才能呼叫 router
  3. debug 複雜度:路由命中哪條規則需看 logger.debug

風險3

  1. 規則設計失誤閾值20% / 200 lines可能不準 → mitigate by Phase 21.2-21.5 灰度觀察
  2. GCP 主機沒拉到對應 modelselect 回的 model 不存在 → mitigate by 拉模型前提(已完成 10 模型對稱)
  3. caller 整合不完整:部分 caller 仍 hardcode → 文件化遷移計畫

Verification

V1unit test

pytest tests/test_llm_model_router.py -v
# 預期 18 tests 全綠

V2caller 整合後 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%

V3cost 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.py18 tests
  • docs/llm_model_full_evaluation_20260504.md 路由優化建議
  • ADR-028LLM 路由統一準則)
  • ADR-029Hermes-First 雙塔分工)
  • ADR-030Frontier 多供應商策略)