Phase 13.1 CI/CD Integration: - #76 workflow_run handler for CI failure diagnosis - #77 SignOz log query (query_logs, error_logs_summary MCP) - #78 CIAutoRepairService with risk-based execution decisions Phase 13.3 Smart Routing: - #85 Intent Classifier v2.0 (rule engine + LLM fallback) - #86 Complexity Scorer (9-dimension scoring) - #87 AI Router v3.0 (routing decision matrix) - #88 Token Counter (OTEL + Langfuse integration) New files: - services/ci_auto_repair.py (risk stratification) - services/model_registry.py (centralized model config) - services/token_counter.py (677 lines) - Skill 08: Model Router Expert - Skill 09: Strangler Pattern Expert - ADR-023: Smart Routing Architecture - ADR-024: API Layer Architecture Tests: - phase11-conversational.spec.ts (E2E tests) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
28 KiB
28 KiB
ADR-023: Phase 13.3 智能路由架構
狀態: 已接受 日期: 2026-03-26 決策者: CTO, CEO 相關: ADR-006 (AI Fallback Strategy), ADR-016 (Smart Routing) Phase: 13.3
1. 背景與問題
問題描述
ADR-006 建立了固定順序的 AI 備援策略 (Ollama → Gemini → Claude),ADR-016 引入了智能路由概念。然而,隨著 AWOOOI 從「告警響應」升級為「全方位 AIOps 平台」(Phase 13),需要更完整的架構設計:
- 意圖分類不夠精細: 缺少針對 K8s 操作的專屬意圖類型
- 複雜度評估不完整: 缺少跨系統、有狀態資源的風險評估
- Token 用量無追蹤: 無法掌握成本分佈與趨勢
- 配置分散: 模型選擇邏輯散落各處,難以維護
目標
- 建立完整的 Intent → Complexity → Model 決策流程
- 定義 K8s 專屬意圖 (RESTART/SCALE/CONFIG/DIAGNOSE)
- 建立 1-5 分複雜度評分系統
- 整合 Token 用量監控 (SignOz + Langfuse)
2. 決策: Intent Classifier + Complexity Scorer + AI Router
核心策略
Intent (意圖) + Complexity (複雜度) + Context (上下文) → Model Selection (模型選擇)
三元件架構
┌──────────────────────────────────────────────────────────────────┐
│ Phase 13.3 Smart Router │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Request / Alert / Webhook │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Intent Classifier (意圖分類器) │ │
│ │ ├── 關鍵字匹配 (< 1ms) │ │
│ │ └── LLM 備援 (qwen2.5:1b, < 100ms) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Complexity Scorer (複雜度評分器) │ │
│ │ ├── 服務數量 / 指標數量 │ │
│ │ ├── 跨系統判斷 / 有狀態風險 │ │
│ │ └── 歷史案例匹配 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ AI Router (智能路由器) │ │
│ │ ├── 意圖覆寫規則 │ │
│ │ ├── 複雜度 → 模型映射 │ │
│ │ └── Circuit Breaker + Fallback │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌────┴────────────────────┬───────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Ollama │ │ Gemini │ │ Claude │ │
│ │ (Local) │ │ (Cloud) │ │ (Cloud) │ │
│ │ $0 │ │ $0.001/1K │ │ $0.008/1K │ │
│ └─────────┘ └─────────────┘ └─────────────┘ │
│ │
├──────────────────────────────────────────────────────────────────┤
│ Token Usage Monitor (SignOz + Langfuse) │
│ └── llm.tokens.* / llm.cost.* / trace.generation() │
└──────────────────────────────────────────────────────────────────┘
3. 架構圖
完整請求流程
┌─────────────────────────────────────────────────────────────────────────┐
│ AWOOOI Phase 13.3 │
│ Smart Routing Architecture │
└─────────────────────────────────────────────────────────────────────────┘
┌────────────────────┐
│ Alert / Request │
│ (Telegram/API) │
└─────────┬──────────┘
│
▼
┌─────────────────────┐
│ Intent Classifier │
│ 目標: < 100ms │
└─────────┬───────────┘
│
┌─────────────────────┼─────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌───────────┐ ┌────────────┐
│ RESTART │ │ SCALE │ │ CONFIG │
│ 重啟類 │ │ 擴縮容 │ │ 配置變更 │
└────┬────┘ └─────┬─────┘ └─────┬──────┘
│ │ │
└─────────┬──────────┴─────────────────────┘
│
▼
┌─────────────────────┐
│ Complexity Scorer │
│ 輸出: 1-5 分 │
└─────────┬───────────┘
│
┌─────────────┼─────────────┬─────────────┬─────────────┐
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
│ 1 │ │ 2 │ │ 3 │ │ 4 │ │ 5 │
│簡單 │ │低風險│ │中等 │ │高複雜│ │極複雜│
└──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘
│ │ │ │ │
└────────────┴─────┬──────┴────────────┴────────────┘
│
▼
┌─────────────────┐
│ AI Router │
│ 模型選擇 │
└────────┬────────┘
│
┌────────────────┼────────────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ Ollama │ │ Gemini │ │ Claude │
│ llama3.2 │ │ gemini-1.5 │ │ claude-3.5 │
│ qwen2.5 │ │ │ │ │
└────┬─────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
└─────────────────┼───────────────────┘
│
▼
┌─────────────────┐
│ Token Monitor │
│ SignOz/Langfuse│
└─────────────────┘
4. 意圖分類 (Intent Classification)
四大核心意圖
| IntentType | 說明 | 典型場景 | 關鍵字 |
|---|---|---|---|
RESTART |
重啟 Pod/Deployment/StatefulSet | Pod CrashLoopBackOff、服務無回應 | restart, 重啟, 重新啟動, rollout restart |
SCALE |
擴縮容、HPA 調整 | CPU 高負載、流量激增 | scale, 擴容, 縮容, replicas, hpa |
CONFIG |
ConfigMap/Secret/ENV 變更 | 配置錯誤、環境變數缺失 | config, 配置, configmap, secret, env |
DIAGNOSE |
日誌查詢、健康檢查、RCA | 錯誤追蹤、根因分析 | diagnose, 診斷, log, describe, rca |
輔助意圖
| IntentType | 說明 | 典型場景 |
|---|---|---|
DEPLOY |
部署操作 | kubectl apply, helm upgrade |
ROLLBACK |
回滾操作 | rollout undo, 版本回滾 |
QUERY |
資訊查詢 | 狀態查詢、資源列表 |
CODE_REVIEW |
程式碼審查 | PR Review, Commit 分析 |
ALERT_TRIAGE |
告警分流 | 高負載告警、OOM、服務 Down |
UNKNOWN |
未知意圖 | 無法分類的請求 |
分類策略 (兩階段)
class IntentClassifier:
"""
意圖分類器 - 兩階段策略
階段 1: 關鍵字匹配 (< 1ms)
階段 2: LLM 備援 (qwen2.5:1b, < 100ms)
"""
async def classify(self, text: str) -> IntentType:
# 階段 1: 關鍵字快速匹配
intent = self._keyword_match(text.lower())
if intent != IntentType.UNKNOWN:
return intent
# 階段 2: LLM 分類 (備援)
return await self._llm_classify(text)
def _keyword_match(self, text: str) -> IntentType:
"""
關鍵字映射 (優先級: 越上面越優先)
"""
INTENT_KEYWORDS = {
IntentType.RESTART: [
"restart", "重啟", "重新啟動", "rollout restart",
"kill", "recreate", "delete pod",
],
IntentType.SCALE: [
"scale", "擴容", "縮容", "replicas", "hpa",
"autoscale", "capacity", "節點",
],
IntentType.CONFIG: [
"config", "配置", "configmap", "secret", "env",
"環境變數", "yaml", "設定",
],
IntentType.DIAGNOSE: [
"diagnose", "診斷", "log", "describe", "rca",
"root cause", "根因", "排查", "debug", "trace",
],
# ... 其他意圖
}
for intent, keywords in INTENT_KEYWORDS.items():
if any(kw in text for kw in keywords):
return intent
return IntentType.UNKNOWN
5. 複雜度評分 (Complexity Scoring)
評分維度與權重
| 維度 | 權重 | 說明 |
|---|---|---|
service_count |
+0.5/服務 | 每增加一個受影響服務 |
metric_count |
+0.3/指標 | 每增加一個相關指標 |
cross_namespace |
+1.0 | 跨命名空間操作 |
cross_cluster |
+2.0 | 跨叢集操作 |
stateful_resource |
+1.0 | 有狀態資源 (StatefulSet, PVC) |
database_operation |
+1.5 | 涉及資料庫操作 |
critical_severity |
+1.0 | CRITICAL 嚴重程度 |
has_playbook |
-0.5 | 有歷史 Playbook (降低複雜度) |
requires_multisig |
+1.0 | 需要 Multi-Sig 審核 |
複雜度等級定義
| 分數 | 等級 | 定義 | 範例 |
|---|---|---|---|
| 1 | 簡單 | 單一資源、無狀態、可立即回滾 | 重啟單一 Pod |
| 2 | 低風險 | 多資源但同命名空間、低風險 | 擴容 Deployment |
| 3 | 中等 | 跨命名空間、需要上下文收集 | 多服務診斷 |
| 4 | 高複雜 | 有狀態資源、需要 Multi-Sig | StatefulSet 操作 |
| 5 | 極複雜 | 跨叢集、資料庫操作、需要人工審核 | 資料庫 Schema 變更 |
評分邏輯
class ComplexityScorer:
"""
複雜度評分器 - 純規則引擎 (< 10ms)
"""
def score(self, context: dict) -> ComplexityScore:
score = 1.0 # 基礎分
# 服務數量
service_count = len(context.get("affected_services", []))
score += service_count * 0.5
# 指標數量
metric_count = len(context.get("metrics", []))
score += metric_count * 0.3
# 跨命名空間
if context.get("cross_namespace"):
score += 1.0
# 跨叢集
if context.get("cross_cluster"):
score += 2.0
# 有狀態資源
if context.get("stateful_resource"):
score += 1.0
# 資料庫操作
if context.get("database_operation"):
score += 1.5
# CRITICAL 嚴重程度
if context.get("severity") == "CRITICAL":
score += 1.0
# 歷史 Playbook (降低)
if context.get("has_playbook"):
score -= 0.5
# Multi-Sig 需求
if context.get("requires_multisig"):
score += 1.0
# 限制範圍 1-5
final_score = min(5, max(1, round(score)))
return ComplexityScore(
score=final_score,
factors=self._extract_factors(context),
)
6. Provider 選擇邏輯
複雜度 → 模型映射
| 複雜度 | 主要模型 | Fallback 順序 | 理由 |
|---|---|---|---|
| 1 | llama3.2:3b |
qwen2.5:7b → gemini → claude | 快速回應,資源節省 |
| 2 | qwen2.5:7b-instruct |
llama3.2:3b → gemini → claude | 平衡品質與延遲 |
| 3 | qwen2.5:7b-instruct |
gemini → claude | 需要較強推理能力 |
| 4 | gemini |
claude → qwen2.5:7b | 需要雲端能力 |
| 5 | claude |
gemini → qwen2.5:7b | 最強模型處理 |
意圖強制覆寫
某些意圖無論複雜度如何,都強制使用特定模型:
| 意圖 | 強制模型 | 原因 |
|---|---|---|
DIAGNOSE |
qwen2.5:7b-instruct (本地) |
日誌可能含敏感資料,禁止送雲端 |
CODE_REVIEW |
qwen2.5:7b-instruct |
程式碼分析需要較強能力 |
QUERY |
llama3.2:3b |
簡單查詢不需大模型 |
路由決策流程
class AIRouter:
"""
智能路由器 - 動態模型選擇
"""
COMPLEXITY_ROUTING = {
1: "llama3.2:3b",
2: "qwen2.5:7b-instruct",
3: "qwen2.5:7b-instruct",
4: "gemini",
5: "claude",
}
INTENT_OVERRIDES = {
IntentType.DIAGNOSE: "qwen2.5:7b-instruct", # 隱私優先
IntentType.CODE_REVIEW: "qwen2.5:7b-instruct",
IntentType.QUERY: "llama3.2:3b",
}
async def route(
self, text: str, context: dict | None = None
) -> RoutingDecision:
# Step 1: 意圖分類
intent = await self._intent_classifier.classify(text)
# Step 2: 複雜度評分
complexity = self._complexity_scorer.score(context or {})
# Step 3: 模型選擇 (考慮意圖覆寫)
if intent in self.INTENT_OVERRIDES:
model = self.INTENT_OVERRIDES[intent]
reason = f"意圖 {intent.value} 強制使用 {model}"
else:
model = self.COMPLEXITY_ROUTING[complexity.score]
reason = f"複雜度 {complexity.score} → {model}"
# Step 4: 建立 Fallback 列表
fallbacks = self._build_fallback_list(model)
return RoutingDecision(
model=model,
intent=intent,
complexity=complexity,
reason=reason,
fallback_models=fallbacks,
)
def _build_fallback_list(self, primary: str) -> list[str]:
"""
建立 Fallback 順序 (ADR-006)
"""
FALLBACK_ORDER = [
"qwen2.5:7b-instruct",
"llama3.2:3b",
"gemini",
"claude",
]
return [m for m in FALLBACK_ORDER if m != primary]
7. Token 用量監控
監控架構
┌─────────────────────────────────────────────────────────────────┐
│ Token Usage Monitoring │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ SignOz │ │ Langfuse │ │
│ │ (Infra 層) │ │ (LLMOps 層) │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ OTEL Metrics │ │ Trace/Generation│ │
│ │ - llm.tokens.* │ │ - cost tracking │ │
│ │ - llm.latency.* │ │ - model compare │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
關鍵指標
| 指標 | 說明 | 類型 |
|---|---|---|
llm.tokens.input |
輸入 Token 數 | Counter |
llm.tokens.output |
輸出 Token 數 | Counter |
llm.cost.usd |
估算成本 (雲端 Provider) | Counter |
llm.latency.p99 |
延遲 P99 | Histogram |
llm.requests.total |
總請求數 | Counter |
llm.requests.failed |
失敗請求數 | Counter |
成本警報閾值 (ADR-006 延伸)
| Provider | 每日上限 | 每月上限 | 告警閾值 |
|---|---|---|---|
| Gemini | 100K tokens | 2M tokens | 70% |
| Claude | 50K tokens | 500K tokens | 70% |
Langfuse 追蹤整合
from langfuse.decorators import langfuse_context, observe
class AIRouter:
@observe(name="smart_routing")
async def route(self, text: str, context: dict) -> RoutingDecision:
decision = await self._make_decision(text, context)
# 記錄路由決策
langfuse_context.update_current_trace(
metadata={
"intent": decision.intent.value,
"complexity": decision.complexity.score,
"model": decision.model,
"reason": decision.reason,
}
)
return decision
@observe(name="llm_generation")
async def generate(self, model: str, prompt: str) -> str:
result = await self._call_model(model, prompt)
# 記錄 Token 用量
langfuse_context.update_current_observation(
usage={
"input_tokens": result.input_tokens,
"output_tokens": result.output_tokens,
},
model=model,
)
return result.content
8. 與 ADR-006 的關係
架構層次
┌─────────────────────────────────────────────────────────────────┐
│ ADR-023 (Phase 13.3) │
│ 智能路由架構 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Intent Classifier → Complexity Scorer → AI Router │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ADR-016 (Smart Routing 基礎實作) │ │
│ │ - IntentClassifier / ComplexityScorer / AIRouter │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ ADR-006 (AI Fallback Strategy) │ │
│ │ - Circuit Breaker / Token 配額 / 固定 Fallback 順序 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
協作關係
| 面向 | ADR-006 (基礎) | ADR-016 (實作) | ADR-023 (架構) |
|---|---|---|---|
| 範疇 | Fallback 策略 | 路由實作 | 完整架構 |
| 觸發時機 | 服務失敗時 | 每個請求 | 架構設計層面 |
| 選擇邏輯 | 固定順序 | 意圖 + 複雜度 | 三元件協作 |
| 目標 | 高可用性 | 資源最佳化 | 全方位 AIOps |
| 狀態 | 仍然有效 | 已實作 | 本文件 |
請求流程整合
Request
│
▼
[ADR-023: 三元件決策]
│
├── Intent Classifier
├── Complexity Scorer
└── AI Router → 選擇 Model A
│
失敗 ▼
[ADR-006: Fallback Chain]
│
├── Model B
├── Model C
└── Static Response
9. 後果分析
優點
| 面向 | 效益 |
|---|---|
| 資源優化 | 簡單任務用小模型 (3B),節省 GPU 資源 30%+ |
| 品質提升 | 複雜任務自動升級到強模型,減少人工介入 |
| 成本可控 | 只有真正需要時才使用雲端 API |
| 延遲改善 | 簡單查詢回應 < 5s (llama3.2:3b) |
| 可觀測性 | Token 用量透明,成本趨勢可預測 |
| 隱私保護 | DIAGNOSE 意圖強制本地,敏感日誌不送雲端 |
缺點
| 面向 | 風險 | 緩解措施 |
|---|---|---|
| 分類錯誤 | 意圖分類可能有邊界情況 | 關鍵字優先 + LLM 備援 |
| 複雜度誤判 | 規則可能需要持續調優 | 收集數據 + 定期調整權重 |
| 延遲增加 | 分類 + 評分 增加約 100ms | 限制 LLM 分類僅作備援 |
| 維護成本 | 需維護關鍵字映射表 | 集中管理於 models.json |
風險
| 風險 | 等級 | 緩解策略 |
|---|---|---|
| LLM 分類器 Timeout | 中 | 設定 100ms Timeout,fallback 到 UNKNOWN |
| 全部 Provider 失敗 | 低 | ADR-006 靜態回應兜底 |
| Token 預算超支 | 中 | 告警閾值 70%,超支自動切本地 |
| 意圖覆寫邏輯錯誤 | 低 | 嚴格測試 + 監控路由決策分佈 |
10. 實作位置
apps/api/src/services/
├── intent_classifier.py # IntentClassifier
├── complexity_scorer.py # ComplexityScorer
├── ai_router.py # AIRouter
└── token_tracker.py # TokenTracker (SignOz + Langfuse)
11. 配置集中管理
單一事實來源: models.json
{
"providers": {
"ollama": {
"models": {
"default": "qwen2.5:7b-instruct",
"fast": "llama3.2:3b",
"intent": "qwen2.5:1b"
},
"circuit_breaker": {
"failure_threshold": 3,
"recovery_timeout": 60
}
},
"gemini": {
"model": "gemini-1.5-flash",
"daily_quota": 100000,
"monthly_quota": 2000000
},
"claude": {
"model": "claude-3-5-sonnet",
"daily_quota": 50000,
"monthly_quota": 500000
}
},
"complexity_routing": {
"1": "llama3.2:3b",
"2": "qwen2.5:7b-instruct",
"3": "qwen2.5:7b-instruct",
"4": "gemini",
"5": "claude"
},
"intent_overrides": {
"DIAGNOSE": "qwen2.5:7b-instruct",
"CODE_REVIEW": "qwen2.5:7b-instruct",
"QUERY": "llama3.2:3b"
}
}
12. 變更記錄
| 日期 | 版本 | 變更 | 作者 |
|---|---|---|---|
| 2026-03-26 | v1.0 | 初版建立 (Phase 13.3 #85-88) | 首席架構師 |
參考
- ADR-006: AI 降級備援策略
- ADR-016: 智能路由 (基礎實作)
- Skill 08: Model Router Expert
- Phase 13.3 Smart Router 設計
- Phase 13 Enterprise AIOps
此 ADR 記錄 Phase 13.3 智能路由架構的完整決策過程,整合 ADR-006 Fallback 策略與 ADR-016 路由實作。