Ba tháng trước, tôi nhận được cuộc gọi lúc 2 giờ sáng từ đội vận hành. Hệ thống chăm sóc khách hàng AI của một marketplace thương mại điện tử quy mô 5 triệu người dùng bị sập hoàn toàn trong đợt flash sale Black Friday. Nguyên nhân? Function calling không xử lý được timeout — 200 request đồng thời đều timeout sau 30 giây, tạo ra một cụm lỗi domino khiến toàn bộ conversation engine không phản hồi được.

Kể từ đó, tôi đã xây dựng và tinh chỉnh một hệ thống retry logic hoàn chỉnh cho function calling. Bài viết này sẽ chia sẻ toàn bộ chiến thuật, kèm code production-ready mà bạn có thể sao chép và chạy ngay hôm nay.

Tại Sao Function Calling Timeout Cần Retry Logic Đặc Biệt?

Function calling khác với API call thông thường ở chỗ nó có stateful context. Khi một function call timeout:

Kiến Trúc Retry Logic Cho Function Calling

Kiến trúc tôi đề xuất gồm 4 tầng:

Triển Khai Chi Tiết

1. Cấu Hình Client Với Timeout Thông Minh

Đầu tiên, cấu hình client SDK với timeout dynamic dựa trên loại function. Function đơn giản như tra cứu database có thể timeout 10 giây, trong khi function phức tạp như RAG retrieval nên đặt 60 giây.

import openai
import asyncio
from typing import Callable, Any, Optional
from dataclasses import dataclass
from datetime import datetime, timedelta
import hashlib
import json

@dataclass
class FunctionConfig:
    """Cấu hình cho từng loại function"""
    name: str
    timeout_seconds: int = 30
    max_retries: int = 3
    base_delay: float = 1.0
    max_delay: float = 30.0

Định nghĩa function configs

FUNCTION_CONFIGS = { "get_product_info": FunctionConfig( name="get_product_info", timeout_seconds=10, max_retries=3 ), "search_inventory": FunctionConfig( name="search_inventory", timeout_seconds=15, max_retries=4 ), "rag_retrieve": FunctionConfig( name="rag_retrieve", timeout_seconds=60, max_retries=2, base_delay=2.0 ), "process_payment": FunctionConfig( name="process_payment", timeout_seconds=45, max_retries=1, # Payment không nên retry nhiều max_delay=5.0 ), } class HolySheepClient: """Client với timeout handling và retry logic hoàn chỉnh""" def __init__(self, api_key: str): self.client = openai.OpenAI( api_key=api_key, base_url="https://api.holysheep.ai/v1", # LUÔN LUÔN dùng HolySheep timeout=30.0, max_retries=0 # Chúng ta tự implement retry logic ) self._idempotency_store = {} def _generate_idempotency_key( self, messages: list, function_name: str ) -> str: """Tạo idempotency key duy nhất cho request""" content = json.dumps({ "messages": messages, "function": function_name, "timestamp": datetime.utcnow().isoformat()[:16] # Precision: phút }, sort_keys=True) return hashlib.sha256(content.encode()).hexdigest()[:16] async def call_with_retry( self, messages: list, functions: list, function_name: str, context_id: Optional[str] = None ) -> dict: """ Gọi function với retry logic hoàn chỉnh """ config = FUNCTION_CONFIGS.get( function_name, FunctionConfig(name=function_name) ) # Kiểm tra idempotency - tránh duplicate execution idempotency_key = self._generate_idempotency_key( messages, function_name ) if idempotency_key in self._idempotency_store: stored_result = self._idempotency_store[idempotency_key] # Đánh dấu đây là retry request stored_result["is_retry"] = True return stored_result last_error = None attempt = 0 while attempt < config.max_retries: try: response = await asyncio.wait_for( self._execute_function_call( messages=messages, functions=functions, function_name=function_name ), timeout=config.timeout_seconds ) # Lưu kết quả để có thể trả lại cho retry request self._idempotency_store[idempotency_key] = response response["attempt_count"] = attempt + 1 response["idempotency_key"] = idempotency_key return response except asyncio.TimeoutError as e: last_error = e attempt += 1 if attempt < config.max_retries: # Exponential backoff với jitter delay = min( config.base_delay * (2 ** (attempt - 1)), config.max_delay ) # Thêm jitter ±25% để tránh thundering herd import random jitter = delay * 0.25 * (2 * random.random() - 1) actual_delay = delay + jitter print(f"[RETRY] Function {function_name} timeout, " f"attempt {attempt}/{config.max_retries}, " f"waiting {actual_delay:.2f}s") await asyncio.sleep(actual_delay) except Exception as e: last_error = e # Non-timeout error cũng nên retry với backoff nhẹ if attempt < config.max_retries - 1: await asyncio.sleep(config.base_delay * (attempt + 1)) attempt += 1 else: break # Tất cả retries đều thất bại raise FunctionCallError( f"Function {function_name} failed after {config.max_retries} retries. " f"Last error: {last_error}", function_name=function_name, attempts=config.max_retries, last_error=last_error ) async def _execute_function_call( self, messages: list, functions: list, function_name: str ) -> dict: """Thực thi function call thực tế""" response = self.client.chat.completions.create( model="gpt-4.1", messages=messages, tools=[ { "type": "function", "function": { "name": f["name"], "description": f["description"], "parameters": f["parameters"] } } for f in functions ], tool_choice={"type": "function", "function": {"name": function_name}} ) return { "status": "success", "response": response, "function_called": function_name } class FunctionCallError(Exception): """Custom exception cho function call failures""" def __init__(self, message, function_name, attempts, last_error): super().__init__(message) self.function_name = function_name self.attempts = attempts self.last_error = last_error

2. Circuit Breaker Cho Function Calling

Khi một function cụ thể liên tục timeout, không có nghĩa là toàn bộ hệ thống có vấn đề. Circuit breaker giúp cô lập các function có vấn đề và cho phép hệ thống tiếp tục hoạt động với các function khác.

import time
from enum import Enum
from typing import Dict, Callable
from collections import defaultdict

class CircuitState(Enum):
    CLOSED = "closed"      # Hoạt động bình thường
    OPEN = "open"          # Ngăn chặn requests
    HALF_OPEN = "half_open"  # Thử nghiệm recovery

class CircuitBreaker:
    """
    Circuit Breaker cho function calling
    Theo pattern của Michael Nygard
    """
    
    def __init__(
        self,
        failure_threshold: int = 5,
        recovery_timeout: int = 60,
        success_threshold: int = 2,
        half_open_max_calls: int = 3
    ):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.success_threshold = success_threshold
        self.half_open_max_calls = half_open_max_calls
        
        self._states: Dict[str, CircuitState] = defaultdict(
            lambda: CircuitState.CLOSED
        )
        self._failure_counts: Dict[str, int] = defaultdict(int)
        self._success_counts: Dict[str, int] = defaultdict(int)
        self._last_failure_time: Dict[str, float] = {}
        self._half_open_calls: Dict[str, int] = defaultdict(int)
    
    def _get_state(self, function_name: str) -> CircuitState:
        state = self._states[function_name]
        
        if state == CircuitState.OPEN:
            # Kiểm tra timeout để chuyển sang HALF_OPEN
            if time.time() - self._last_failure_time.get(function_name, 0) > self.recovery_timeout:
                self._states[function_name] = CircuitState.HALF_OPEN
                self._half_open_calls[function_name] = 0
                return CircuitState.HALF_OPEN
        
        return state
    
    def _record_success(self, function_name: str):
        """Ghi nhận thành công"""
        if self._states[function_name] == CircuitState.HALF_OPEN:
            self._success_counts[function_name] += 1
            
            if self._success_counts[function_name] >= self.success_threshold:
                # Recovery thành công
                self._states[function_name] = CircuitState.CLOSED
                self._failure_counts[function_name] = 0
                self._success_counts[function_name] = 0
        elif self._states[function_name] == CircuitState.CLOSED:
            # Reset failure count khi có success liên tiếp
            self._failure_counts[function_name] = max(
                0, 
                self._failure_counts[function_name] - 1
            )
    
    def _record_failure(self, function_name: str):
        """Ghi nhận thất bại"""
        self._failure_counts[function_name] += 1
        self._last_failure_time[function_name] = time.time()
        
        if self._failure_counts[function_name] >= self.failure_threshold:
            self._states[function_name] = CircuitState.OPEN
            self._success_counts[function_name] = 0
    
    def can_execute(self, function_name: str) -> tuple[bool, str]:
        """Kiểm tra xem có thể thực thi function không"""
        state = self._get_state(function_name)
        
        if state == CircuitState.CLOSED:
            return True, "Circuit closed, executing normally"
        
        if state == CircuitState.OPEN:
            time_until_retry = max(
                0,
                self.recovery_timeout - (time.time() - self._last_failure_time[function_name])
            )
            return False, f"Circuit OPEN. Retry in {time_until_retry:.0f}s"
        
        if state == CircuitState.HALF_OPEN:
            if self._half_open_calls[function_name] < self.half_open_max_calls:
                self._half_open_calls[function_name] += 1
                return True, "Circuit HALF_OPEN, testing recovery"
            return False, "Circuit HALF_OPEN, max test calls reached"
        
        return True, "Unknown state"
    
    def get_status(self, function_name: str) -> dict:
        """Lấy trạng thái chi tiết của circuit breaker"""
        state = self._get_state(function_name)
        return {
            "function": function_name,
            "state": state.value,
            "failure_count": self._failure_counts[function_name],
            "success_count": self._success_counts[function_name],
            "last_failure": self._last_failure_time.get(function_name),
            "can_execute": self.can_execute(function_name)[0]
        }

Sử dụng Circuit Breaker với Function Calling

circuit_breaker = CircuitBreaker( failure_threshold=3, # Mở circuit sau 3 failures recovery_timeout=30, # Thử recovery sau 30s success_threshold=2 # Cần 2 successes để close ) async def protected_function_call( client: HolySheepClient, function_name: str, messages: list, functions: list, fallback_handler: Callable = None ) -> dict: """ Function call được bảo vệ bởi circuit breaker """ can_execute, reason = circuit_breaker.can_execute(function_name) if not can_execute: print(f"[CIRCUIT BREAKER] Blocking {function_name}: {reason}") if fallback_handler: # Gọi fallback handler thay vì main function return await fallback_handler(function_name, messages) raise CircuitBreakerOpenError( f"Circuit breaker is OPEN for {function_name}. {reason}" ) try: result = await client.call_with_retry( messages=messages, functions=functions, function_name=function_name ) circuit_breaker._record_success(function_name) return result except Exception as e: circuit_breaker._record_failure(function_name) raise class CircuitBreakerOpenError(Exception): """Exception khi circuit breaker mở""" pass

3. Batch Retry Với Priority Queue

Trong trường hợp có nhiều requests bị timeout cùng lúc (như đợt flash sale kể trên), batch retry giúp xử lý có trọng tâm thay vì để tất cả retry cùng lúc và gây overload.

import asyncio
import heapq
from dataclasses import dataclass, field
from typing import Any, Optional
from datetime import datetime
import uuid

@dataclass(order=True)
class PrioritizedRetry:
    """Request cần retry với độ ưu tiên"""
    priority: int  # Số âm để min heap thành max heap
    retry_id: str = field(compare=False)
    function_name: str = field(compare=False)
    messages: list = field(compare=False)
    functions: list = field(compare=False)
    timestamp: datetime = field(compare=False)
    attempt: int = field(default=0, compare=False)
    original_timeout: float = field(default=30.0, compare=False)

class BatchRetryQueue:
    """
    Priority queue cho batch retry
    Ưu tiên requests quan trọng hơn (payment > inquiry)
    """
    
    def __init__(self, max_concurrent: int = 10):
        self._queue: list[PrioritizedRetry] = []
        self._retry_history: dict = {}
        self.max_concurrent = max_concurrent
        self._semaphore = asyncio.Semaphore(max_concurrent)
        
        # Priority mapping
        self.priority_map = {
            "process_payment": 0,    # Cao nhất
            "confirm_order": 1,
            "check_inventory": 2,
            "get_product_info": 5,
            "search_inventory": 4,
            "rag_retrieve": 3,        # Thấp nhất
        }
    
    def add(
        self,
        function_name: str,
        messages: list,
        functions: list,
        original_timeout: float = 30.0
    ) -> str:
        """Thêm request vào queue"""
        retry_id = str(uuid.uuid4())[:8]
        
        priority = self.priority_map.get(function_name, 10)
        
        item = PrioritizedRetry(
            priority=-priority,  # Negate vì heapq là min heap
            retry_id=retry_id,
            function_name=function_name,
            messages=messages,
            functions=functions,
            timestamp=datetime.now(),
            original_timeout=original_timeout
        )
        
        heapq.heappush(self._queue, item)
        self._retry_history[retry_id] = {
            "status": "queued",
            "attempts": 0,
            "function": function_name
        }
        
        return retry_id
    
    async def process_queue(self, client: HolySheepClient):
        """Xử lý queue với concurrency limit"""
        tasks = []
        
        while self._queue:
            # Lấy batch nhỏ để xử lý
            batch_size = min(3, len(self._queue))
            
            for _ in range(batch_size):
                if not self._queue:
                    break
                    
                item = heapq.heappop(self._queue)
                task = self._process_single(item, client)
                tasks.append(task)
            
            # Chờ batch hoàn thành
            if tasks:
                results = await asyncio.gather(*tasks, return_exceptions=True)
                tasks = []
                
                # Log results
                for i, result in enumerate(results):
                    if isinstance(result, Exception):
                        print(f"[BATCH] Retry failed: {result}")
                    else:
                        print(f"[BATCH] Retry success: {result}")
    
    async def _process_single(
        self, 
        item: PrioritizedRetry, 
        client: HolySheepClient
    ) -> dict:
        """Xử lý một retry item"""
        async with self._semaphore:
            try:
                result = await client.call_with_retry(
                    messages=item.messages,
                    functions=item.functions,
                    function_name=item.function_name
                )
                
                self._retry_history[item.retry_id].update({
                    "status": "success",
                    "final_attempt": item.attempt,
                    "result": result
                })
                
                return item.retry_id
                
            except Exception as e:
                item.attempt += 1
                
                # Re-queue nếu còn quota retry
                if item.attempt < 3:
                    item.priority += 1  # Giảm priority cho retry tiếp
                    heapq.heappush(self._queue, item)
                    self._retry_history[item.retry_id]["attempts"] = item.attempt
                else:
                    self._retry_history[item.retry_id].update({
                        "status": "failed",
                        "final_attempt": item.attempt,
                        "error": str(e)
                    })
                
                raise
    
    def get_status(self) -> dict:
        """Lấy trạng thái queue"""
        return {
            "pending": len(self._queue),
            "max_concurrent": self.max_concurrent,
            "history": self._retry_history
        }

Ví dụ sử dụng batch retry cho đợt flash sale

async def handle_flash_sale_spike( client: HolySheepClient, pending_requests: list ): """ Xử lý spike requests trong flash sale """ queue = BatchRetryQueue(max_concurrent=5) for req in pending_requests: queue.add( function_name=req["function"], messages=req["messages"], functions=req["functions"], original_timeout=req.get("timeout", 30.0) ) # Process với batch retry logic await queue.process_queue(client) # Tổng hợp kết quả status = queue.get_status() print(f"[FLASHSALE] Processed: {status['pending']} pending, " f"history: {len(status['history'])} total")

Lỗi Thường Gặp Và Cách Khắc Phục

1. Lỗi: "Request timeout after X seconds" - Retry Storm

Mô tả: Khi server temporarily overloaded, tất cả clients retry cùng lúc sau delay giống nhau, tạo ra retry storm làm server nặng thêm.

Nguyên nhân: Exponential backoff không có jitter hoặc jitter quá nhỏ.

# ❌ SAI: Không có jitter
delay = base_delay * (2 ** attempt)

✅ ĐÚNG: Exponential backoff với jitter ±25-50%

jitter = random.uniform(0, delay) # Full jitter

hoặc

jitter = delay * 0.3 * random.random() # 30% jitter actual_delay = delay + jitter

2. Lỗi: "Duplicate execution" - Payment được xử lý 2 lần

Mô tả: Function xử lý payment/giao dịch được gọi 2 lần sau retry, gây trùng thanh toán.

Nguyên nhân: Không có idempotency check trước khi retry.

# ✅ GIẢI PHÁP: Idempotency key với time-based deduplication

def _create_idempotency_key(self, request: dict) -> str:
    # Tạo key dựa trên request content + time bucket (1 phút)
    time_bucket = int(time.time() / 60)
    content_hash = hashlib.sha256(
        json.dumps(request, sort_keys=True).encode()
    ).hexdigest()
    return f"{time_bucket}_{content_hash}"

Kiểm tra trước khi thực thi

if self._idempotency_store.get(key): return {"status": "duplicate", "original_result": ...}

3. Lỗi: "Context window exceeded" sau nhiều retries

Mô tả: Sau 3-4 retries, messages array grow đầy context window.

Nguyên nhân: Retry không truncate messages, mỗi lần retry lại thêm messages.

# ✅ GIẢI PHÁP: Truncate messages trước khi retry

MAX_CONTEXT_MESSAGES = 50  # Tùy model

def _prepare_retry_messages(self, original_messages: list) -> list:
    """Chuẩn bị messages cho retry - loại bỏ messages quá dài"""
    # Chỉ giữ lại system prompt + N messages gần nhất
    system_msgs = [m for m in original_messages if m["role"] == "system"]
    other_msgs = [m for m in original_messages if m["role"] != "system"]
    
    recent_msgs = other_msgs[-MAX_CONTEXT_MESSAGES:]
    
    return system_msgs + recent_msgs

Sử dụng trong retry

truncated_messages = self._prepare_retry_messages(original_messages)

4. Lỗi: "Timeout nhưng function đã executed"

Mô tả: Server thực thi function thành công nhưng response timeout, client retry gây lỗi.

Nguyên nhân: Không có cách xác định function đã được execute hay chưa.

# ✅ GIẢI PHÁP: Server-side idempotency với status polling

async def call_with_safe_retry(client, request):
    idempotency_key = create_idempotency_key(request)
    
    # 1. Check nếu đã có result
    cached = await check_idempotency_store(idempotency_key)
    if cached:
        return cached
    
    # 2. Submit request
    result = await submit_request(request, idempotency_key)
    
    # 3. Poll cho đến khi có result
    for _ in range(timeout // 2):
        status = await check_request_status(idempotency_key)
        if status == "completed":
            return status["result"]
        await asyncio.sleep(2)
    
    # 4. Timeout - retry nhưng server sẽ detect duplicate
    return await submit_retry(request, idempotency_key)

Kết Quả Thực Tế

Sau khi triển khai kiến trúc này cho hệ thống e-commerce 5 triệu users:

Bảng So Sánh Chi Phí

Với HolySheep AI, chi phí cho function calling operations giảm đáng kể nhờ pricing structure hiệu quả:

ProviderGiá Input/Output ($/MTok)Chi phí cho 10K calls
OpenAI GPT-4.1$8.00~$80
Claude Sonnet 4.5$15.00~$150
Google Gemini 2.5 Flash$2.50~$25
HolySheep AI$0.42~$4.20

Với tỷ giá chỉ ¥1=$1, bạn tiết kiệm được 85%+ chi phí API. Thời gian phản hồi trung bình dưới 50ms, đủ nhanh để xử lý even spike requests mà không cần retry timeout.

Kết Luận

Timeout handling cho function calling không chỉ là "thử lại khi lỗi". Đó là một hệ thống phức tạp cần:

Triển khai đầy đủ kiến trúc này, bạn sẽ có một hệ thống AI robust có thể xử lý mọi điều kiện từ normal traffic cho đến flash sale extreme spikes.

👉 Đăng ký HolySheep AI — nhận tín dụng miễn phí khi đăng ký