Tháng trước, một startup ở Việt Nam gặp sự cố nghiêm trọng: Claude API timeout liên tục trong 3 giờ đồng thời Gemini trả về lỗi 5xx. Kết quả? 200K requests thất bại, 50K USD chi phí đội lên do retry vô tội, và quan trọng hơn — khách hàng chuyển sang đối thủ.

Bài viết này là kinh nghiệm thực chiến của tôi khi thiết kế SLO cho multi-provider AI gateway tại HolySheep AI. Tôi sẽ chia sẻ cách định lượng từng loại lỗi, xây dựng alerting thông minh, và tối ưu chi phí 85%+ so với việc dùng API gốc.

Bảng giá AI Provider 2026 — Dữ liệu đã xác minh

Trước khi đi vào SLO, hãy xem chi phí thực tế khi dùng API gốc vs HolySheep AI:

Model Giá gốc (USD/MTok) Giá HolySheep (USD/MTok) Tiết kiệm
GPT-4.1 $8.00 $1.20 (¥1≈$1) 85%
Claude Sonnet 4.5 $15.00 $2.25 (¥1≈$1) 85%
Gemini 2.5 Flash $2.50 $0.38 (¥1≈$1) 85%
DeepSeek V3.2 $0.42 $0.06 (¥1≈$1) 85%

So sánh chi phí cho 10 triệu token/tháng

Model Chi phí gốc/tháng Chi phí HolySheep/tháng Chênh lệch
GPT-4.1 (10M output) $80,000 $12,000 Tiết kiệm $68,000
Claude Sonnet 4.5 (10M output) $150,000 $22,500 Tiết kiệm $127,500
Gemini 2.5 Flash (10M output) $25,000 $3,750 Tiết kiệm $21,250
DeepSeek V3.2 (10M output) $4,200 $630 Tiết kiệm $3,570

Với mức tiết kiệm 85% này, budget dự phòng cho retry và failover trở nên dễ kiểm soát hơn nhiều.

Tại sao SLO cho AI Provider lại quan trọng?

Traditional SLO cho web service đã quen thuộc: uptime 99.9%, p99 latency < 200ms. Nhưng AI provider có 3 đặc thù riêng:

HolySheep SLO Architecture — Thực chiến

Tại HolySheep AI, tôi thiết kế hệ thống theo 3 layers:

┌─────────────────────────────────────────────────────────────┐
│                    SLO Dashboard Layer                       │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │ OpenAI   │  │ Claude   │  │ Gemini   │  │DeepSeek  │    │
│  │ SLO: 99% │  │ SLO: 98% │  │ SLO: 97% │  │ SLO: 99.5%│   │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘    │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                   Retry & Fallback Layer                     │
│  • Circuit Breaker per provider                             │
│  • Smart retry với jitter có kiểm soát                      │
│  • Automatic failover khi SLO breach                       │
└─────────────────────────────────────────────────────────────┘
                              │
┌─────────────────────────────────────────────────────────────┐
│                    Cost Guard Layer                          │
│  • Per-request budget tracking                               │
│  • Automatic throttling khi vượt ngưỡng                     │
│  • Alert trước khi billing spike                            │
└─────────────────────────────────────────────────────────────┘

Triển khai SLO với HolySheep API

Dưới đây là code production-ready để implement SLO tracking. Base URL luôn là https://api.holysheep.ai/v1.

const https = require('https');

class AIServiceSLO {
  constructor(apiKey) {
    this.baseUrl = 'https://api.holysheep.ai/v1';
    this.apiKey = apiKey;
    
    // SLO targets - có thể tùy chỉnh theo business requirement
    this.sloTargets = {
      openai: { availability: 0.99, latencyP99: 5000, errorBudget: 0.01 },
      claude: { availability: 0.98, latencyP99: 8000, errorBudget: 0.02 },
      gemini: { availability: 0.97, latencyP99: 3000, errorBudget: 0.03 },
      deepseek: { availability: 0.995, latencyP99: 2000, errorBudget: 0.005 }
    };
    
    // Circuit breaker state
    this.circuitState = {
      openai: { failures: 0, lastFailure: null, state: 'CLOSED' },
      claude: { failures: 0, lastFailure: null, state: 'CLOSED' },
      gemini: { failures: 0, lastFailure: null, state: 'CLOSED' },
      deepseek: { failures: 0, lastFailure: null, state: 'CLOSED' }
    };
    
    // Metrics tracking
    this.metrics = {
      requests: { total: 0, success: 0, failed: 0, byProvider: {} },
      latency: { buckets: {}, p99: null },
      errors: { byCode: {}, byProvider: {} }
    };
  }

  // Khởi tạo metrics cho provider mới
  initProviderMetrics(provider) {
    if (!this.metrics.requests.byProvider[provider]) {
      this.metrics.requests.byProvider[provider] = { total: 0, success: 0, failed: 0 };
    }
    if (!this.metrics.errors.byProvider[provider]) {
      this.metrics.errors.byProvider[provider] = {};
    }
  }

  // Kiểm tra circuit breaker
  isCircuitOpen(provider) {
    const circuit = this.circuitState[provider];
    if (circuit.state === 'OPEN') {
      // Check if timeout đã hết (5 phút)
      if (Date.now() - circuit.lastFailure > 300000) {
        circuit.state = 'HALF_OPEN';
        console.log([CircuitBreaker] ${provider}: OPEN → HALF_OPEN);
      } else {
        return true;
      }
    }
    return false;
  }

  // Cập nhật circuit breaker
  updateCircuitBreaker(provider, success) {
    const circuit = this.circuitState[provider];
    
    if (success) {
      circuit.failures = 0;
      if (circuit.state === 'HALF_OPEN') {
        circuit.state = 'CLOSED';
        console.log([CircuitBreaker] ${provider}: HALF_OPEN → CLOSED);
      }
    } else {
      circuit.failures++;
      circuit.lastFailure = Date.now();
      
      // Mở circuit sau 5 failures liên tiếp
      if (circuit.failures >= 5 && circuit.state === 'CLOSED') {
        circuit.state = 'OPEN';
        console.log([CircuitBreaker] ${provider}: CLOSED → OPEN (${circuit.failures} failures));
      }
    }
  }

  // Gọi API với SLO tracking
  async chatCompletion(model, messages, options = {}) {
    const provider = this.detectProvider(model);
    const startTime = Date.now();
    
    this.initProviderMetrics(provider);
    this.metrics.requests.total++;
    this.metrics.requests.byProvider[provider].total++;

    // Check circuit breaker
    if (this.isCircuitOpen(provider)) {
      console.warn([SLO] Circuit open for ${provider}, attempting fallback...);
      return this.handleFallback(provider, model, messages);
    }

    try {
      const response = await this.makeRequest(model, messages, options);
      const latency = Date.now() - startTime;
      
      // Track metrics
      this.metrics.requests.success++;
      this.metrics.requests.byProvider[provider].success++;
      this.trackLatency(provider, latency);
      this.updateCircuitBreaker(provider, true);
      
      // Check SLO compliance
      this.checkSLOCompliance(provider, latency);
      
      return { success: true, data: response, latency, provider };
      
    } catch (error) {
      const latency = Date.now() - startTime;
      
      this.metrics.requests.failed++;
      this.metrics.requests.byProvider[provider].failed++;
      this.trackError(provider, error.code || 'UNKNOWN', error);
      this.updateCircuitBreaker(provider, false);
      
      console.error([SLO] ${provider} failed: ${error.message} (${latency}ms));
      
      // Attempt fallback
      return this.handleFallback(provider, model, messages, error);
    }
  }

  // Detect provider từ model name
  detectProvider(model) {
    if (model.includes('gpt') || model.includes('o1') || model.includes('o3')) {
      return 'openai';
    } else if (model.includes('claude') || model.includes('sonnet') || model.includes('opus')) {
      return 'claude';
    } else if (model.includes('gemini') || model.includes('flash')) {
      return 'gemini';
    } else if (model.includes('deepseek')) {
      return 'deepseek';
    }
    return 'openai'; // default
  }

  // Make HTTP request
  makeRequest(model, messages, options) {
    return new Promise((resolve, reject) => {
      const postData = JSON.stringify({
        model: model,
        messages: messages,
        ...options
      });

      const url = new URL(${this.baseUrl}/chat/completions);
      const reqOptions = {
        hostname: url.hostname,
        path: url.pathname,
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': Bearer ${this.apiKey},
          'Content-Length': Buffer.byteLength(postData)
        }
      };

      const req = https.request(reqOptions, (res) => {
        let data = '';
        res.on('data', chunk => data += chunk);
        res.on('end', () => {
          if (res.statusCode >= 200 && res.statusCode < 300) {
            resolve(JSON.parse(data));
          } else {
            const error = new Error(HTTP ${res.statusCode});
            error.code = res.statusCode;
            error.response = data;
            reject(error);
          }
        });
      });

      req.on('error', reject);
      req.setTimeout(30000, () => {
        req.destroy();
        reject(new Error('Request timeout'));
      });

      req.write(postData);
      req.end();
    });
  }

  // Handle fallback khi provider chính fail
  async handleFallback(primaryProvider, model, messages, error) {
    console.log([Fallback] Attempting failover from ${primaryProvider}...);
    
    // Priority fallback order
    const fallbackOrder = {
      openai: ['deepseek', 'gemini', 'claude'],
      claude: ['deepseek', 'gemini', 'openai'],
      gemini: ['deepseek', 'openai', 'claude'],
      deepseek: ['openai', 'gemini', 'claude']
    };

    const fallbacks = fallbackOrder[primaryProvider] || [];
    
    for (const fallbackProvider of fallbacks) {
      if (this.isCircuitOpen(fallbackProvider)) continue;
      
      try {
        const mappedModel = this.mapModel(fallbackProvider, model);
        console.log([Fallback] Trying ${fallbackProvider} with model ${mappedModel});
        
        const response = await this.makeRequest(mappedModel, messages, {});
        console.log([Fallback] Success with ${fallbackProvider});
        
        // Track fallback metrics
        this.metrics.requests.byProvider[fallbackProvider].total++;
        this.metrics.requests.byProvider[fallbackProvider].success++;
        
        return { 
          success: true, 
          data: response, 
          provider: fallbackProvider,
          fallback: true,
          originalError: error?.message
        };
      } catch (e) {
        console.warn([Fallback] ${fallbackProvider} also failed: ${e.message});
        this.updateCircuitBreaker(fallbackProvider, false);
      }
    }

    // Tất cả fallback đều fail
    return {
      success: false,
      error: 'All providers unavailable',
      originalError: error?.message,
      metrics: this.getCurrentMetrics()
    };
  }

  // Map model name giữa các providers
  mapModel(targetProvider, originalModel) {
    const modelMap = {
      'gpt-4': { deepseek: 'deepseek-chat', gemini: 'gemini-2.0-flash', claude: 'claude-sonnet-4-20250514' },
      'gpt-4o': { deepseek: 'deepseek-chat', gemini: 'gemini-2.0-flash', claude: 'claude-sonnet-4-20250514' },
      'claude-sonnet-4-20250514': { deepseek: 'deepseek-chat', openai: 'gpt-4o', gemini: 'gemini-2.0-flash' },
      'gemini-2.0-flash': { deepseek: 'deepseek-chat', openai: 'gpt-4o', claude: 'claude-sonnet-4-20250514' }
    };
    
    return modelMap[originalModel]?.[targetProvider] || originalModel;
  }

  // Track latency
  trackLatency(provider, latency) {
    if (!this.metrics.latency.buckets[provider]) {
      this.metrics.latency.buckets[provider] = [];
    }
    this.metrics.latency.buckets[provider].push(latency);
    
    // Keep last 1000 samples
    if (this.metrics.latency.buckets[provider].length > 1000) {
      this.metrics.latency.buckets[provider].shift();
    }
  }

  // Track errors
  trackError(provider, code, error) {
    if (!this.metrics.errors.byCode[code]) {
      this.metrics.errors.byCode[code] = 0;
    }
    this.metrics.errors.byCode[code]++;
    
    if (!this.metrics.errors.byProvider[provider][code]) {
      this.metrics.errors.byProvider[provider][code] = 0;
    }
    this.metrics.errors.byProvider[provider][code]++;
  }

  // Check SLO compliance
  checkSLOCompliance(provider, latency) {
    const target = this.sloTargets[provider];
    const metrics = this.metrics.requests.byProvider[provider];
    
    const availability = metrics.total > 0 
      ? metrics.success / metrics.total 
      : 1;
    
    // Calculate p99 latency
    const latencies = this.metrics.latency.buckets[provider] || [];
    const sorted = [...latencies].sort((a, b) => a - b);
    const p99Index = Math.floor(sorted.length * 0.99);
    const p99Latency = sorted[p99Index] || 0;
    
    // Check violations
    if (availability < target.availability) {
      console.error([SLO ALERT] ${provider} availability ${(availability * 100).toFixed(2)}% below target ${(target.availability * 100)}%);
    }
    
    if (p99Latency > target.latencyP99) {
      console.error([SLO ALERT] ${provider} p99 latency ${p99Latency}ms exceeds target ${target.latencyP99}ms);
    }
    
    return { availability, p99Latency, compliant: availability >= target.availability && p99Latency <= target.latencyP99 };
  }

  // Get current metrics
  getCurrentMetrics() {
    const result = {};
    
    for (const provider of Object.keys(this.metrics.requests.byProvider)) {
      const m = this.metrics.requests.byProvider[provider];
      const latencies = this.metrics.latency.buckets[provider] || [];
      const sorted = [...latencies].sort((a, b) => a - b);
      const p99Index = Math.floor(sorted.length * 0.99);
      
      result[provider] = {
        requests: m,
        availability: m.total > 0 ? (m.success / m.total * 100).toFixed(2) + '%' : 'N/A',
        p99Latency: sorted[p99Index] || 0,
        circuitState: this.circuitState[provider].state,
        sloCompliance: this.checkSLOCompliance(provider, 0)
      };
    }
    
    return result;
  }

  // Alert khi error budget sắp hết
  checkErrorBudget() {
    for (const provider of Object.keys(this.sloTargets)) {
      const target = this.sloTargets[provider];
      const metrics = this.metrics.requests.byProvider[provider];
      
      if (metrics.total === 0) continue;
      
      const errorRate = metrics.failed / metrics.total;
      const remainingBudget = target.errorBudget - errorRate;
      
      // Alert khi còn dưới 30% error budget
      if (remainingBudget < target.errorBudget * 0.3) {
        console.error([SLO CRITICAL] ${provider} error budget running low: ${(remainingBudget * 100).toFixed(2)}% remaining);
      }
    }
  }
}

// Usage example
const slo = new AIServiceSLO('YOUR_HOLYSHEEP_API_KEY');

// Monitor periodically
setInterval(() => {
  console.log('\n=== SLO Metrics ===');
  console.log(JSON.stringify(slo.getCurrentMetrics(), null, 2));
  slo.checkErrorBudget();
}, 60000);

// Example request
async function main() {
  const result = await slo.chatCompletion('gpt-4o', [
    { role: 'user', content: 'Explain SLO design for AI providers' }
  ]);
  
  console.log('Result:', result);
}

main().catch(console.error);

Error Classification & Response Strategy

Mỗi loại error có chiến lược xử lý khác nhau. Đây là bảng tổng hợp từ kinh nghiệm thực chiến:

Error Type Provider HTTP Code Retry? Exponential Backoff Cost Impact
Rate Limit OpenAI 429 ✅ Yes 1s, 2s, 4s, 8s (max 4 retries) Retry requests không mất phí
Timeout Claude 500/529 ⚠️ Safe với idempotency key 5s, 10s, 20s (max 3 retries) Có thể bị charge 2x
Server Error Gemini 5xx ✅ Yes 2s, 4s, 8s (max 3 retries) Retry miễn phí nếu chưa consume
Context Overflow Tất cả 400 ❌ No N/A Cắt context + retry
Auth Error Tất cả 401 ❌ No N/A Kiểm tra API key

Cost Guard Implementation

Một trong những vấn đề nghiêm trọng nhất: retry không kiểm soát có thể đẩy chi phí lên 10x. Dưới đây là implementation cho cost guard:

class CostGuard {
  constructor(options = {}) {
    this.monthlyBudget = options.monthlyBudget || 10000; // USD
    this.alertThreshold = options.alertThreshold || 0.8; // 80%
    this.dailyLimit = options.dailyLimit || 1000; // USD/day
    
    this.currentSpend = 0;
    this.dailySpend = 0;
    this.requestCount = 0;
    this.startDate = new Date();
    this.lastResetDate = new Date();
    
    // Cost per token by model (from HolySheep pricing)
    this.costPerToken = {
      'gpt-4o': 0.00000120,      // $1.20/MTok
      'gpt-4': 0.00000120,
      'claude-sonnet-4-20250514': 0.00000225, // $2.25/MTok
      'gemini-2.0-flash': 0.00000038, // $0.38/MTok
      'deepseek-chat': 0.00000006,   // $0.06/MTok
    };
    
    this.alerts = [];
  }

  // Calculate cost for a request
  calculateCost(model, inputTokens, outputTokens) {
    const rate = this.costPerToken[model] || 0.000001;
    // Input + Output tokens
    return (inputTokens + outputTokens) * rate;
  }

  // Check if request is allowed
  canProceed(model, estimatedTokens) {
    const estimatedCost = estimatedTokens * (this.costPerToken[model] || 0.000001);
    
    // Check daily limit
    if (this.dailySpend + estimatedCost > this.dailyLimit) {
      this.alert('DAILY_LIMIT', Daily spend ${this.dailySpend} USD approaching limit ${this.dailyLimit} USD);
      return { allowed: false, reason: 'Daily limit exceeded' };
    }
    
    // Check monthly budget
    if (this.currentSpend + estimatedCost > this.monthlyBudget) {
      this.alert('MONTHLY_BUDGET', Monthly budget nearly exhausted);
      return { allowed: false, reason: 'Monthly budget exceeded' };
    }
    
    return { allowed: true, estimatedCost };
  }

  // Record actual spend after request
  recordSpend(model, inputTokens, outputTokens, actualCost) {
    this.currentSpend += actualCost;
    this.dailySpend += actualCost;
    this.requestCount++;
    
    // Check alert thresholds
    const monthlyPct = this.currentSpend / this.monthlyBudget;
    if (monthlyPct >= this.alertThreshold) {
      this.alert('SPEND_THRESHOLD', Monthly spend at ${(monthlyPct * 100).toFixed(1)}% of budget);
    }
    
    // Daily reset check
    const now = new Date();
    if (now.toDateString() !== this.lastResetDate.toDateString()) {
      this.dailySpend = 0;
      this.lastResetDate = now;
      console.log('[CostGuard] Daily spend reset');
    }
    
    return {
      totalSpend: this.currentSpend,
      dailySpend: this.dailySpend,
      remainingBudget: this.monthlyBudget - this.currentSpend,
      requestCount: this.requestCount
    };
  }

  // Alert system
  alert(type, message) {
    const alert = { type, message, timestamp: Date.now() };
    this.alerts.push(alert);
    
    // Keep last 100 alerts
    if (this.alerts.length > 100) {
      this.alerts.shift();
    }
    
    console.error([COST ALERT] ${type}: ${message});
    
    // Could integrate with Slack, PagerDuty, etc.
    if (process.env.SLACK_WEBHOOK) {
      this.sendSlackAlert(alert);
    }
  }

  async sendSlackAlert(alert) {
    // Implementation for Slack integration
    const payload = {
      text: *Cost Guard Alert*\n*Type:* ${alert.type}\n*Message:* ${alert.message}\n*Time:* ${new Date(alert.timestamp).toISOString()}
    };
    
    // POST to Slack webhook
  }

  // Get current status
  getStatus() {
    return {
      monthlyBudget: this.monthlyBudget,
      currentSpend: this.currentSpend,
      remainingBudget: this.monthlyBudget - this.currentSpend,
      spendPercentage: (this.currentSpend / this.monthlyBudget * 100).toFixed(2) + '%',
      dailySpend: this.dailySpend,
      dailyLimit: this.dailyLimit,
      requestCount: this.requestCount,
      avgCostPerRequest: this.requestCount > 0 
        ? (this.currentSpend / this.requestCount).toFixed(6) 
        : 0,
      alerts: this.alerts.slice(-10) // Last 10 alerts
    };
  }

  // Auto-throttle when budget is low
  shouldThrottle() {
    const remainingPct = (this.monthlyBudget - this.currentSpend) / this.monthlyBudget;
    
    if (remainingPct < 0.1) {
      return { throttle: true, factor: 0.5, reason: 'Critical budget (10% remaining)' };
    } else if (remainingPct < 0.25) {
      return { throttle: true, factor: 0.75, reason: 'Low budget (25% remaining)' };
    }
    
    return { throttle: false };
  }
}

// Integrated SLO + Cost Guard usage
class HolySheepAIGateway {
  constructor(apiKey, options = {}) {
    this.slo = new AIServiceSLO(apiKey);
    this.costGuard = new CostGuard({
      monthlyBudget: options.monthlyBudget || 10000,
      alertThreshold: options.alertThreshold || 0.8
    });
  }

  async chat(model, messages, options = {}) {
    // Estimate tokens (rough calculation: ~4 chars per token)
    const estimatedInputTokens = JSON.stringify(messages).length / 4;
    const estimatedOutputTokens = (options.maxTokens || 1000);
    const estimatedTotal = estimatedInputTokens + estimatedOutputTokens;
    
    // Cost check
    const costCheck = this.costGuard.canProceed(model, estimatedTotal);
    if (!costCheck.allowed) {
      console.warn([Gateway] Request blocked: ${costCheck.reason});
      return { 
        success: false, 
        error: 'Budget exceeded',
        canRetry: false,
        status: this.costGuard.getStatus()
      };
    }
    
    // Execute request
    const result = await this.slo.chatCompletion(model, messages, options);
    
    if (result.success) {
      // Record actual cost
      const actualCost = this.costGuard.calculateCost(
        model,
        result.data.usage?.input_tokens || estimatedInputTokens,
        result.data.usage?.output_tokens || estimatedOutputTokens
      );
      
      this.costGuard.recordSpend(
        model,
        result.data.usage?.input_tokens || estimatedInputTokens,
        result.data.usage?.output_tokens || estimatedOutputTokens,
        actualCost
      );
    }
    
    // Check if should throttle
    const throttle = this.costGuard.shouldThrottle();
    if (throttle.throttle) {
      console.warn([Gateway] Throttling active: ${throttle.reason});
    }
    
    return {
      ...result,
      costStatus: this.costGuard.getStatus(),
      throttled: throttle.throttle
    };
  }

  // Get combined dashboard
  getDashboard() {
    return {
      slo: this.slo.getCurrentMetrics(),
      cost: this.costGuard.getStatus(),
      throttle: this.costGuard.shouldThrottle()
    };
  }
}

// Usage
const gateway = new HolySheepAIGateway('YOUR_HOLYSHEEP_API_KEY', {
  monthlyBudget: 5000,
  alertThreshold: 0.75
});

// Example request
gateway.chat('gpt-4o', [
  { role: 'user', content: 'Write a detailed technical document about SLO design' }
]).then(result => {
  console.log(JSON.stringify(result, null, 2));
});

// Monitor dashboard every minute
setInterval(() => {
  console.log('\n=== Gateway Dashboard ===');
  console.log(JSON.stringify(gateway.getDashboard(), null, 2));
}, 60000);

Lỗi thường gặp và cách khắc phục

1. Lỗi OpenAI 429 — Rate Limit

Mô tả: Request bị từ chối do vượt quota hoặc rate limit. Đây là lỗi phổ biến nhất khi scale up đột ngột.

// ❌ SAI: Retry không có backoff = cascade failure
async function badRetry() {
  while (true) {
    try {
      return await makeRequest();
    } catch (e) {
      if (e.status === 429) {
        await sleep(100); // Retry ngay = chết tiếp
      }
    }
  }
}

// ✅ ĐÚNG: Exponential backoff với jitter
async function smartRetry(requestFn, maxRetries = 4) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await requestFn();
    } catch (error) {
      if (error.status === 429 && attempt < maxRetries) {
        // Exponential backoff: 1s, 2s, 4s, 8s
        const baseDelay = Math.pow(2, attempt) * 1000;
        // Jitter để tránh thundering herd
        const jitter = Math.random() * 1000;
        const delay = baseDelay + jitter;
        
        console.log([Retry] Attempt ${attempt + 1}/${maxRetries}, waiting ${delay}ms);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else if (error.status === 429) {
        throw new Error('Rate limit exceeded after max retries');
      } else {
        throw error; // Non-retryable error
      }
    }
  }
}

2. Lỗi Claude Timeout — 500/529

Mô tả: Claude response quá chậm hoặc server overloaded. Đặc biệt nguy hiểm với streaming.

// ❌ SAI: Streaming + retry = duplicate content disaster
async function badStreamingRetry() {
  const stream = await makeStreamingRequest();
  for await (const chunk of stream) {
    // Nếu timeout ở đây, retry = duplicate content
    process.stdout.write(chunk);
  }
}

// ✅ ĐÚNG: Timeout có kiểm soát + idempotency
async function smartStreamingWithTimeout(requestFn, timeoutMs = 30000) {
  let