{"product_id":"smart-model-routing-tu-dong-chon-haiku-sonnet-opus-theo-task","title":"Smart Model Routing — Tu dong chon Haiku\/Sonnet\/Opus theo task","description":"\n\u003cp\u003eKhông phải mọi request đều cần model mạnh nhất. Một câu hỏi \"Hôm nay thứ mấy?\" không cần Opus, nhưng một yêu cầu phân tích hợp đồng pháp lý thì Haiku không đủ. Smart Model Routing giúp bạn tự động chọn model phù hợp, giảm chi phí 60-80% mà vẫn đảm bảo chất lượng.\u003c\/p\u003e\n\n\u003ch2\u003eTại sao cần Model Routing?\u003c\/h2\u003e\n\u003cp\u003eCác model Claude có độ mạnh và chi phí khác nhau đáng kể:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e\n\u003cstrong\u003eHaiku:\u003c\/strong\u003e Nhanh nhất, rẻ nhất. Phù hợp cho các tác vụ đơn giản: phân loại, trích xuất, trả lời ngắn\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eSonnet:\u003c\/strong\u003e Cân bằng giữa tốc độ và chất lượng. Phù hợp cho đa số tác vụ: viết nội dung, phân tích, code\u003c\/li\u003e\n  \u003cli\u003e\n\u003cstrong\u003eOpus:\u003c\/strong\u003e Mạnh nhất, đắt nhất. Chỉ cần cho tác vụ phức tạp: suy luận nhiều bước, phân tích sâu, sáng tạo phức tạp\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch3\u003eChi phí so sánh\u003c\/h3\u003e\n\u003cp\u003eGiả sử một ứng dụng xử lý 100,000 requests\/ngày. Nếu dùng Opus cho tất cả:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003e100% Opus: chi phí rất cao, nhưng 70% request không cần sức mạnh đó\u003c\/li\u003e\n  \u003cli\u003eVới routing: 60% Haiku + 30% Sonnet + 10% Opus = giảm 70% chi phí tổng\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003cp\u003eNhưng quan trọng hơn chi phí, routing còn đảm bảo response time tốt hơn. Haiku trả lời trong 200-500ms, trong khi Opus có thể mất 5-15 giây. Người dùng hỏi câu đơn giản mà đợi 10 giây là trải nghiệm tệ.\u003c\/p\u003e\n\n\u003ch2\u003eClassification-based Routing\u003c\/h2\u003e\n\u003cp\u003ePhương pháp phổ biến nhất: dùng một model nhẹ (Haiku) để phân loại độ phức tạp của request, rồi route đến model phù hợp.\u003c\/p\u003e\n\n\u003ch3\u003eKiến trúc tổng quan\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Flow:\n# User Request -\u0026gt; Classifier (Haiku) -\u0026gt; Route Decision -\u0026gt; Target Model -\u0026gt; Response\n\n# Classifier output:\n# - \"simple\"  -\u0026gt; Haiku  (phan loai, tra loi ngan, extraction)\n# - \"medium\"  -\u0026gt; Sonnet (viet content, phan tich, code generation)\n# - \"complex\" -\u0026gt; Opus   (suy luan phuc tap, phan tich sau, sang tao)\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003ePython implementation\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eimport anthropic\nfrom enum import Enum\nfrom dataclasses import dataclass\n\nclass Complexity(Enum):\n    SIMPLE = \"simple\"\n    MEDIUM = \"medium\"\n    COMPLEX = \"complex\"\n\n@dataclass\nclass RouteDecision:\n    complexity: Complexity\n    model: str\n    reason: str\n    confidence: float\n\nclass ModelRouter:\n    \"\"\"Router tu dong chon model dua tren do phuc tap cua request.\"\"\"\n\n    MODEL_MAP = {\n        Complexity.SIMPLE: \"claude-haiku-4-20250514\",\n        Complexity.MEDIUM: \"claude-sonnet-4-20250514\",\n        Complexity.COMPLEX: \"claude-opus-4-20250514\",\n    }\n\n    COST_PER_1K_INPUT = {\n        \"claude-haiku-4-20250514\": 0.001,\n        \"claude-sonnet-4-20250514\": 0.003,\n        \"claude-opus-4-20250514\": 0.015,\n    }\n\n    def __init__(self):\n        self.client = anthropic.Anthropic()\n        self.stats = {\"simple\": 0, \"medium\": 0, \"complex\": 0}\n\n    def classify(self, user_message: str) -\u0026gt; RouteDecision:\n        \"\"\"Dung Haiku de phan loai do phuc tap cua request.\"\"\"\n\n        classification_prompt = f\"\"\"Phan loai do phuc tap cua yeu cau sau.\nTra loi CHINH XAC mot trong ba muc: simple, medium, complex.\n\nQuy tac phan loai:\n- simple: Cau hoi co tra loi ngan, phan loai, trich xuat thong tin,\n  dich thuat ngan, tinh toan don gian\n- medium: Viet noi dung 1-3 doan, phan tich van ban, giai thich\n  khai niem, viet code don gian, tom tat\n- complex: Suy luan nhieu buoc, phan tich phuc tap, viet code phuc tap,\n  so sanh nhieu yeu to, sang tao dai, phan tich phap ly\/tai chinh\n\nYeu cau: {user_message}\n\nTra loi theo format:\nCOMPLEXITY: [simple|medium|complex]\nCONFIDENCE: [0.0-1.0]\nREASON: [ly do ngan gon]\"\"\"\n\n        response = self.client.messages.create(\n            model=\"claude-haiku-4-20250514\",\n            max_tokens=100,\n            messages=[{\"role\": \"user\", \"content\": classification_prompt}]\n        )\n\n        result = response.content[0].text\n        return self._parse_classification(result)\n\n    def _parse_classification(self, result: str) -\u0026gt; RouteDecision:\n        \"\"\"Parse ket qua phan loai tu Haiku.\"\"\"\n        lines = result.strip().split('\n')\n        complexity_str = \"medium\"\n        confidence = 0.8\n        reason = \"\"\n\n        for line in lines:\n            if line.startswith(\"COMPLEXITY:\"):\n                complexity_str = line.split(\":\")[1].strip().lower()\n            elif line.startswith(\"CONFIDENCE:\"):\n                try:\n                    confidence = float(line.split(\":\")[1].strip())\n                except ValueError:\n                    confidence = 0.8\n            elif line.startswith(\"REASON:\"):\n                reason = line.split(\":\", 1)[1].strip()\n\n        complexity = Complexity(complexity_str)\n        model = self.MODEL_MAP[complexity]\n\n        return RouteDecision(\n            complexity=complexity,\n            model=model,\n            reason=reason,\n            confidence=confidence\n        )\n\n    def route(self, user_message: str, system_prompt: str = \"\") -\u0026gt; str:\n        \"\"\"Phan loai va gui request den model phu hop.\"\"\"\n\n        # Buoc 1: Phan loai\n        decision = self.classify(user_message)\n        self.stats[decision.complexity.value] += 1\n\n        print(f\"Routing to {decision.model} \"\n              f\"(complexity: {decision.complexity.value}, \"\n              f\"confidence: {decision.confidence:.0%})\")\n\n        # Buoc 2: Gui request den model da chon\n        messages = [{\"role\": \"user\", \"content\": user_message}]\n        kwargs = {\n            \"model\": decision.model,\n            \"max_tokens\": 4096,\n            \"messages\": messages,\n        }\n        if system_prompt:\n            kwargs[\"system\"] = system_prompt\n\n        response = self.client.messages.create(**kwargs)\n\n        return response.content[0].text\n\n    def get_stats(self) -\u0026gt; dict:\n        \"\"\"Tra ve thong ke routing.\"\"\"\n        total = sum(self.stats.values())\n        if total == 0:\n            return self.stats\n        return {\n            k: {\"count\": v, \"percentage\": f\"{v\/total*100:.1f}%\"}\n            for k, v in self.stats.items()\n        }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eSử dụng router\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Khoi tao router\nrouter = ModelRouter()\n\n# Cac request khac nhau se duoc route den model khac nhau\n\n# Request don gian -\u0026gt; Haiku\nresult1 = router.route(\"Dich sang tieng Anh: Xin chao\")\n# Output: Routing to claude-haiku-4-20250514 (complexity: simple)\n\n# Request trung binh -\u0026gt; Sonnet\nresult2 = router.route(\"Viet mot email xin loi khach hang ve viec giao hang tre 3 ngay\")\n# Output: Routing to claude-sonnet-4-20250514 (complexity: medium)\n\n# Request phuc tap -\u0026gt; Opus\nresult3 = router.route(\n    \"Phan tich uu nhuoc diem cua 5 kien truc microservices pho bien, \"\n    \"so sanh voi monolith trong boi canh startup Viet Nam co 10 developer, \"\n    \"de xuat kien truc phu hop nhat voi lo trinh chuyen doi 2 nam\"\n)\n# Output: Routing to claude-opus-4-20250514 (complexity: complex)\n\n# Xem thong ke\nprint(router.get_stats())\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eComplexity Scoring — Phương pháp nâng cao\u003c\/h2\u003e\n\u003cp\u003eThay vì chỉ dùng Haiku phân loại, bạn có thể kết hợp nhiều tín hiệu để tính điểm phức tạp (complexity score) mà không cần gọi API:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport re\nfrom typing import List\n\nclass ComplexityScorer:\n    \"\"\"Tinh diem phuc tap cua request dua tren nhieu tin hieu.\"\"\"\n\n    # Tu khoa chi muc phuc tap cao\n    COMPLEX_KEYWORDS = [\n        \"phan tich\", \"so sanh\", \"danh gia\", \"thiet ke\",\n        \"kien truc\", \"chien luoc\", \"toi uu\", \"debug\",\n        \"review code\", \"refactor\", \"bao mat\", \"hieu suat\"\n    ]\n\n    # Tu khoa chi muc don gian\n    SIMPLE_KEYWORDS = [\n        \"dich\", \"tom tat ngan\", \"la gi\", \"dinh nghia\",\n        \"chuyen doi\", \"tinh\", \"liet ke\", \"format\"\n    ]\n\n    def score(self, message: str) -\u0026gt; float:\n        \"\"\"Tra ve diem phuc tap tu 0.0 (don gian) den 1.0 (phuc tap).\"\"\"\n        scores = []\n\n        # 1. Do dai message (0.0 - 0.3)\n        word_count = len(message.split())\n        if word_count \u0026lt; 20:\n            scores.append(0.0)\n        elif word_count \u0026lt; 50:\n            scores.append(0.1)\n        elif word_count \u0026lt; 150:\n            scores.append(0.2)\n        else:\n            scores.append(0.3)\n\n        # 2. Tu khoa phuc tap (0.0 - 0.3)\n        complex_count = sum(\n            1 for kw in self.COMPLEX_KEYWORDS\n            if kw in message.lower()\n        )\n        scores.append(min(complex_count * 0.1, 0.3))\n\n        # 3. Tu khoa don gian (giam diem) (-0.2 - 0.0)\n        simple_count = sum(\n            1 for kw in self.SIMPLE_KEYWORDS\n            if kw in message.lower()\n        )\n        scores.append(max(-simple_count * 0.1, -0.2))\n\n        # 4. Yeu cau nhieu buoc (0.0 - 0.2)\n        step_indicators = len(re.findall(\n            r'(d+.|buoc|sau do|tiep theo|cuoi cung)', message.lower()\n        ))\n        scores.append(min(step_indicators * 0.05, 0.2))\n\n        # 5. Co code\/data dinh kem (0.0 - 0.2)\n        has_code = '```' in message or 'def ' in message or 'function ' in message\n        scores.append(0.15 if has_code else 0.0)\n\n        total = max(0.0, min(1.0, sum(scores)))\n        return round(total, 2)\n\n    def get_model(self, score: float) -\u0026gt; str:\n        \"\"\"Chon model dua tren diem phuc tap.\"\"\"\n        if score \u0026lt; 0.3:\n            return \"claude-haiku-4-20250514\"\n        elif score \u0026lt; 0.7:\n            return \"claude-sonnet-4-20250514\"\n        else:\n            return \"claude-opus-4-20250514\"\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eKết hợp hai phương pháp\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003eclass HybridRouter:\n    \"\"\"Ket hop rule-based scoring voi LLM classification.\"\"\"\n\n    def __init__(self):\n        self.scorer = ComplexityScorer()\n        self.llm_router = ModelRouter()\n\n    def route(self, message: str) -\u0026gt; str:\n        # Buoc 1: Rule-based scoring (mien phi, nhanh)\n        score = self.scorer.score(message)\n\n        # Neu diem rat thap hoac rat cao, khong can goi Haiku\n        if score \u0026lt; 0.15:\n            model = \"claude-haiku-4-20250514\"\n            print(f\"Fast route (score={score}): {model}\")\n        elif score \u0026gt; 0.85:\n            model = \"claude-opus-4-20250514\"\n            print(f\"Fast route (score={score}): {model}\")\n        else:\n            # Vung xam: dung Haiku de phan loai chinh xac hon\n            decision = self.llm_router.classify(message)\n            model = decision.model\n            print(f\"LLM route (score={score}, \"\n                  f\"llm={decision.complexity.value}): {model}\")\n\n        # Buoc 2: Gui request\n        client = anthropic.Anthropic()\n        response = client.messages.create(\n            model=model,\n            max_tokens=4096,\n            messages=[{\"role\": \"user\", \"content\": message}]\n        )\n        return response.content[0].text\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eFallback Chains\u003c\/h2\u003e\n\u003cp\u003eKhi model được chọn không trả về kết quả tốt (output quá ngắn, từ chối trả lời, hoặc người dùng không hài lòng), bạn cần fallback lên model mạnh hơn:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eclass FallbackRouter:\n    \"\"\"Router voi co che fallback tu dong.\"\"\"\n\n    CHAIN = [\n        \"claude-haiku-4-20250514\",\n        \"claude-sonnet-4-20250514\",\n        \"claude-opus-4-20250514\",\n    ]\n\n    def __init__(self):\n        self.client = anthropic.Anthropic()\n\n    def route_with_fallback(\n        self,\n        message: str,\n        start_model: str,\n        min_response_length: int = 50,\n        max_retries: int = 2\n    ) -\u0026gt; dict:\n        \"\"\"Gui request voi co che fallback.\"\"\"\n        start_idx = self.CHAIN.index(start_model)\n\n        for i in range(start_idx, min(start_idx + max_retries + 1, len(self.CHAIN))):\n            model = self.CHAIN[i]\n\n            try:\n                response = self.client.messages.create(\n                    model=model,\n                    max_tokens=4096,\n                    messages=[{\"role\": \"user\", \"content\": message}]\n                )\n\n                result = response.content[0].text\n\n                # Kiem tra chat luong output\n                if len(result) \u0026gt;= min_response_length:\n                    return {\n                        \"model_used\": model,\n                        \"fallback_count\": i - start_idx,\n                        \"response\": result,\n                        \"input_tokens\": response.usage.input_tokens,\n                        \"output_tokens\": response.usage.output_tokens,\n                    }\n                else:\n                    print(f\"{model}: Response qua ngan ({len(result)} chars), \"\n                          f\"fallback len model manh hon...\")\n\n            except Exception as e:\n                print(f\"{model}: Error - {e}, fallback...\")\n\n        return {\n            \"model_used\": self.CHAIN[-1],\n            \"fallback_count\": len(self.CHAIN) - start_idx - 1,\n            \"response\": \"Khong the xu ly yeu cau nay.\",\n            \"error\": True\n        }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eA\/B Testing Models\u003c\/h2\u003e\n\u003cp\u003eTrước khi quyết định routing strategy, bạn nên A\/B test để biết model nào phù hợp nhất cho từng loại task của ứng dụng:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eimport random\nimport json\nimport time\nfrom datetime import datetime\n\nclass ModelABTest:\n    \"\"\"A\/B testing giua cac model Claude.\"\"\"\n\n    def __init__(self, test_name: str, models: list, split: list = None):\n        self.test_name = test_name\n        self.models = models\n        self.split = split or [1.0 \/ len(models)] * len(models)\n        self.results = []\n\n    def select_model(self) -\u0026gt; str:\n        \"\"\"Chon model theo ty le split.\"\"\"\n        rand = random.random()\n        cumulative = 0\n        for model, ratio in zip(self.models, self.split):\n            cumulative += ratio\n            if rand \u0026lt;= cumulative:\n                return model\n        return self.models[-1]\n\n    def run_test(self, message: str, evaluate_fn=None) -\u0026gt; dict:\n        \"\"\"Chay mot test va ghi lai ket qua.\"\"\"\n        model = self.select_model()\n        client = anthropic.Anthropic()\n\n        start_time = time.time()\n        response = client.messages.create(\n            model=model,\n            max_tokens=4096,\n            messages=[{\"role\": \"user\", \"content\": message}]\n        )\n        latency = time.time() - start_time\n\n        result_text = response.content[0].text\n\n        record = {\n            \"timestamp\": datetime.now().isoformat(),\n            \"model\": model,\n            \"message_preview\": message[:100],\n            \"response_length\": len(result_text),\n            \"latency_seconds\": round(latency, 2),\n            \"input_tokens\": response.usage.input_tokens,\n            \"output_tokens\": response.usage.output_tokens,\n        }\n\n        # Danh gia chat luong neu co evaluate function\n        if evaluate_fn:\n            record[\"quality_score\"] = evaluate_fn(message, result_text)\n\n        self.results.append(record)\n        return {\"response\": result_text, \"metadata\": record}\n\n    def get_summary(self) -\u0026gt; dict:\n        \"\"\"Tong hop ket qua A\/B test.\"\"\"\n        summary = {}\n        for model in self.models:\n            model_results = [r for r in self.results if r[\"model\"] == model]\n            if not model_results:\n                continue\n\n            summary[model] = {\n                \"count\": len(model_results),\n                \"avg_latency\": round(\n                    sum(r[\"latency_seconds\"] for r in model_results) \/ len(model_results), 2\n                ),\n                \"avg_response_length\": round(\n                    sum(r[\"response_length\"] for r in model_results) \/ len(model_results)\n                ),\n                \"avg_tokens\": round(\n                    sum(r[\"output_tokens\"] for r in model_results) \/ len(model_results)\n                ),\n            }\n\n            quality_scores = [r.get(\"quality_score\") for r in model_results if r.get(\"quality_score\")]\n            if quality_scores:\n                summary[model][\"avg_quality\"] = round(\n                    sum(quality_scores) \/ len(quality_scores), 2\n                )\n\n        return summary\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eChạy A\/B test\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Tao test so sanh Haiku va Sonnet cho viec tom tat\ntest = ModelABTest(\n    test_name=\"summarization_test\",\n    models=[\"claude-haiku-4-20250514\", \"claude-sonnet-4-20250514\"],\n    split=[0.5, 0.5]  # 50\/50\n)\n\n# Chay 100 requests\ntest_messages = [\n    \"Tom tat bai viet sau trong 3 cau: ...\",\n    \"Tom tat email nay thanh 1 doan: ...\",\n    # ... them cac messages test\n]\n\nfor msg in test_messages:\n    test.run_test(msg)\n\n# Xem ket qua\nsummary = test.get_summary()\nprint(json.dumps(summary, indent=2))\n\n# Ket qua giup ban quyet dinh:\n# Neu Haiku tom tat du tot -\u0026gt; dung Haiku cho task nay (re hon 5x)\n# Neu Sonnet tot hon dang ke -\u0026gt; chi phi them la xung dang\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eĐo lường chi phí tiết kiệm\u003c\/h2\u003e\n\u003cp\u003eĐể chứng minh giá trị của routing, bạn cần đo lường chi phí thực tế:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eclass CostTracker:\n    \"\"\"Theo doi chi phi API voi va khong co routing.\"\"\"\n\n    PRICING = {\n        \"claude-haiku-4-20250514\": {\"input\": 0.001, \"output\": 0.005},\n        \"claude-sonnet-4-20250514\": {\"input\": 0.003, \"output\": 0.015},\n        \"claude-opus-4-20250514\": {\"input\": 0.015, \"output\": 0.075},\n    }\n\n    def __init__(self):\n        self.requests = []\n\n    def log_request(self, model: str, input_tokens: int, output_tokens: int):\n        cost = (\n            (input_tokens \/ 1000) * self.PRICING[model][\"input\"] +\n            (output_tokens \/ 1000) * self.PRICING[model][\"output\"]\n        )\n        self.requests.append({\n            \"model\": model,\n            \"input_tokens\": input_tokens,\n            \"output_tokens\": output_tokens,\n            \"cost\": cost\n        })\n\n    def compare_with_single_model(self, baseline_model: str) -\u0026gt; dict:\n        \"\"\"So sanh chi phi routing vs dung 1 model cho tat ca.\"\"\"\n        actual_cost = sum(r[\"cost\"] for r in self.requests)\n\n        # Tinh chi phi neu dung baseline cho tat ca\n        baseline_cost = sum(\n            (r[\"input_tokens\"] \/ 1000) * self.PRICING[baseline_model][\"input\"] +\n            (r[\"output_tokens\"] \/ 1000) * self.PRICING[baseline_model][\"output\"]\n            for r in self.requests\n        )\n\n        savings = baseline_cost - actual_cost\n        savings_pct = (savings \/ baseline_cost * 100) if baseline_cost \u0026gt; 0 else 0\n\n        return {\n            \"total_requests\": len(self.requests),\n            \"routing_cost\": f\"${actual_cost:.4f}\",\n            f\"all_{baseline_model}_cost\": f\"${baseline_cost:.4f}\",\n            \"savings\": f\"${savings:.4f}\",\n            \"savings_percentage\": f\"{savings_pct:.1f}%\",\n        }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eDomain-specific Routing\u003c\/h2\u003e\n\u003cp\u003eNgoài độ phức tạp, bạn có thể route dựa trên domain của request. Mỗi domain có model phù hợp riêng:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eclass DomainRouter:\n    \"\"\"Route model dua tren domain cua request.\"\"\"\n\n    DOMAIN_MODELS = {\n        # Domain -\u0026gt; (default_model, description)\n        \"translation\": (\"claude-haiku-4-20250514\", \"Dich thuat don gian dung Haiku\"),\n        \"summarization\": (\"claude-haiku-4-20250514\", \"Tom tat ngan dung Haiku\"),\n        \"code_generation\": (\"claude-sonnet-4-20250514\", \"Viet code dung Sonnet\"),\n        \"content_writing\": (\"claude-sonnet-4-20250514\", \"Viet noi dung dung Sonnet\"),\n        \"analysis\": (\"claude-sonnet-4-20250514\", \"Phan tich dung Sonnet\"),\n        \"legal_review\": (\"claude-opus-4-20250514\", \"Review phap ly dung Opus\"),\n        \"architecture\": (\"claude-opus-4-20250514\", \"Thiet ke kien truc dung Opus\"),\n        \"research\": (\"claude-opus-4-20250514\", \"Nghien cuu sau dung Opus\"),\n    }\n\n    def __init__(self):\n        self.client = anthropic.Anthropic()\n        self.classifier = ModelRouter()\n\n    def detect_domain(self, message: str) -\u0026gt; str:\n        \"\"\"Dung Haiku de xac dinh domain cua request.\"\"\"\n        prompt = f\"\"\"Phan loai yeu cau sau vao MOT trong cac domain:\ntranslation, summarization, code_generation, content_writing,\nanalysis, legal_review, architecture, research\n\nYeu cau: {message}\n\nTra loi CHI MOT tu (domain name):\"\"\"\n\n        response = self.client.messages.create(\n            model=\"claude-haiku-4-20250514\",\n            max_tokens=20,\n            messages=[{\"role\": \"user\", \"content\": prompt}]\n        )\n        domain = response.content[0].text.strip().lower()\n        return domain if domain in self.DOMAIN_MODELS else \"analysis\"\n\n    def route(self, message: str) -\u0026gt; dict:\n        \"\"\"Route request den model phu hop theo domain.\"\"\"\n        domain = self.detect_domain(message)\n        model, reason = self.DOMAIN_MODELS[domain]\n\n        response = self.client.messages.create(\n            model=model,\n            max_tokens=4096,\n            messages=[{\"role\": \"user\", \"content\": message}]\n        )\n\n        return {\n            \"domain\": domain,\n            \"model\": model,\n            \"reason\": reason,\n            \"response\": response.content[0].text,\n        }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eKết hợp Domain và Complexity routing\u003c\/h3\u003e\n\u003cp\u003eTrong thực tế, bạn nên kết hợp cả hai phương pháp. Ví dụ: dịch thuật thường dùng Haiku, nhưng dịch tài liệu pháp lý phức tạp thì cần Sonnet hoặc Opus. Domain routing cho bạn baseline, complexity scoring điều chỉnh lên hoặc xuống từ đó.\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003eclass SmartRouter:\n    \"\"\"Ket hop domain routing va complexity scoring.\"\"\"\n\n    def __init__(self):\n        self.domain_router = DomainRouter()\n        self.complexity_scorer = ComplexityScorer()\n        self.client = anthropic.Anthropic()\n\n    def route(self, message: str) -\u0026gt; dict:\n        # Buoc 1: Xac dinh domain va model mac dinh\n        domain = self.domain_router.detect_domain(message)\n        base_model, _ = DomainRouter.DOMAIN_MODELS[domain]\n\n        # Buoc 2: Tinh complexity score\n        score = self.complexity_scorer.score(message)\n\n        # Buoc 3: Dieu chinh model dua tren complexity\n        models_tier = [\n            \"claude-haiku-4-20250514\",\n            \"claude-sonnet-4-20250514\",\n            \"claude-opus-4-20250514\",\n        ]\n        base_tier = models_tier.index(base_model)\n\n        # Neu complexity cao, tang len 1 tier\n        if score \u0026gt; 0.7 and base_tier \u0026lt; 2:\n            final_model = models_tier[base_tier + 1]\n            reason = f\"Upgraded tu {base_model} do complexity={score}\"\n        # Neu complexity thap, giam xuong 1 tier\n        elif score \u0026lt; 0.2 and base_tier \u0026gt; 0:\n            final_model = models_tier[base_tier - 1]\n            reason = f\"Downgraded tu {base_model} do complexity={score}\"\n        else:\n            final_model = base_model\n            reason = f\"Giu nguyen {base_model}\"\n\n        response = self.client.messages.create(\n            model=final_model,\n            max_tokens=4096,\n            messages=[{\"role\": \"user\", \"content\": message}]\n        )\n\n        return {\n            \"domain\": domain,\n            \"complexity_score\": score,\n            \"model\": final_model,\n            \"reason\": reason,\n            \"response\": response.content[0].text,\n        }\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eRouting với Streaming\u003c\/h2\u003e\n\u003cp\u003eKhi dùng streaming, bạn không thể đợi kết quả hoàn chỉnh để đánh giá chất lượng. Thay vào đó, bạn route trước và stream kết quả:\u003c\/p\u003e\n\n\u003cpre\u003e\u003ccode\u003edef route_and_stream(message: str):\n    \"\"\"Route va stream response.\"\"\"\n    router = HybridRouter()\n\n    # Classification van nhu cu\n    score = router.scorer.score(message)\n    if score \u0026lt; 0.15:\n        model = \"claude-haiku-4-20250514\"\n    elif score \u0026gt; 0.85:\n        model = \"claude-opus-4-20250514\"\n    else:\n        decision = router.llm_router.classify(message)\n        model = decision.model\n\n    # Stream response\n    client = anthropic.Anthropic()\n    with client.messages.stream(\n        model=model,\n        max_tokens=4096,\n        messages=[{\"role\": \"user\", \"content\": message}]\n    ) as stream:\n        print(f\"[Streaming from {model}]\")\n        for text in stream.text_stream:\n            print(text, end=\"\", flush=True)\n        print()  # Newline cuoi\n\n    # Lay usage info sau khi stream xong\n    final_message = stream.get_final_message()\n    print(f\"Tokens: {final_message.usage.input_tokens} in, \"\n          f\"{final_message.usage.output_tokens} out\")\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch2\u003eProduction Considerations\u003c\/h2\u003e\n\u003cp\u003eKhi triển khai routing trong production, cần chú ý:\u003c\/p\u003e\n\n\u003ch3\u003eLatency budget\u003c\/h3\u003e\n\u003cp\u003eClassification bằng Haiku mất thêm 200-500ms. Nếu ứng dụng cần response dưới 1 giây, dùng rule-based scoring (miễn phí) thay vì LLM classification.\u003c\/p\u003e\n\n\u003ch3\u003eCaching classification\u003c\/h3\u003e\n\u003cpre\u003e\u003ccode\u003e# Cache ket qua classification cho cac message tuong tu\nimport hashlib\n\nclass CachedRouter(ModelRouter):\n    def __init__(self):\n        super().__init__()\n        self.cache = {}\n\n    def classify(self, message: str) -\u0026gt; RouteDecision:\n        # Tao cache key tu 200 ky tu dau cua message\n        key = hashlib.md5(message[:200].encode()).hexdigest()\n\n        if key in self.cache:\n            return self.cache[key]\n\n        decision = super().classify(message)\n        self.cache[key] = decision\n        return decision\u003c\/code\u003e\u003c\/pre\u003e\n\n\u003ch3\u003eMonitoring và alerts\u003c\/h3\u003e\n\u003cul\u003e\n  \u003cli\u003eTheo dõi tỷ lệ routing: nếu Opus tăng đột ngột, có thể classifier bị lỗi\u003c\/li\u003e\n  \u003cli\u003eTheo dõi fallback rate: nếu quá cao, routing strategy cần điều chỉnh\u003c\/li\u003e\n  \u003cli\u003eTheo dõi chi phí hàng ngày và so sánh với baseline\u003c\/li\u003e\n\u003c\/ul\u003e\n\n\u003ch2\u003eTóm tắt\u003c\/h2\u003e\n\u003cp\u003eSmart Model Routing là một trong những kỹ thuật quan trọng nhất để tối ưu chi phí khi sử dụng Claude API trong production. Các điểm chính:\u003c\/p\u003e\n\u003cul\u003e\n  \u003cli\u003eDùng Haiku phân loại, route đến model phù hợp — tiết kiệm 60-80% chi phí\u003c\/li\u003e\n  \u003cli\u003eKết hợp rule-based scoring và LLM classification cho độ chính xác cao\u003c\/li\u003e\n  \u003cli\u003eXây dựng fallback chains để đảm bảo chất lượng output\u003c\/li\u003e\n  \u003cli\u003eA\/B test để tìm routing strategy tối ưu cho từng loại task\u003c\/li\u003e\n  \u003cli\u003eĐo lường chi phí thực tế để chứng minh ROI của hệ thống routing\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003cp\u003eTìm hiểu thêm về các kỹ thuật API nâng cao tại \u003ca href=\"\/collections\/nang-cao\"\u003eThư viện Nâng cao\u003c\/a\u003e.\u003c\/p\u003e\n","brand":"Minh Tuấn","offers":[{"title":"Default Title","offer_id":47730150605012,"sku":null,"price":0.0,"currency_code":"VND","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0821\/0264\/9044\/files\/smart-model-routing-tu-dong-chon-haiku-sonnet-opus-theo-task.jpg?v=1774715519","url":"https:\/\/claude.vn\/products\/smart-model-routing-tu-dong-chon-haiku-sonnet-opus-theo-task","provider":"CLAUDE.VN","version":"1.0","type":"link"}