Files
awoooi/docs/adr/ADR-023-smart-routing-architecture.md
OG T 579da38b8b feat(api): Phase 13 智能路由 + CI/CD 整合 (#74-88)
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>
2026-03-26 15:32:52 +08:00

28 KiB
Raw Blame History

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),需要更完整的架構設計:

  1. 意圖分類不夠精細: 缺少針對 K8s 操作的專屬意圖類型
  2. 複雜度評估不完整: 缺少跨系統、有狀態資源的風險評估
  3. Token 用量無追蹤: 無法掌握成本分佈與趨勢
  4. 配置分散: 模型選擇邏輯散落各處,難以維護

目標

  • 建立完整的 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 Timeoutfallback 到 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 記錄 Phase 13.3 智能路由架構的完整決策過程,整合 ADR-006 Fallback 策略與 ADR-016 路由實作。