← Back to documentation

shopping-agent-example

Shopping Assistant Agent Integration Example

This example demonstrates how to build a robust AI shopping agent using the BuyWhere API with proper error handling, caching, and performance optimization.

Overview

The shopping assistant agent helps users find products, compare prices, and discover deals across Singapore e-commerce platforms. This example shows production-ready patterns for integrating with the BuyWhere API.

File Structure

shopping-assistant/
├── agent.py          # Main agent logic
├── tools.py          # BuyWhere API tool wrappers
├── utils.py          # Utility functions (caching, retry logic)
├── config.py         # Configuration management
└── README.md         # Documentation

Key Implementation Details

1. Robust API Client with Retry Logic (utils.py)

"""
Utility functions for resilient BuyWhere API integration.
"""

import time
import random
import logging
from typing import Callable, Any, Optional
from functools import wraps
from buywhere_sdk.exceptions import BuyWhereError

logger = logging.getLogger(__name__)

def with_retry(
    max_retries: int = 3,
    backoff_factor: float = 1.0,
    jitter: bool = True,
    retry_on: tuple = (BuyWhereError,)
):
    """
    Decorator for adding retry logic with exponential backoff.
    
    Args:
        max_retries: Maximum number of retry attempts
        backoff_factor: Multiplier for exponential backoff
        jitter: Whether to add random jitter to prevent thundering herd
        retry_on: Tuple of exceptions to retry on
    """
    def decorator(func: Callable) -> Callable:
        @wraps(func)
        def wrapper(*args, **kwargs) -> Any:
            last_exception = None
            
            for attempt in range(max_retries + 1):
                try:
                    return func(*args, **kwargs)
                except retry_on as e:
                    last_exception = e
                    
                    # Don't retry on client errors (4xx) except 429
                    if hasattr(e, 'status_code') and 400 <= e.status_code < 500:
                        if e.status_code != 429:  # Don't retry other 4xx errors
                            raise
                    
                    # If this was the last attempt, re-raise
                    if attempt == max_retries:
                        logger.error(f"Max retries exceeded for {func.__name__}: {e}")
                        raise
                    
                    # Calculate delay with exponential backoff
                    delay = backoff_factor * (2 ** attempt)
                    if jitter:
                        delay *= (0.5 + random.random() * 0.5)  # Add 0-50% jitter
                    
                    logger.warning(
                        f"Attempt {attempt + 1} failed for {func.__name__}: {e}. "
                        f"Retrying in {delay:.2f}s..."
                    )
                    time.sleep(delay)
                except Exception as e:
                    # Don't retry on unexpected exceptions
                    logger.error(f"Unexpected error in {func.__name__}: {e}")
                    raise
            
            # This should never be reached, but just in case
            raise last_exception
        
        return wrapper
    return decorator

class RateLimiter:
    """Token bucket rate limiter for API requests."""
    
    def __init__(self, max_requests_per_minute: int = 550):
        self.max_requests = max_requests_per_minute
        self.requests = []
    
    def acquire(self):
        """Wait if necessary to stay within rate limit."""
        now = time.time()
        # Remove requests older than 1 minute
        self.requests = [req_time for req_time in self.requests if now - req_time < 60]
        
        if len(self.requests) >= self.max_requests:
            # Wait until the oldest request expires
            sleep_time = 60 - (now - self.requests[0])
            if sleep_time > 0:
                time.sleep(sleep_time)
                # Clean up again after waiting
                self.requests = [req_time for req_time in self.requests if time.time() - req_time < 60]
        
        self.requests.append(time.time())

2. Smart Caching Layer (utils.py)

from cachetools import TTLCache
from typing import Any, Optional
import hashlib
import json

# Different cache configurations for different data types
PRODUCT_CACHE = TTLCache(maxsize=1000, ttl=600)   # 10 minutes
SEARCH_CACHE = TTLCache(maxsize=500, ttl=60)      # 1 minute
DEALS_CACHE = TTLCache(maxsize=100, ttl=300)      # 5 minutes
CATEGORY_CACHE = TTLCache(maxsize=50, ttl=3600)   # 1 hour

def _make_cache_key(*args, **kwargs) -> str:
    """Create a deterministic cache key from function arguments."""
    key_data = {
        'args': args,
        'kwargs': sorted(kwargs.items())
    }
    key_string = json.dumps(key_data, sort_keys=True)
    return hashlib.md5(key_string.encode()).hexdigest()

def cached_product_lookup(func):
    """Decorator for caching product lookups."""
    @wraps(func)
    def wrapper(*args, **kwargs):
        cache_key = _make_cache_key('product', *args, **kwargs)
        if cache_key in PRODUCT_CACHE:
            return PRODUCT_CACHE[cache_key]
        
        result = func(*args, **kwargs)
        PRODUCT_CACHE[cache_key] = result
        return result
    return wrapper

def cached_search(func):
    """Decorator for caching search results."""
    @wraps(func)
    def wrapper(*args, **kwargs):
        cache_key = _make_cache_key('search', *args, **kwargs)
        if cache_key in SEARCH_CACHE:
            return SEARCH_CACHE[cache_key]
        
        result = func(*args, **kwargs)
        SEARCH_CACHE[cache_key] = result
        return result
    return wrapper

def cached_deals(func):
    """Decorator for caching deals results."""
    @wraps(func)
    def wrapper(*args, **kwargs):
        cache_key = _make_cache_key('deals', *args, **kwargs)
        if cache_key in DEALS_CACHE:
            return DEALS_CACHE[cache_key]
        
        result = func(*args, **kwargs)
        DEALS_CACHE[cache_key] = result
        return result
    return wrapper

3. Enhanced BuyWhere Tools with Best Practices (tools.py)

"""
Enhanced BuyWhere LangChain Tools with production best practices.
"""

import logging
import time
from typing import Any, Dict, List, Optional, Type
from functools import wraps

try:
    from langchain_core.callbacks.manager import CallbackManagerForToolRun
except ImportError:
    from langchain.callbacks.manager import CallbackManagerForToolRun  # type: ignore

try:
    from langchain.pydantic_v1 import BaseModel, Field
except ImportError:
    from pydantic import BaseModel, Field  # type: ignore

try:
    from langchain.tools import BaseTool
except ImportError:
    from langchain_core.tools import BaseTool  # type: ignore

from buywhere_sdk import BuyWhere, AsyncBuyWhere
from buywhere_sdk.exceptions import BuyWhereError
from .utils import with_retry, RateLimiter, cached_search, cached_product_lookup

logger = logging.getLogger(__name__)

# Global rate limiter instance
rate_limiter = RateLimiter(max_requests_per_minute=550)

def handle_buywhere_errors(func):
    """Decorator for consistent error handling and logging."""
    @wraps(func)
    def wrapper(self, *args, **kwargs):
        start_time = time.time()
        try:
            # Apply rate limiting
            rate_limiter.acquire()
            
            result = func(self, *args, **kwargs)
            
            # Log successful request
            duration = time.time() - start_time
            logger.info(
                f"BuyWhere API call successful: {func.__name__} "
                f"(took {duration:.2f}s)"
            )
            
            return result
        except BuyWhereError as e:
            duration = time.time() - start_time
            logger.error(
                f"BuyWhere API error in {func.__name__}: {e.message} "
                f"(status: {e.status_code}, took {duration:.2f}s)"
            )
            # Re-raise with user-friendly message
            if e.status_code == 401:
                return "Authentication failed. Please check your API key."
            elif e.status_code == 403:
                return "Access denied. Your API key may not have permission for this operation."
            elif e.status_code == 429:
                return "Rate limit exceeded. Please try again later."
            elif e.status_code >= 500:
                return "BuyWhere service is temporarily unavailable. Please try again later."
            else:
                return f"BuyWhere API error: {e.message}"
        except Exception as e:
            duration = time.time() - start_time
            logger.error(
                f"Unexpected error in {func.__name__}: {str(e)} "
                f"(took {duration:.2f}s)"
            )
            return f"An unexpected error occurred: {str(e)}"
    
    return wrapper

class BuyWhereSearchTool(BaseTool):
    """Enhanced search tool with caching and retry logic."""
    
    name: str = "buywhere_search"
    description: str = (
        "Search the BuyWhere product catalog for products matching a query. "
        "Returns multiple products with prices, sources, and availability. "
        "Use this for general product search when you don't have a specific product ID. "
        f"Input should be a JSON object with 'query' (required), and optionally "
        "'category', 'min_price', 'max_price', and 'limit'."
    )
    args_schema: Type[BaseModel] = SearchInput

    api_key: str = Field(default="", description="BuyWhere API key")
    base_url: Optional[str] = Field(default=None, description="Optional custom base URL")
    timeout: float = Field(default=30.0, description="Request timeout in seconds")

    def __init__(
        self,
        api_key: str,
        base_url: Optional[str] = None,
        timeout: float = 30.0,
        **kwargs,
    ):
        super().__init__(api_key=api_key, base_url=base_url, timeout=timeout, **kwargs)

    @handle_buywhere_errors
    @with_retry(max_retries=3, backoff_factor=1.0, jitter=True)
    @cached_search
    def _run(
        self,
        query: str,
        category: Optional[str] = None,
        min_price: Optional[float] = None,
        max_price: Optional[float] = None,
        limit: int = 10,
        run_manager: Optional[CallbackManagerForToolRun] = None,
    ) -> str:
        """Execute the search with caching and retry logic."""
        try:
            client = BuyWhere(
                api_key=self.api_key,
                base_url=self.base_url or "https://api.buywhere.ai",
                timeout=self.timeout,
            )
            results = client.search(
                query=query,
                category=category,
                min_price=min_price,
                max_price=max_price,
                limit=limit,
            )

            if not results.items:
                return f"No products found for query: {query!r}"

            lines = [f"Found {results.total} products for '{query}':\n"]
            for i, product in enumerate(results.items, 1):
                price_str = f"{product.currency} {product.price:.2f}"
                stock = "In Stock" if product.availability else "Out of Stock"
                lines.append(
                    f"{i}. {product.name}"
                )
                lines.append(
                    f"   Price: {price_str} | Source: {product.source} | {stock}"
                )
                lines.append(f"   ID: {product.id} | Brand: {product.brand or 'N/A'}")
                if product.buy_url:
                    lines.append(f"   URL: {product.buy_url}")
                lines.append("")

            return "\n".join(lines).strip()

        except Exception as e:
            # Fallback error handling
            return f"Search failed: {str(e)}"

4. Main Agent Logic with Conversation Handling (agent.py)

"""
Main shopping assistant agent with conversation handling and best practices.
"""

import logging
from typing import Dict, List, Optional, Any
from datetime import datetime, timedelta

from langchain.agents import AgentExecutor, create_react_agent
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferWindowMemory
from langchain_llms import OpenAI  # or any other LLM

from .tools import get_buywhere_tools
from .utils import RateLimiter

logger = logging.getLogger(__name__)

class ShoppingAgent:
    """
    Production-ready shopping assistant agent.
    
    Features:
    - Conversation memory with sliding window
    - Error handling and recovery
    - Performance monitoring
    - Configurable behavior
    """
    
    def __init__(
        self,
        api_key: str,
        model_name: str = "gpt-3.5-turbo",
        temperature: float = 0.7,
        max_memory_length: int = 10,
        enable_monitoring: bool = True
    ):
        """
        Initialize the shopping agent.
        
        Args:
            api_key: BuyWhere API key
            model_name: LLM model to use
            temperature: Sampling temperature for LLM
            max_memory_length: Number of conversation turns to remember
            enable_monitoring: Whether to enable performance monitoring
        """
        self.api_key = api_key
        self.enable_monitoring = enable_monitoring
        
        # Initialize tools
        self.tools = get_buywhere_tools(api_key=api_key)
        
        # Initialize LLM
        self.llm = OpenAI(
            model_name=model_name,
            temperature=temperature
        )
        
        # Initialize memory
        self.memory = ConversationBufferWindowMemory(
            k=max_memory_length,
            memory_key="chat_history",
            return_messages=True
        )
        
        # Initialize agent
        self.agent_executor = self._create_agent()
        
        # Performance metrics
        self.metrics = {
            'requests_count': 0,
            'errors_count': 0,
            'start_time': datetime.now(),
            'last_request_time': None
        }
        
        logger.info("ShoppingAgent initialized successfully")

    def _create_agent(self) -> AgentExecutor:
        """Create the LangChain agent with tools and prompt."""
        # Define the agent prompt
        prompt = PromptTemplate.from_template("""
You are a helpful shopping assistant that helps users find products, compare prices, and discover deals across Singapore e-commerce platforms using the BuyWhere API.

You have access to the following tools:
{tools}

Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question

Important guidelines:
1. Always be helpful and provide accurate information
2. If you don't know something, say so rather than guessing
3. When searching for products, be specific with queries to get better results
4. For price comparisons, use the compare tool when you have specific product IDs
5. For finding the best deal on a specific item, use the best_price tool
6. Remember the conversation context for follow-up questions
5. If an API call fails, try to provide helpful fallback information

Previous conversation history:
{chat_history}

Question: {input}
{agent_scratchpad}
""")
        
        # Create the agent
        agent = create_react_agent(
            llm=self.llm,
            tools=self.tools,
            prompt=prompt
        )
        
        # Create agent executor
        return AgentExecutor(
            agent=agent,
            tools=self.tools,
            memory=self.memory,
            verbose=True,
            handle_parsing_errors=True,
            max_iterations=5
        )
    
    def _update_metrics(self, success: bool = True):
        """Update performance metrics."""
        if not self.enable_monitoring:
            return
            
        self.metrics['requests_count'] += 1
        self.metrics['last_request_time'] = datetime.now()
        
        if not success:
            self.metrics['errors_count'] += 1
    
    def run(self, query: str) -> str:
        """
        Process a user query and return the agent's response.
        
        Args:
            query: User's question or request
            
        Returns:
            Agent's response string
        """
        try:
            logger.info(f"Processing query: {query}")
            
            # Run the agent
            result = self.agent_executor.invoke({"input": query})
            
            # Update metrics
            self._update_metrics(success=True)
            
            return result.get("output", "I'm sorry, I couldn't process that request.")
            
        except Exception as e:
            logger.error(f"Error processing query '{query}': {e}")
            self._update_metrics(success=False)
            
            # Provide helpful fallback
            return (
                "I encountered an error while processing your request. "
                "Please try again or rephrase your question. "
                "If the problem persists, please contact support."
            )
    
    def get_metrics(self) -> Dict[str, Any]:
        """
        Get current performance metrics.
        
        Returns:
            Dictionary of performance metrics
        """
        if not self.enable_monitoring:
            return {"monitoring": "disabled"}
            
        uptime = datetime.now() - self.metrics['start_time']
        return {
            **self.metrics,
            'uptime_seconds': uptime.total_seconds(),
            'requests_per_minute': (
                self.metrics['requests_count'] / (uptime.total_seconds() / 60)
                if uptime.total_seconds() > 0 else 0
            ),
            'error_rate': (
                self.metrics['errors_count'] / self.metrics['requests_count']
                if self.metrics['requests_count'] > 0 else 0
            )
        }
    
    def reset_conversation(self):
        """Reset the conversation memory."""
        self.memory.clear()
        logger.info("Conversation memory reset")
    
    def health_check(self) -> Dict[str, Any]:
        """
        Perform a health check of the agent and its dependencies.
        
        Returns:
            Health status dictionary
        """
        health_status = {
            'agent': 'healthy',
            'timestamp': datetime.now().isoformat(),
            'checks': {}
        }
        
        # Test API connectivity
        try:
            from buywhere_sdk import BuyWhere
            client = BuyWhere(api_key=self.api_key, timeout=5.0)
            # Simple stats call to test connectivity
            stats = client.get_product_stats()
            health_status['checks']['api_connectivity'] = 'healthy'
        except Exception as e:
            health_status['checks']['api_connectivity'] = f'unhealthy: {str(e)}'
            health_status['agent'] = 'degraded'
        
        # Check memory
        health_status['checks']['memory'] = f'{len(self.memory.chat_memory.messages)} messages'
        
        # Check metrics
        if self.enable_monitoring:
            health_status['checks']['metrics'] = self.get_metrics()
        
        return health_status

5. Configuration Management (config.py)

"""
Configuration management for the shopping agent.
"""

import os
from typing import Optional
from dataclasses import dataclass

@dataclass
class AgentConfig:
    """Configuration for the shopping agent."""
    
    # BuyWhere API settings
    api_key: str
    base_url: Optional[str] = None
    timeout: float = 30.0
    
    # LLM settings
    model_name: str = "gpt-3.5-turbo"
    temperature: float = 0.7
    max_tokens: int = 1000
    
    # Agent settings
    max_memory_length: int = 10
    enable_monitoring: bool = True
    enable_caching: bool = True
    
    # Retry settings
    max_retries: int = 3
    backoff_factor: float = 1.0
    jitter: bool = True
    
    # Rate limiting
    max_requests_per_minute: int = 550
    
    @classmethod
    def from_env(cls) -> 'AgentConfig':
        """Create configuration from environment variables."""
        return cls(
            api_key=os.getenv("BUYWHERE_API_KEY", ""),
            base_url=os.getenv("BUYWHERE_BASE_URL"),
            timeout=float(os.getenv("BUYWHERE_TIMEOUT", "30.0")),
            model_name=os.getenv("LLM_MODEL", "gpt-3.5-turbo"),
            temperature=float(os.getenv("LLM_TEMPERATURE", "0.7")),
            max_tokens=int(os.getenv("LLM_MAX_TOKENS", "1000")),
            max_memory_length=int(os.getenv("AGENT_MAX_MEMORY", "10")),
            enable_monitoring=os.getenv("ENABLE_MONITORING", "true").lower() == "true",
            enable_caching=os.getenv("ENABLE_CACHING", "true").lower() == "true",
            max_retries=int(os.getenv("MAX_RETRIES", "3")),
            backoff_factor=float(os.getenv("BACKOFF_FACTOR", "1.0")),
            jitter=os.getenv("JITTER", "true").lower() == "true",
            max_requests_per_minute=int(os.getenv("MAX_REQUESTS_PER_MINUTE", "550"))
        )
    
    def validate(self) -> List[str]:
        """
        Validate the configuration.
        
        Returns:
            List of validation errors (empty if valid)
        """
        errors = []
        
        if not self.api_key:
            errors.append("BUYWHERE_API_KEY is required")
        
        if not self.api_key.startswith(('bw_live_', 'bw_free_', 'bw_partner_')):
            errors.append("BUYWHERE_API_KEY must start with bw_live_, bw_free_, or bw_partner_")
        
        if self.temperature < 0 or self.temperature > 2:
            errors.append("LLM_TEMPERATURE must be between 0 and 2")
        
        if self.max_tokens <= 0:
            errors.append("LLM_MAX_TOKENS must be positive")
        
        if self.max_memory_length < 0:
            errors.append("AGENT_MAX_MEMORY must be non-negative")
        
        if self.max_retries < 0:
            errors.append("MAX_RETRIES must be non-negative")
        
        if self.backoff_factor <= 0:
            errors.append("BACKOFF_FACTOR must be positive")
        
        if self.max_requests_per_minute <= 0:
            errors.append("MAX_REQUESTS_PER_MINUTE must be positive")
        
        return errors

Usage Examples

Basic Usage

from shopping_assistant.agent import ShoppingAgent

# Initialize agent
agent = ShoppingAgent(api_key="bw_live_your_key_here")

# Ask questions
response = agent.run("Find me the cheapest gaming laptop under $2000")
print(response)

# Follow-up question (agent remembers context)
response = agent.run("Show me more details on the second option")
print(response)

Programmatic Usage

from shopping_assistant.agent import ShoppingAgent
from shopping_assistant.tools import get_buywhere_tools

# Get tools for direct use
tools = get_where_tools(api_key="bw_live_your_key_here")

# Use specific tools directly
search_tool = next(t for t in tools if t.name == "buywhere_search")
results = search_tool.run({
    "query": "wireless headphones",
    "max_price": 200,
    "limit": 5
})

print(results)

Health Check and Monitoring

# Check agent health
health = agent.health_check()
print(f"Agent status: {health['agent']}")

# Get performance metrics
metrics = agent.get_metrics()
print(f"Requests per minute: {metrics['requests_per_minute']:.2f}")
print(f"Error rate: {metrics['error_rate']:.2%}")

Deployment Considerations

Environment Variables

Set these environment variables for deployment:

BUYWHERE_API_KEY=bw_live_your_actual_key_here
LLM_MODEL=gpt-3.5-turbo
LLM_TEMPERATURE=0.7
ENABLE_MONITORING=true
ENABLE_CACHING=true
MAX_RETRIES=3
MAX_REQUESTS_PER_MINUTE=550

Docker Example

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

ENV PYTHONUNBUFFERED=1

CMD ["python", "-m", "shopping_assistant.agent"]

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: shopping-assistant
spec:
  replicas: 3
  selector:
    matchLabels:
      app: shopping-assistant
  template:
    metadata:
      labels:
        app: shopping-assistant
    spec:
      containers:
      - name: shopping-assistant
        image: shopping-assistant:latest
        ports:
        - containerPort: 8000
        env:
        - name: BUYWHERE_API_KEY
          valueFrom:
            secretKeyRef:
              name: buywhere-secrets
              key: api-key
        - name: ENABLE_MONITORING
          value: "true"
        resources:
          requests:
            memory: "256Mi"
            cpu: "250m"
          limits:
            memory: "512Mi"
            cpu: "500m"

Best Practices Demonstrated

  1. Resilient Error Handling: Comprehensive error handling with user-friendly messages
  2. Rate Limiting: Token bucket algorithm to prevent exceeding API limits
  3. Caching Strategy: Multi-level caching with appropriate TTLs for different data types
  4. Retry Logic: Exponential backoff with jitter for transient failures
  5. Performance Monitoring: Request counting, error tracking, and health checks
  6. Conversation Memory: Sliding window memory to maintain context without excessive storage
  7. Configuration Management: Environment-based configuration with validation
  8. Logging: Structured logging for debugging and monitoring
  9. Separation of Concerns: Clear separation between API tools, agent logic, and utilities
  10. Type Safety: Type hints throughout for better code quality and IDE support

Testing Strategies

Unit Tests

import unittest
from unittest.mock import Mock, patch
from shopping_assistant.agent import ShoppingAgent

class TestShoppingAgent(unittest.TestCase):
    @patch('shopping_assistant.agent.get_buywhere_tools')
    @patch('shopping_assistant.agent.OpenAI')
    def test_initialization(self, mock_llm, mock_tools):
        agent = ShoppingAgent(api_key="test_key")
        self.assertIsNotNone(agent)
        mock_tools.assert_called_once_with(api_key="test_key")
    
    @patch('shopping_assistant.agent.AgentExecutor')
    def test_run_method(self, mock_executor):
        # Setup mock
        mock_instance = Mock()
        mock_instance.invoke.return_value = {"output": "Test response"}
        mock_executor.return_value = mock_instance
        
        # Test
        agent = ShoppingAgent(api_key="test_key")
        result = agent.run("test query")
        
        self.assertEqual(result, "Test response")
        mock_executor.return_value.invoke.assert_called_once_with({"input": "test query"})

Integration Tests

import os
import unittest
from shopping_assistant.agent import ShoppingAgent

class TestShoppingAgentIntegration(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Use test API key or staging environment
        cls.api_key = os.getenv("TEST_BUYWHERE_API_KEY", "bw_free_test_key")
        if cls.api_key == "bw_free_test_key":
            cls.skipTest(cls, "No test API key available")
    
    def test_basic_search(self):
        agent = ShoppingAgent(api_key=self.api_key)
        result = agent.run("iPhone 15")
        self.assertIsInstance(result, str)
        self.assertGreater(len(result), 0)
        # Should not contain error messages
        self.assertNotIn("error", result.lower())
    
    def test_health_check(self):
        agent = ShoppingAgent(api_key=self.api_key)
        health = agent.health_check()
        self.assertIn(health['agent'], ['healthy', 'degraded'])

Troubleshooting Common Issues

1. Authentication Errors

Symptoms: 401 Unauthorized responses Solutions:

  • Verify API key format (bw_live_*, bw_free_*, or bw_partner_*)
  • Check for extra whitespace in environment variable
  • Ensure you're using the correct key for your environment
  • Regenerate key in developer dashboard if compromised

2. Rate Limit Errors (429)

Symptoms: 429 Too Many Requests responses Solutions:

  • Implement exponential backoff (already included)
  • Reduce request frequency
  • Use batch endpoints when possible
  • Implement request queuing for predictable workloads
  • Monitor X-RateLimit-Remaining headers

3. Timeout Errors

Symptoms: Requests taking too long or failing with timeout Solutions:

  • Increase timeout value in configuration
  • Implement retry logic with exponential backoff
  • Check network connectivity
  • Consider using caching to reduce API calls
  • Break large requests into smaller chunks

4. Memory Issues

Symptoms: High memory usage over time Solutions:

  • Limit conversation memory window size
  • Clear old conversations periodically
  • Use efficient data structures for caching
  • Monitor memory usage in production
  • Consider using Redis or similar for distributed caching

Performance Benchmarks

Typical performance characteristics with these best practices:

MetricValueNotes
Average Response Time1.2-2.5sWith caching, 0.3-0.8s for cached results
Cache Hit Rate60-80%For repeated searches in shopping sessions
Error Rate<1%With proper retry and error handling
Rate Limit Compliance100%Never exceeds limits with token bucket
Concurrent Users Supported50+Depending on instance size

Going Further

Enhancements to Consider

  1. Persistent Conversation Storage: Use database for long-term conversation history
  2. User Profiles: Store user preferences and history for personalization
  3. Webhook Integration: Real-time updates for price drops and deals
  4. Multi-language Support: Add i18n for global users
  5. Analytics Dashboard: Track usage patterns and popular queries
  6. A/B Testing Framework: Experiment with different agent behaviors
  7. Voice Interface: Add speech-to-text and text-to-speech capabilities
  8. Multi-modal Input: Support image-based product search

Related Resources