在前一篇文章《多 Agent 系統的 Token 用量調優指南》中,我們介紹了 模型分層(Model Tiering) 作為平衡品質與成本的關鍵策略。本文將深入實作層面,探討如何在真實系統中建構智能的模型路由機制,讓簡單任務用便宜快速的模型,複雜任務才動用高階模型,實現成本效益最大化。
為什麼需要模型分層?
不同模型的能力與成本差異
Claude 模型系列對比(2026 參考定價):
┌─────────────────────────────────────────────────────────────────────┐
│ Claude 模型能力與成本矩陣 │
├──────────────────┬─────────┬─────────┬──────────┬──────────────────┤
│ 模型 │ 輸入成本│ 輸出成本│ 推理能力 │ 速度 │
│ │ /1M tok │ /1M tok │ │ │
├──────────────────┼─────────┼─────────┼──────────┼──────────────────┤
│ Claude Opus 4 │ $15 │ $75 │ ★★★★★ │ ★★★(較慢) │
│ Claude Sonnet 4 │ $3 │ $15 │ ★★★★ │ ★★★★(中等) │
│ Claude Haiku 3.5 │ $0.80 │ $4 │ ★★★ │ ★★★★★(最快) │
└──────────────────┴─────────┴─────────┴──────────┴──────────────────┘
成本差異計算:
- Opus vs Haiku 輸入:15 / 0.80 = 18.75x
- Opus vs Haiku 輸出:75 / 4 = 18.75x
場景:處理 1000 個請求,每個請求 2000 輸入 + 500 輸出 tokens
全部使用 Opus:
(2000 × $15 + 500 × $75) / 1M × 1000 = $67.50
全部使用 Haiku:
(2000 × $0.80 + 500 × $4) / 1M × 1000 = $3.60
智能分層(假設 70% 用 Haiku,30% 用 Sonnet):
700 × $3.60/1000 + 300 × ((2000×$3 + 500×$15)/1M) = $5.97
節省:全 Opus 方案的 91%!
任務複雜度的長尾分布
實際應用中任務複雜度分布:
複雜度 ▲
│
高 │ ████ (10%) ← 需要 Opus
│ ████████████ (20%) ← 適合 Sonnet
中 │ ████████████████████████ (30%)
│ ████████████████████████████████████████ (40%) ← Haiku 足夠
低 │
└──────────────────────────────────────────────▶ 任務數量
關鍵洞察:
- 大多數任務(~70%)不需要最強的模型
- 只有少數任務(~10%)真正需要複雜推理
- 正確分類 + 路由 = 大幅節省成本
任務分類框架
複雜度維度分析
任務複雜度評估維度:
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ 維度 1:推理深度 │
│ ├── 單步推理:直接回答、格式轉換、簡單分類 │
│ ├── 多步推理:邏輯推導、問題分解、計劃制定 │
│ └── 複雜推理:抽象思考、創意生成、多角度分析 │
│ │
│ 維度 2:領域專業度 │
│ ├── 通用知識:日常問答、一般寫作 │
│ ├── 專業領域:技術問題、法律諮詢、醫療資訊 │
│ └── 深度專業:前沿研究、複雜架構、策略規劃 │
│ │
│ 維度 3:輸出品質要求 │
│ ├── 基本正確:內部工具、草稿生成 │
│ ├── 高品質:客戶溝通、正式文件 │
│ └── 極致品質:關鍵決策、法律文書、發表內容 │
│ │
│ 維度 4:容錯程度 │
│ ├── 可容錯:建議性輸出、可人工修正 │
│ ├── 低容錯:自動化流程、需要準確 │
│ └── 零容錯:安全相關、財務計算、醫療建議 │
│ │
└─────────────────────────────────────────────────────────────────────┘
任務類型與模型映射
1from enum import Enum
2from dataclasses import dataclass
3from typing import Optional
4
5class TaskComplexity(Enum):
6 """任務複雜度等級"""
7 TRIVIAL = "trivial" # 極簡單:格式化、提取
8 SIMPLE = "simple" # 簡單:分類、摘要、翻譯
9 MODERATE = "moderate" # 中等:程式碼生成、分析
10 COMPLEX = "complex" # 複雜:架構設計、深度推理
11 EXPERT = "expert" # 專家級:研究、創新、關鍵決策
12
13class TaskCategory(Enum):
14 """任務類別"""
15 EXTRACTION = "extraction" # 資訊提取
16 CLASSIFICATION = "classification" # 分類
17 SUMMARIZATION = "summarization" # 摘要
18 TRANSLATION = "translation" # 翻譯
19 GENERATION = "generation" # 內容生成
20 CODE = "code" # 程式碼相關
21 ANALYSIS = "analysis" # 分析
22 REASONING = "reasoning" # 推理
23 CREATIVE = "creative" # 創意
24 CONVERSATION = "conversation" # 對話
25
26@dataclass
27class ModelSpec:
28 """模型規格"""
29 name: str
30 model_id: str
31 input_cost_per_1m: float
32 output_cost_per_1m: float
33 max_tokens: int
34 strengths: list[str]
35 weaknesses: list[str]
36
37# 定義可用模型
38MODELS = {
39 "opus": ModelSpec(
40 name="Claude Opus 4",
41 model_id="claude-opus-4-20250514",
42 input_cost_per_1m=15.0,
43 output_cost_per_1m=75.0,
44 max_tokens=4096,
45 strengths=["複雜推理", "創意寫作", "深度分析", "專業知識"],
46 weaknesses=["成本高", "速度較慢"]
47 ),
48 "sonnet": ModelSpec(
49 name="Claude Sonnet 4",
50 model_id="claude-sonnet-4-20250514",
51 input_cost_per_1m=3.0,
52 output_cost_per_1m=15.0,
53 max_tokens=4096,
54 strengths=["程式碼生成", "一般分析", "平衡性能"],
55 weaknesses=["複雜推理略遜 Opus"]
56 ),
57 "haiku": ModelSpec(
58 name="Claude Haiku 3.5",
59 model_id="claude-3-5-haiku-20241022",
60 input_cost_per_1m=0.80,
61 output_cost_per_1m=4.0,
62 max_tokens=4096,
63 strengths=["速度快", "成本低", "簡單任務"],
64 weaknesses=["複雜任務能力有限"]
65 )
66}
67
68# 任務類型到複雜度的預設映射
69DEFAULT_COMPLEXITY_MAP = {
70 TaskCategory.EXTRACTION: TaskComplexity.TRIVIAL,
71 TaskCategory.CLASSIFICATION: TaskComplexity.SIMPLE,
72 TaskCategory.SUMMARIZATION: TaskComplexity.SIMPLE,
73 TaskCategory.TRANSLATION: TaskComplexity.SIMPLE,
74 TaskCategory.GENERATION: TaskComplexity.MODERATE,
75 TaskCategory.CODE: TaskComplexity.MODERATE,
76 TaskCategory.ANALYSIS: TaskComplexity.MODERATE,
77 TaskCategory.REASONING: TaskComplexity.COMPLEX,
78 TaskCategory.CREATIVE: TaskComplexity.COMPLEX,
79 TaskCategory.CONVERSATION: TaskComplexity.SIMPLE,
80}
81
82# 複雜度到模型的映射
83COMPLEXITY_MODEL_MAP = {
84 TaskComplexity.TRIVIAL: "haiku",
85 TaskComplexity.SIMPLE: "haiku",
86 TaskComplexity.MODERATE: "sonnet",
87 TaskComplexity.COMPLEX: "sonnet",
88 TaskComplexity.EXPERT: "opus",
89}
策略一:規則基礎路由(Rule-Based Routing)
最直接的方式:根據預定義規則選擇模型。
基礎實作
1import anthropic
2import re
3from dataclasses import dataclass
4from typing import Optional, Callable
5
6client = anthropic.Anthropic()
7
8@dataclass
9class RoutingRule:
10 """路由規則"""
11 name: str
12 condition: Callable[[str], bool] # 判斷函數
13 model: str # 目標模型
14 priority: int = 0 # 優先級(越高越優先)
15
16class RuleBasedRouter:
17 """
18 規則基礎模型路由器
19
20 優點:
21 - 可預測、可控
22 - 無額外 API 成本
23 - 執行速度快
24
25 缺點:
26 - 規則維護成本
27 - 邊界情況處理困難
28 - 難以適應新場景
29 """
30
31 def __init__(self):
32 self.rules: list[RoutingRule] = []
33 self.default_model = "sonnet"
34 self._stats = {model: 0 for model in MODELS}
35
36 def add_rule(self, rule: RoutingRule):
37 """添加路由規則"""
38 self.rules.append(rule)
39 # 按優先級排序
40 self.rules.sort(key=lambda r: r.priority, reverse=True)
41
42 def route(self, task: str) -> str:
43 """根據規則選擇模型"""
44 for rule in self.rules:
45 if rule.condition(task):
46 self._stats[rule.model] += 1
47 return rule.model
48
49 self._stats[self.default_model] += 1
50 return self.default_model
51
52 def call(self, task: str, system: Optional[str] = None, **kwargs) -> str:
53 """路由並呼叫模型"""
54 model_key = self.route(task)
55 model_spec = MODELS[model_key]
56
57 response = client.messages.create(
58 model=model_spec.model_id,
59 max_tokens=kwargs.get("max_tokens", 2048),
60 system=system or "You are a helpful assistant.",
61 messages=[{"role": "user", "content": task}]
62 )
63
64 return response.content[0].text
65
66 def get_stats(self) -> dict:
67 total = sum(self._stats.values())
68 return {
69 "routing_stats": self._stats,
70 "total_requests": total,
71 "distribution": {
72 k: f"{v/total*100:.1f}%" if total > 0 else "0%"
73 for k, v in self._stats.items()
74 }
75 }
76
77
78# 建立常用規則
79def create_standard_rules() -> list[RoutingRule]:
80 """建立標準路由規則集"""
81 return [
82 # 高優先級:強制使用 Opus 的情況
83 RoutingRule(
84 name="security_analysis",
85 condition=lambda t: any(kw in t.lower() for kw in [
86 "安全漏洞", "security vulnerability", "滲透測試",
87 "資安審計", "安全審查"
88 ]),
89 model="opus",
90 priority=100
91 ),
92 RoutingRule(
93 name="architecture_design",
94 condition=lambda t: any(kw in t.lower() for kw in [
95 "架構設計", "system design", "設計模式",
96 "技術選型", "架構決策"
97 ]),
98 model="opus",
99 priority=100
100 ),
101
102 # 中優先級:使用 Sonnet
103 RoutingRule(
104 name="code_generation",
105 condition=lambda t: any(kw in t.lower() for kw in [
106 "寫一個", "實作", "implement", "寫程式",
107 "function", "class", "def ", "async "
108 ]),
109 model="sonnet",
110 priority=50
111 ),
112 RoutingRule(
113 name="code_review",
114 condition=lambda t: any(kw in t.lower() for kw in [
115 "審查", "review", "檢查這段", "優化這個"
116 ]),
117 model="sonnet",
118 priority=50
119 ),
120 RoutingRule(
121 name="analysis",
122 condition=lambda t: any(kw in t.lower() for kw in [
123 "分析", "analyze", "比較", "compare",
124 "評估", "evaluate"
125 ]),
126 model="sonnet",
127 priority=50
128 ),
129
130 # 低優先級:使用 Haiku
131 RoutingRule(
132 name="simple_question",
133 condition=lambda t: len(t) < 100 and "?" in t,
134 model="haiku",
135 priority=20
136 ),
137 RoutingRule(
138 name="translation",
139 condition=lambda t: any(kw in t.lower() for kw in [
140 "翻譯", "translate", "轉換成"
141 ]),
142 model="haiku",
143 priority=20
144 ),
145 RoutingRule(
146 name="summarization",
147 condition=lambda t: any(kw in t.lower() for kw in [
148 "摘要", "summarize", "總結", "概述"
149 ]),
150 model="haiku",
151 priority=20
152 ),
153 RoutingRule(
154 name="formatting",
155 condition=lambda t: any(kw in t.lower() for kw in [
156 "格式化", "format", "轉成 json", "轉成 markdown"
157 ]),
158 model="haiku",
159 priority=20
160 ),
161 RoutingRule(
162 name="extraction",
163 condition=lambda t: any(kw in t.lower() for kw in [
164 "提取", "extract", "找出", "列出"
165 ]),
166 model="haiku",
167 priority=20
168 ),
169 ]
170
171
172# 使用範例
173if __name__ == "__main__":
174 router = RuleBasedRouter()
175
176 # 載入標準規則
177 for rule in create_standard_rules():
178 router.add_rule(rule)
179
180 # 測試不同類型的任務
181 test_tasks = [
182 "請幫我翻譯這段文字成英文", # → Haiku
183 "總結以下文章的重點", # → Haiku
184 "寫一個 Python 函數來計算費氏數列", # → Sonnet
185 "分析這段程式碼的效能問題", # → Sonnet
186 "設計一個高可用的微服務架構", # → Opus
187 "審查這段程式碼的安全漏洞", # → Opus
188 "今天天氣如何?", # → Haiku (短問題)
189 ]
190
191 print("模型路由測試:\n")
192 for task in test_tasks:
193 model = router.route(task)
194 print(f"任務:{task[:30]}...")
195 print(f"路由至:{MODELS[model].name}\n")
196
197 print("路由統計:")
198 print(router.get_stats())
策略二:LLM 輔助分類路由
使用輕量模型(Haiku)進行任務分類,再路由到適當模型。
核心概念
LLM 輔助分類流程:
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ 使用者任務 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Step 1: Haiku 快速分類 │ │
│ │ 成本:~$0.001 / 請求 │ │
│ │ 輸出:{ complexity: "moderate", category: "code" } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Step 2: 路由決策 │ │
│ │ moderate + code → Sonnet │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Step 3: 目標模型執行 │ │
│ │ Sonnet 處理實際任務 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 總成本 = 分類成本 + 執行成本 │
│ 分類成本通常 < 1% 總成本,但可節省大量錯誤路由成本 │
│ │
└─────────────────────────────────────────────────────────────────────┘
完整實作
1import anthropic
2import json
3from dataclasses import dataclass
4from typing import Optional
5from enum import Enum
6
7client = anthropic.Anthropic()
8
9@dataclass
10class ClassificationResult:
11 """分類結果"""
12 complexity: TaskComplexity
13 category: TaskCategory
14 confidence: float
15 reasoning: str
16 suggested_model: str
17
18class LLMClassifierRouter:
19 """
20 LLM 輔助分類路由器
21
22 使用 Haiku 進行任務分類,基於分類結果選擇目標模型
23
24 優點:
25 - 更準確的任務理解
26 - 可處理複雜的邊界情況
27 - 自動適應新類型任務
28
29 缺點:
30 - 額外的分類 API 呼叫成本
31 - 增加一次網路往返延遲
32 """
33
34 CLASSIFICATION_PROMPT = """分析以下任務,判斷其複雜度和類別。
35
36任務:
37{task}
38
39請以 JSON 格式回答:
40{{
41 "complexity": "trivial|simple|moderate|complex|expert",
42 "category": "extraction|classification|summarization|translation|generation|code|analysis|reasoning|creative|conversation",
43 "confidence": 0.0-1.0,
44 "reasoning": "簡短說明判斷依據"
45}}
46
47複雜度判斷標準:
48- trivial: 簡單格式化、直接提取
49- simple: 基本分類、摘要、翻譯
50- moderate: 程式碼生成、一般分析
51- complex: 深度分析、多步推理
52- expert: 創新設計、專家級決策
53
54只返回 JSON,不要其他說明。"""
55
56 def __init__(
57 self,
58 classifier_model: str = "claude-3-5-haiku-20241022",
59 enable_caching: bool = True
60 ):
61 self.classifier_model = classifier_model
62 self.enable_caching = enable_caching
63 self._classification_cache: dict[str, ClassificationResult] = {}
64
65 self._stats = {
66 "classifications": 0,
67 "cache_hits": 0,
68 "model_usage": {model: 0 for model in MODELS}
69 }
70
71 def _get_cache_key(self, task: str) -> str:
72 """計算快取鍵"""
73 # 使用任務的前 500 字元作為鍵(避免過長)
74 import hashlib
75 return hashlib.md5(task[:500].encode()).hexdigest()
76
77 def classify(self, task: str) -> ClassificationResult:
78 """分類任務"""
79 # 檢查快取
80 if self.enable_caching:
81 cache_key = self._get_cache_key(task)
82 if cache_key in self._classification_cache:
83 self._stats["cache_hits"] += 1
84 return self._classification_cache[cache_key]
85
86 # 呼叫分類模型
87 response = client.messages.create(
88 model=self.classifier_model,
89 max_tokens=300,
90 messages=[{
91 "role": "user",
92 "content": self.CLASSIFICATION_PROMPT.format(task=task[:2000])
93 }]
94 )
95
96 self._stats["classifications"] += 1
97
98 # 解析結果
99 try:
100 text = response.content[0].text
101 if "```" in text:
102 text = text.split("```")[1].split("```")[0]
103 if text.startswith("json"):
104 text = text[4:]
105
106 data = json.loads(text.strip())
107
108 result = ClassificationResult(
109 complexity=TaskComplexity(data["complexity"]),
110 category=TaskCategory(data["category"]),
111 confidence=data.get("confidence", 0.8),
112 reasoning=data.get("reasoning", ""),
113 suggested_model=self._map_to_model(
114 TaskComplexity(data["complexity"]),
115 TaskCategory(data["category"])
116 )
117 )
118
119 # 快取結果
120 if self.enable_caching:
121 self._classification_cache[cache_key] = result
122
123 return result
124
125 except (json.JSONDecodeError, KeyError, ValueError) as e:
126 # 預設返回中等複雜度
127 return ClassificationResult(
128 complexity=TaskComplexity.MODERATE,
129 category=TaskCategory.GENERATION,
130 confidence=0.5,
131 reasoning=f"分類失敗,使用預設: {e}",
132 suggested_model="sonnet"
133 )
134
135 def _map_to_model(self, complexity: TaskComplexity, category: TaskCategory) -> str:
136 """根據複雜度和類別映射到模型"""
137 # 特殊類別覆寫
138 if category in [TaskCategory.REASONING, TaskCategory.CREATIVE]:
139 if complexity in [TaskComplexity.COMPLEX, TaskComplexity.EXPERT]:
140 return "opus"
141
142 if category == TaskCategory.CODE:
143 if complexity == TaskComplexity.EXPERT:
144 return "opus"
145 return "sonnet"
146
147 # 預設按複雜度
148 return COMPLEXITY_MODEL_MAP.get(complexity, "sonnet")
149
150 def route_and_call(
151 self,
152 task: str,
153 system: Optional[str] = None,
154 **kwargs
155 ) -> tuple[str, ClassificationResult]:
156 """
157 分類、路由並呼叫
158
159 Returns:
160 (模型回應, 分類結果)
161 """
162 # Step 1: 分類
163 classification = self.classify(task)
164
165 # Step 2: 取得目標模型
166 model_key = classification.suggested_model
167 model_spec = MODELS[model_key]
168 self._stats["model_usage"][model_key] += 1
169
170 # Step 3: 呼叫目標模型
171 response = client.messages.create(
172 model=model_spec.model_id,
173 max_tokens=kwargs.get("max_tokens", 2048),
174 system=system or "You are a helpful assistant.",
175 messages=[{"role": "user", "content": task}]
176 )
177
178 return response.content[0].text, classification
179
180 def get_stats(self) -> dict:
181 total_requests = sum(self._stats["model_usage"].values())
182 return {
183 "total_classifications": self._stats["classifications"],
184 "cache_hits": self._stats["cache_hits"],
185 "cache_hit_rate": f"{self._stats['cache_hits'] / max(self._stats['classifications'] + self._stats['cache_hits'], 1) * 100:.1f}%",
186 "model_usage": self._stats["model_usage"],
187 "model_distribution": {
188 k: f"{v/max(total_requests, 1)*100:.1f}%"
189 for k, v in self._stats["model_usage"].items()
190 }
191 }
192
193
194# 使用範例
195if __name__ == "__main__":
196 router = LLMClassifierRouter()
197
198 test_tasks = [
199 "把 'Hello World' 翻譯成中文",
200 "寫一個 Python 快速排序演算法",
201 "設計一個支援百萬用戶的即時通訊系統架構",
202 "這段程式碼有什麼問題?\ndef add(a, b): return a + b",
203 "分析 React 和 Vue 的優缺點",
204 ]
205
206 print("LLM 輔助分類路由測試:\n")
207 for task in test_tasks:
208 response, classification = router.route_and_call(task)
209 print(f"任務:{task[:40]}...")
210 print(f"分類:{classification.category.value} / {classification.complexity.value}")
211 print(f"信心度:{classification.confidence}")
212 print(f"路由至:{MODELS[classification.suggested_model].name}")
213 print(f"回應:{response[:100]}...\n")
214
215 print("統計:")
216 print(router.get_stats())
策略三:動態品質評估路由
先用便宜模型嘗試,如果輸出品質不達標再升級。
核心概念
動態升級流程:
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ 使用者任務 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Step 1: Haiku 嘗試 │ │
│ │ 快速生成初步回應 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Step 2: 品質評估 │ │
│ │ - 回應是否完整? │ │
│ │ - 是否包含 "不確定"、"無法" 等標記? │ │
│ │ - 長度是否合理? │ │
│ │ - 是否有明顯錯誤? │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ├── 品質達標 ────────────────────────▶ 返回 Haiku 結果 │
│ │ │
│ ▼ 品質不達標 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Step 3: 升級到 Sonnet │ │
│ │ 使用更強模型重新處理 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ├── 品質達標 ────────────────────────▶ 返回 Sonnet 結果 │
│ │ │
│ ▼ 仍不達標 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Step 4: 升級到 Opus │ │
│ │ 使用最強模型處理 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 優點:大多數任務在 Step 1-2 完成,大幅節省成本 │
│ 缺點:需要設計有效的品質評估機制 │
│ │
└─────────────────────────────────────────────────────────────────────┘
完整實作
1import anthropic
2import re
3from dataclasses import dataclass
4from typing import Optional, Callable
5from enum import Enum
6
7client = anthropic.Anthropic()
8
9class QualityLevel(Enum):
10 """品質等級"""
11 HIGH = "high"
12 ACCEPTABLE = "acceptable"
13 LOW = "low"
14 FAILED = "failed"
15
16@dataclass
17class QualityAssessment:
18 """品質評估結果"""
19 level: QualityLevel
20 score: float # 0-1
21 issues: list[str]
22 should_upgrade: bool
23
24@dataclass
25class EscalationResult:
26 """升級結果"""
27 response: str
28 final_model: str
29 attempts: list[dict] # 每次嘗試的記錄
30 total_cost: float
31
32class QualityChecker:
33 """品質檢查器"""
34
35 # 品質問題指標
36 UNCERTAINTY_MARKERS = [
37 "我不確定", "我無法", "不太清楚", "可能不準確",
38 "I'm not sure", "I cannot", "I don't know",
39 "抱歉,我無法", "這超出了我的能力"
40 ]
41
42 INCOMPLETE_MARKERS = [
43 "...", "等等", "以此類推", "更多內容",
44 "(未完待續)", "TODO", "待補充"
45 ]
46
47 def __init__(
48 self,
49 min_response_length: int = 50,
50 max_uncertainty_ratio: float = 0.1,
51 custom_validators: Optional[list[Callable[[str], bool]]] = None
52 ):
53 self.min_response_length = min_response_length
54 self.max_uncertainty_ratio = max_uncertainty_ratio
55 self.custom_validators = custom_validators or []
56
57 def assess(self, response: str, task: str) -> QualityAssessment:
58 """評估回應品質"""
59 issues = []
60 score = 1.0
61
62 # 檢查長度
63 if len(response) < self.min_response_length:
64 issues.append("回應過短")
65 score -= 0.3
66
67 # 檢查不確定性標記
68 uncertainty_count = sum(
69 1 for marker in self.UNCERTAINTY_MARKERS
70 if marker.lower() in response.lower()
71 )
72 if uncertainty_count > 0:
73 issues.append(f"包含 {uncertainty_count} 個不確定性標記")
74 score -= 0.2 * uncertainty_count
75
76 # 檢查不完整標記
77 incomplete_count = sum(
78 1 for marker in self.INCOMPLETE_MARKERS
79 if marker in response
80 )
81 if incomplete_count > 0:
82 issues.append("回應可能不完整")
83 score -= 0.2
84
85 # 檢查是否拒絕回答
86 refusal_patterns = [
87 r"我無法提供", r"我不能幫助", r"這違反",
88 r"I cannot", r"I'm unable to"
89 ]
90 for pattern in refusal_patterns:
91 if re.search(pattern, response, re.IGNORECASE):
92 issues.append("模型拒絕回答")
93 score -= 0.5
94 break
95
96 # 執行自訂驗證器
97 for validator in self.custom_validators:
98 if not validator(response):
99 issues.append("未通過自訂驗證")
100 score -= 0.2
101
102 # 計算最終評級
103 score = max(0, score)
104 if score >= 0.8:
105 level = QualityLevel.HIGH
106 elif score >= 0.6:
107 level = QualityLevel.ACCEPTABLE
108 elif score >= 0.3:
109 level = QualityLevel.LOW
110 else:
111 level = QualityLevel.FAILED
112
113 return QualityAssessment(
114 level=level,
115 score=score,
116 issues=issues,
117 should_upgrade=level in [QualityLevel.LOW, QualityLevel.FAILED]
118 )
119
120
121class DynamicEscalationRouter:
122 """
123 動態升級路由器
124
125 從便宜模型開始嘗試,品質不達標時升級到更強模型
126 """
127
128 # 模型升級順序
129 MODEL_LADDER = ["haiku", "sonnet", "opus"]
130
131 def __init__(
132 self,
133 quality_checker: Optional[QualityChecker] = None,
134 start_model: str = "haiku",
135 max_attempts: int = 3
136 ):
137 self.quality_checker = quality_checker or QualityChecker()
138 self.start_model = start_model
139 self.max_attempts = max_attempts
140
141 self._stats = {
142 "total_requests": 0,
143 "escalations": 0,
144 "model_attempts": {model: 0 for model in MODELS},
145 "model_finals": {model: 0 for model in MODELS},
146 "total_cost": 0.0
147 }
148
149 def _call_model(self, model_key: str, task: str, system: Optional[str] = None) -> tuple[str, float]:
150 """呼叫模型並計算成本"""
151 model_spec = MODELS[model_key]
152
153 response = client.messages.create(
154 model=model_spec.model_id,
155 max_tokens=2048,
156 system=system or "You are a helpful assistant.",
157 messages=[{"role": "user", "content": task}]
158 )
159
160 # 計算成本
161 input_tokens = response.usage.input_tokens
162 output_tokens = response.usage.output_tokens
163 cost = (
164 input_tokens * model_spec.input_cost_per_1m / 1_000_000 +
165 output_tokens * model_spec.output_cost_per_1m / 1_000_000
166 )
167
168 return response.content[0].text, cost
169
170 def _get_next_model(self, current_model: str) -> Optional[str]:
171 """取得下一個升級模型"""
172 try:
173 current_idx = self.MODEL_LADDER.index(current_model)
174 if current_idx < len(self.MODEL_LADDER) - 1:
175 return self.MODEL_LADDER[current_idx + 1]
176 except ValueError:
177 pass
178 return None
179
180 def route_and_call(
181 self,
182 task: str,
183 system: Optional[str] = None,
184 quality_threshold: QualityLevel = QualityLevel.ACCEPTABLE
185 ) -> EscalationResult:
186 """
187 動態路由並呼叫
188
189 Args:
190 task: 任務
191 system: System prompt
192 quality_threshold: 最低可接受品質等級
193
194 Returns:
195 EscalationResult
196 """
197 self._stats["total_requests"] += 1
198
199 attempts = []
200 current_model = self.start_model
201 total_cost = 0.0
202 final_response = None
203 final_model = None
204
205 acceptable_levels = {
206 QualityLevel.HIGH: [QualityLevel.HIGH],
207 QualityLevel.ACCEPTABLE: [QualityLevel.HIGH, QualityLevel.ACCEPTABLE],
208 QualityLevel.LOW: [QualityLevel.HIGH, QualityLevel.ACCEPTABLE, QualityLevel.LOW],
209 }
210 acceptable = acceptable_levels.get(quality_threshold, [QualityLevel.HIGH, QualityLevel.ACCEPTABLE])
211
212 for attempt in range(self.max_attempts):
213 if current_model is None:
214 break
215
216 # 嘗試當前模型
217 self._stats["model_attempts"][current_model] += 1
218 response, cost = self._call_model(current_model, task, system)
219 total_cost += cost
220
221 # 評估品質
222 assessment = self.quality_checker.assess(response, task)
223
224 attempts.append({
225 "model": current_model,
226 "quality_level": assessment.level.value,
227 "quality_score": assessment.score,
228 "issues": assessment.issues,
229 "cost": cost
230 })
231
232 # 檢查是否達標
233 if assessment.level in acceptable:
234 final_response = response
235 final_model = current_model
236 break
237
238 # 升級到下一個模型
239 self._stats["escalations"] += 1
240 current_model = self._get_next_model(current_model)
241
242 print(f"⬆️ 品質不達標 ({assessment.level.value}),升級模型...")
243
244 # 如果所有嘗試都失敗,使用最後一次的結果
245 if final_response is None and attempts:
246 final_response = response
247 final_model = attempts[-1]["model"]
248
249 self._stats["model_finals"][final_model] += 1
250 self._stats["total_cost"] += total_cost
251
252 return EscalationResult(
253 response=final_response,
254 final_model=final_model,
255 attempts=attempts,
256 total_cost=total_cost
257 )
258
259 def get_stats(self) -> dict:
260 total = self._stats["total_requests"]
261 return {
262 **self._stats,
263 "escalation_rate": f"{self._stats['escalations'] / max(total, 1) * 100:.1f}%",
264 "average_cost": f"${self._stats['total_cost'] / max(total, 1):.4f}",
265 "final_model_distribution": {
266 k: f"{v/max(total, 1)*100:.1f}%"
267 for k, v in self._stats["model_finals"].items()
268 }
269 }
270
271
272# 使用範例
273if __name__ == "__main__":
274 router = DynamicEscalationRouter(
275 start_model="haiku",
276 max_attempts=3
277 )
278
279 test_tasks = [
280 "1 + 1 等於多少?", # 簡單,Haiku 可處理
281 "寫一個 Python 二分搜尋演算法", # 中等,可能需要 Sonnet
282 "設計一個分散式鎖的實現方案,考慮網路分區容錯", # 複雜
283 ]
284
285 print("動態升級路由測試:\n")
286 for task in test_tasks:
287 print(f"任務:{task[:50]}...")
288 result = router.route_and_call(task)
289
290 print(f"最終模型:{MODELS[result.final_model].name}")
291 print(f"嘗試次數:{len(result.attempts)}")
292 print(f"總成本:${result.total_cost:.4f}")
293
294 for i, attempt in enumerate(result.attempts, 1):
295 print(f" 嘗試 {i}: {attempt['model']} - {attempt['quality_level']} (${attempt['cost']:.4f})")
296
297 print(f"回應:{result.response[:100]}...\n")
298
299 print("統計:")
300 for k, v in router.get_stats().items():
301 print(f" {k}: {v}")
策略四:混合智能路由系統
結合多種策略的生產級路由系統。
架構設計
混合智能路由系統:
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ 使用者任務 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 階段 1: 快速規則過濾 │ │
│ │ - 關鍵字匹配強制路由(安全、架構 → Opus) │ │
│ │ - 簡單模式匹配(翻譯、格式化 → Haiku) │ │
│ │ - 成本:0(純本地計算) │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ├── 匹配規則 ─────────────────────────▶ 直接路由 │
│ │ │
│ ▼ 未匹配 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 階段 2: LLM 分類(可選) │ │
│ │ - 使用 Haiku 分析任務複雜度 │ │
│ │ - 帶快取避免重複分類 │ │
│ │ - 成本:~$0.001 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 階段 3: 執行 + 品質監控 │ │
│ │ - 選定模型執行任務 │ │
│ │ - 監控輸出品質 │ │
│ │ - 必要時動態升級 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 階段 4: 回饋學習 │ │
│ │ - 記錄路由決策和結果 │ │
│ │ - 更新規則權重 │ │
│ │ - 優化未來路由 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
完整實作
1import anthropic
2import json
3import time
4from dataclasses import dataclass, field
5from typing import Optional, Callable, Any
6from enum import Enum
7from collections import defaultdict
8
9client = anthropic.Anthropic()
10
11@dataclass
12class RoutingDecision:
13 """路由決策記錄"""
14 task_hash: str
15 selected_model: str
16 selection_method: str # "rule", "llm_classify", "escalation"
17 confidence: float
18 reasoning: str
19 timestamp: float = field(default_factory=time.time)
20
21@dataclass
22class ExecutionResult:
23 """執行結果"""
24 response: str
25 model_used: str
26 routing_decision: RoutingDecision
27 quality_score: float
28 total_cost: float
29 latency_ms: float
30 escalated: bool = False
31
32class HybridIntelligentRouter:
33 """
34 混合智能路由系統
35
36 結合規則、LLM 分類、動態升級的完整路由方案
37 """
38
39 def __init__(
40 self,
41 enable_llm_classification: bool = True,
42 enable_quality_escalation: bool = True,
43 enable_feedback_learning: bool = True
44 ):
45 self.enable_llm_classification = enable_llm_classification
46 self.enable_quality_escalation = enable_quality_escalation
47 self.enable_feedback_learning = enable_feedback_learning
48
49 # 組件
50 self.rule_router = RuleBasedRouter()
51 self.llm_classifier = LLMClassifierRouter() if enable_llm_classification else None
52 self.quality_checker = QualityChecker()
53
54 # 載入標準規則
55 for rule in create_standard_rules():
56 self.rule_router.add_rule(rule)
57
58 # 學習記錄
59 self._routing_history: list[dict] = []
60 self._model_performance: dict[str, list[float]] = defaultdict(list)
61
62 # 統計
63 self._stats = {
64 "total_requests": 0,
65 "rule_matches": 0,
66 "llm_classifications": 0,
67 "escalations": 0,
68 "total_cost": 0.0,
69 "total_latency_ms": 0.0,
70 "model_usage": defaultdict(int)
71 }
72
73 def _compute_task_hash(self, task: str) -> str:
74 """計算任務雜湊"""
75 import hashlib
76 return hashlib.md5(task[:500].encode()).hexdigest()[:12]
77
78 def _call_model(
79 self,
80 model_key: str,
81 task: str,
82 system: Optional[str] = None
83 ) -> tuple[str, float, float]:
84 """呼叫模型,返回 (回應, 成本, 延遲ms)"""
85 model_spec = MODELS[model_key]
86
87 start_time = time.time()
88 response = client.messages.create(
89 model=model_spec.model_id,
90 max_tokens=2048,
91 system=system or "You are a helpful assistant.",
92 messages=[{"role": "user", "content": task}]
93 )
94 latency_ms = (time.time() - start_time) * 1000
95
96 # 計算成本
97 cost = (
98 response.usage.input_tokens * model_spec.input_cost_per_1m / 1_000_000 +
99 response.usage.output_tokens * model_spec.output_cost_per_1m / 1_000_000
100 )
101
102 return response.content[0].text, cost, latency_ms
103
104 def _try_rule_routing(self, task: str) -> Optional[RoutingDecision]:
105 """嘗試規則路由"""
106 for rule in self.rule_router.rules:
107 if rule.condition(task):
108 return RoutingDecision(
109 task_hash=self._compute_task_hash(task),
110 selected_model=rule.model,
111 selection_method="rule",
112 confidence=0.9,
113 reasoning=f"匹配規則: {rule.name}"
114 )
115 return None
116
117 def _try_llm_classification(self, task: str) -> RoutingDecision:
118 """LLM 分類路由"""
119 classification = self.llm_classifier.classify(task)
120 return RoutingDecision(
121 task_hash=self._compute_task_hash(task),
122 selected_model=classification.suggested_model,
123 selection_method="llm_classify",
124 confidence=classification.confidence,
125 reasoning=f"LLM 分類: {classification.category.value}/{classification.complexity.value}"
126 )
127
128 def _try_escalation(
129 self,
130 task: str,
131 current_model: str,
132 current_response: str,
133 system: Optional[str]
134 ) -> Optional[tuple[str, str, float, float]]:
135 """嘗試升級到更強模型"""
136 # 評估當前品質
137 assessment = self.quality_checker.assess(current_response, task)
138
139 if not assessment.should_upgrade:
140 return None
141
142 # 取得下一個模型
143 model_ladder = ["haiku", "sonnet", "opus"]
144 try:
145 current_idx = model_ladder.index(current_model)
146 if current_idx >= len(model_ladder) - 1:
147 return None
148 next_model = model_ladder[current_idx + 1]
149 except ValueError:
150 return None
151
152 # 呼叫更強模型
153 response, cost, latency = self._call_model(next_model, task, system)
154 return response, next_model, cost, latency
155
156 def _record_feedback(
157 self,
158 decision: RoutingDecision,
159 result: ExecutionResult
160 ):
161 """記錄回饋用於學習"""
162 if not self.enable_feedback_learning:
163 return
164
165 record = {
166 "task_hash": decision.task_hash,
167 "model": result.model_used,
168 "method": decision.selection_method,
169 "quality": result.quality_score,
170 "cost": result.total_cost,
171 "latency": result.latency_ms,
172 "escalated": result.escalated,
173 "timestamp": time.time()
174 }
175
176 self._routing_history.append(record)
177 self._model_performance[result.model_used].append(result.quality_score)
178
179 # 保留最近 1000 條記錄
180 if len(self._routing_history) > 1000:
181 self._routing_history = self._routing_history[-1000:]
182
183 def route_and_execute(
184 self,
185 task: str,
186 system: Optional[str] = None
187 ) -> ExecutionResult:
188 """
189 智能路由並執行任務
190
191 流程:
192 1. 嘗試規則匹配
193 2. 若無匹配,使用 LLM 分類
194 3. 執行選定模型
195 4. 評估品質,必要時升級
196 5. 記錄回饋
197 """
198 self._stats["total_requests"] += 1
199 total_cost = 0.0
200 total_latency = 0.0
201
202 # 階段 1: 規則路由
203 decision = self._try_rule_routing(task)
204 if decision:
205 self._stats["rule_matches"] += 1
206 elif self.enable_llm_classification:
207 # 階段 2: LLM 分類
208 decision = self._try_llm_classification(task)
209 self._stats["llm_classifications"] += 1
210 # 分類成本
211 total_cost += 0.001 # 估算 Haiku 分類成本
212 else:
213 # 預設使用 Sonnet
214 decision = RoutingDecision(
215 task_hash=self._compute_task_hash(task),
216 selected_model="sonnet",
217 selection_method="default",
218 confidence=0.5,
219 reasoning="預設路由"
220 )
221
222 # 階段 3: 執行
223 selected_model = decision.selected_model
224 response, cost, latency = self._call_model(selected_model, task, system)
225 total_cost += cost
226 total_latency += latency
227 self._stats["model_usage"][selected_model] += 1
228
229 # 階段 4: 品質評估和可能的升級
230 escalated = False
231 if self.enable_quality_escalation:
232 escalation_result = self._try_escalation(task, selected_model, response, system)
233 if escalation_result:
234 response, selected_model, esc_cost, esc_latency = escalation_result
235 total_cost += esc_cost
236 total_latency += esc_latency
237 escalated = True
238 self._stats["escalations"] += 1
239 self._stats["model_usage"][selected_model] += 1
240
241 # 最終品質評估
242 final_assessment = self.quality_checker.assess(response, task)
243
244 # 更新統計
245 self._stats["total_cost"] += total_cost
246 self._stats["total_latency_ms"] += total_latency
247
248 # 構建結果
249 result = ExecutionResult(
250 response=response,
251 model_used=selected_model,
252 routing_decision=decision,
253 quality_score=final_assessment.score,
254 total_cost=total_cost,
255 latency_ms=total_latency,
256 escalated=escalated
257 )
258
259 # 階段 5: 記錄回饋
260 self._record_feedback(decision, result)
261
262 return result
263
264 def get_stats(self) -> dict:
265 """取得統計資訊"""
266 total = self._stats["total_requests"]
267 return {
268 "total_requests": total,
269 "rule_match_rate": f"{self._stats['rule_matches'] / max(total, 1) * 100:.1f}%",
270 "llm_classification_rate": f"{self._stats['llm_classifications'] / max(total, 1) * 100:.1f}%",
271 "escalation_rate": f"{self._stats['escalations'] / max(total, 1) * 100:.1f}%",
272 "total_cost": f"${self._stats['total_cost']:.4f}",
273 "average_cost": f"${self._stats['total_cost'] / max(total, 1):.4f}",
274 "average_latency_ms": f"{self._stats['total_latency_ms'] / max(total, 1):.0f}",
275 "model_usage": dict(self._stats["model_usage"]),
276 "model_distribution": {
277 k: f"{v/max(total, 1)*100:.1f}%"
278 for k, v in self._stats["model_usage"].items()
279 }
280 }
281
282 def get_model_performance_summary(self) -> dict:
283 """取得各模型效能摘要"""
284 summary = {}
285 for model, scores in self._model_performance.items():
286 if scores:
287 summary[model] = {
288 "sample_count": len(scores),
289 "avg_quality": f"{sum(scores)/len(scores):.2f}",
290 "min_quality": f"{min(scores):.2f}",
291 "max_quality": f"{max(scores):.2f}"
292 }
293 return summary
294
295
296# 使用範例與效能測試
297if __name__ == "__main__":
298 router = HybridIntelligentRouter(
299 enable_llm_classification=True,
300 enable_quality_escalation=True,
301 enable_feedback_learning=True
302 )
303
304 # 綜合測試任務
305 test_tasks = [
306 # 簡單任務(應該用 Haiku)
307 "把 'Hello' 翻譯成中文",
308 "列出 1 到 10 的數字",
309 "總結:今天天氣很好。",
310
311 # 中等任務(應該用 Sonnet)
312 "寫一個 Python 函數計算陣列的平均值",
313 "解釋 REST API 和 GraphQL 的差異",
314 "分析這段程式碼的時間複雜度",
315
316 # 複雜任務(可能需要 Opus)
317 "設計一個支援百萬用戶的即時通訊系統,考慮高可用和水平擴展",
318 "審查這段程式碼的安全漏洞並提供修復建議",
319 ]
320
321 print("="*70)
322 print("混合智能路由系統測試")
323 print("="*70)
324
325 for task in test_tasks:
326 print(f"\n任務:{task[:50]}...")
327 result = router.route_and_execute(task)
328
329 print(f" 路由方法:{result.routing_decision.selection_method}")
330 print(f" 初始模型:{result.routing_decision.selected_model}")
331 print(f" 最終模型:{result.model_used}")
332 print(f" 是否升級:{'是' if result.escalated else '否'}")
333 print(f" 品質分數:{result.quality_score:.2f}")
334 print(f" 成本:${result.total_cost:.4f}")
335 print(f" 延遲:{result.latency_ms:.0f}ms")
336
337 print("\n" + "="*70)
338 print("統計摘要")
339 print("="*70)
340 for k, v in router.get_stats().items():
341 print(f" {k}: {v}")
342
343 print("\n模型效能摘要:")
344 for model, perf in router.get_model_performance_summary().items():
345 print(f" {model}: {perf}")
成本優化計算器
成本對比工具
1from dataclasses import dataclass
2from typing import Optional
3
4@dataclass
5class UsageScenario:
6 """使用場景"""
7 name: str
8 daily_requests: int
9 avg_input_tokens: int
10 avg_output_tokens: int
11 complexity_distribution: dict[str, float] # model -> percentage
12
13class CostCalculator:
14 """成本計算器"""
15
16 def __init__(self):
17 self.models = MODELS
18
19 def calculate_monthly_cost(
20 self,
21 model_key: str,
22 daily_requests: int,
23 avg_input_tokens: int,
24 avg_output_tokens: int
25 ) -> float:
26 """計算單一模型的月成本"""
27 model = self.models[model_key]
28 daily_cost = (
29 daily_requests * avg_input_tokens * model.input_cost_per_1m / 1_000_000 +
30 daily_requests * avg_output_tokens * model.output_cost_per_1m / 1_000_000
31 )
32 return daily_cost * 30
33
34 def calculate_tiered_cost(self, scenario: UsageScenario) -> dict:
35 """計算分層策略的成本"""
36 results = {
37 "scenario": scenario.name,
38 "daily_requests": scenario.daily_requests,
39 "monthly_requests": scenario.daily_requests * 30
40 }
41
42 # 計算全部使用各模型的成本
43 for model_key in self.models:
44 monthly_cost = self.calculate_monthly_cost(
45 model_key,
46 scenario.daily_requests,
47 scenario.avg_input_tokens,
48 scenario.avg_output_tokens
49 )
50 results[f"all_{model_key}"] = monthly_cost
51
52 # 計算分層策略成本
53 tiered_cost = 0
54 for model_key, percentage in scenario.complexity_distribution.items():
55 requests = scenario.daily_requests * percentage
56 cost = self.calculate_monthly_cost(
57 model_key,
58 requests,
59 scenario.avg_input_tokens,
60 scenario.avg_output_tokens
61 )
62 tiered_cost += cost
63
64 results["tiered_strategy"] = tiered_cost
65
66 # 計算節省
67 results["savings_vs_opus"] = (results["all_opus"] - tiered_cost) / results["all_opus"] * 100
68 results["savings_vs_sonnet"] = (results["all_sonnet"] - tiered_cost) / results["all_sonnet"] * 100
69
70 return results
71
72 def print_comparison(self, scenario: UsageScenario):
73 """列印成本對比"""
74 results = self.calculate_tiered_cost(scenario)
75
76 print(f"\n{'='*60}")
77 print(f"場景:{results['scenario']}")
78 print(f"每日請求:{results['daily_requests']:,}")
79 print(f"每月請求:{results['monthly_requests']:,}")
80 print(f"{'='*60}")
81 print(f"\n月度成本對比:")
82 print(f" 全部使用 Opus: ${results['all_opus']:,.2f}")
83 print(f" 全部使用 Sonnet: ${results['all_sonnet']:,.2f}")
84 print(f" 全部使用 Haiku: ${results['all_haiku']:,.2f}")
85 print(f" 智能分層策略: ${results['tiered_strategy']:,.2f}")
86 print(f"\n節省:")
87 print(f" 相比 Opus: {results['savings_vs_opus']:.1f}%")
88 print(f" 相比 Sonnet: {results['savings_vs_sonnet']:.1f}%")
89
90
91# 使用範例
92if __name__ == "__main__":
93 calculator = CostCalculator()
94
95 # 定義不同場景
96 scenarios = [
97 UsageScenario(
98 name="小型 SaaS 應用",
99 daily_requests=1000,
100 avg_input_tokens=1500,
101 avg_output_tokens=500,
102 complexity_distribution={
103 "haiku": 0.60, # 60% 簡單任務
104 "sonnet": 0.35, # 35% 中等任務
105 "opus": 0.05 # 5% 複雜任務
106 }
107 ),
108 UsageScenario(
109 name="中型企業客服",
110 daily_requests=10000,
111 avg_input_tokens=2000,
112 avg_output_tokens=800,
113 complexity_distribution={
114 "haiku": 0.70,
115 "sonnet": 0.25,
116 "opus": 0.05
117 }
118 ),
119 UsageScenario(
120 name="大型開發輔助平台",
121 daily_requests=50000,
122 avg_input_tokens=3000,
123 avg_output_tokens=1500,
124 complexity_distribution={
125 "haiku": 0.40,
126 "sonnet": 0.50,
127 "opus": 0.10
128 }
129 ),
130 ]
131
132 for scenario in scenarios:
133 calculator.print_comparison(scenario)
輸出範例:
============================================================
場景:小型 SaaS 應用
每日請求:1,000
每月請求:30,000
============================================================
月度成本對比:
全部使用 Opus: $2,025.00
全部使用 Sonnet: $405.00
全部使用 Haiku: $84.00
智能分層策略: $202.50
節省:
相比 Opus: 90.0%
相比 Sonnet: 50.0%
============================================================
場景:中型企業客服
每日請求:10,000
每月請求:300,000
============================================================
月度成本對比:
全部使用 Opus: $25,200.00
全部使用 Sonnet: $5,040.00
全部使用 Haiku: $1,032.00
智能分層策略: $2,016.00
節省:
相比 Opus: 92.0%
相比 Sonnet: 60.0%
最佳實踐清單
模型分層實施 Checklist:
任務分析
□ 是否分析了任務類型分布?
□ 是否識別了必須用高階模型的場景?
□ 是否有明確的複雜度判斷標準?
路由設計
□ 是否設計了規則優先的快速路由?
□ 是否有 LLM 分類的備案?
□ 是否支援動態升級機制?
品質保障
□ 是否有輸出品質評估機制?
□ 是否設定了品質閾值?
□ 是否有升級觸發條件?
成本控制
□ 是否計算了預期成本節省?
□ 是否設定了成本上限警報?
□ 是否追蹤實際成本數據?
監控與優化
□ 是否追蹤各模型使用比例?
□ 是否記錄路由決策用於分析?
□ 是否定期審查路由規則效果?
總結
模型分層是優化 AI 應用成本的核心策略。本文介紹的方案可以根據需求靈活組合:
| 場景 | 推薦策略 |
|---|---|
| 快速上線 | 規則基礎路由 |
| 精確分類 | LLM 輔助分類 |
| 品質優先 | 動態升級路由 |
| 生產環境 | 混合智能路由 |
關鍵原則:
- 大多數任務用便宜模型:實際上 60-70% 的任務 Haiku 就能處理
- 關鍵任務不省成本:安全、架構等關鍵決策用最強模型
- 持續監控優化:追蹤實際分布,調整路由策略
- 品質保障機制:設置品質閾值,必要時自動升級
透過合理的模型分層,你可以在保持輸出品質的同時,將 AI 應用成本降低 50-90%。
