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>
177 lines
6.3 KiB
Markdown
177 lines
6.3 KiB
Markdown
# 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。
|
||
|
||
**痛點**:
|
||
1. **資源浪費**:sales_copy 短文(< 100 字)也用 8B 模型 → 應走 `gemma3:4b`(4GB vs 5GB,延遲 -50%)
|
||
2. **品質瓶頸**:Hermes 競價遇複雜 SKU(gap > 20%)仍用 `hermes3:latest`(8B)→ 應升 `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 成本
|
||
|
||
```python
|
||
# 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. 整合方式(建議分階段)
|
||
|
||
```python
|
||
# 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 主機沒拉到對應 model**:select 回的 model 不存在 → mitigate by 拉模型前提(已完成 10 模型對稱)
|
||
3. **caller 整合不完整**:部分 caller 仍 hardcode → 文件化遷移計畫
|
||
|
||
---
|
||
|
||
## Verification
|
||
|
||
### V1:unit test
|
||
```bash
|
||
pytest tests/test_llm_model_router.py -v
|
||
# 預期 18 tests 全綠
|
||
```
|
||
|
||
### V2:caller 整合後 ai_calls 觀察
|
||
```sql
|
||
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 整合
|
||
```python
|
||
# 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 多供應商策略)
|