AWS Bedrock LLM GenAI

AWS Bedrock ThrottlingException na InvokeModel: diagnostyka limitów i naprawa błędów 429

Rozwiązanie błędów ThrottlingException i ModelStreamErrorException w Bedrock poprzez diagnostykę limitów, implementację backoff i zwiększenie kwot.

·
Wywołania Bedrock InvokeModel kończą z błędem ThrottlingException (HTTP 429)? Twoje konto przekroczyło limit tokenów na minutę lub zapytań na minutę. Rozwiązanie wymaga zdiagnozowania, który limit został osiągnięty, wdrożenia exponential backoff i złożenia wniosku o zwiększenie quoty.

Objawy

Logi aplikacji pokazują błędy API Bedrock:

botocore.exceptions.ClientError: An error occurred (ThrottlingException) when calling the InvokeModel operation: Too many requests, please wait before trying again.

Lub przy streamingu:

botocore.exceptions.EventStreamError: An error occurred (ModelStreamErrorException) when calling the InvokeModelWithResponseStream operation: Rate exceeded

Obserwowalny wpływ:

  • Opóźnienie inferencji skacze z normalnych 200-400ms do timeoutów.
  • Metryka Bedrock > InvocationThrottles w CloudWatch skacze z 0 do setek ms.
  • Użytkownicy końcowi widzą błędy lub długie ładowanie
  • Ponawianie w aplikacji bez backoff pogarsza sytuację (efekt stada)

Przyczyna

AWS Bedrock stosuje limity per model, per region w dwóch wymiarach:

  1. Requests per minute (RPM) - liczba wywołań API na minutę, niezależnie od rozmiaru
  2. Tokens per minute (TPM) - suma tokenów wejściowych i wyjściowych przetworzonych na minutę

Domyślne limity różnią się w zależności od modelu. Przykładowe wartości (eu-central-1):

Model Domyślny RPM Domyślny TPM
Claude Sonnet 4.6 60 80 000
Claude 3.5 Haiku 100 100 000
Llama 3 70B 60 100 000

Typowe przyczyny:

  • Skok przetwarzania wsadowego - pipeline ingestion wywołuje Bedrock do embeddingów/klasyfikacji równocześnie
  • Brak backoff - aplikacja natychmiast ponawia próby po błędzie, tworząc efekt stada (thundering herd)
  • Współdzielona kwota - wiele środowisk (dev, staging, produkcja) korzysta z tego samego konta i regionu
  • Długie prompty - kilka requestów z kontekstem 100K+ tokenów wyczerpuje cały budżet TPM

Rozwiązanie

Krok 1: Zidentyfikuj, który limit został osiągnięty

Sprawdź metryki CloudWatch w przestrzeni nazw Bedrock:

# Sprawdź liczbę throttle w ostatniej godzinie
aws cloudwatch get-metric-statistics \
  --namespace AWS/Bedrock \
  --metric-name InvocationThrottles \
  --dimensions Name=ModelId,Value=anthropic.claude-3-5-haiku-20241022-v1:0 \
  --start-time $(date -u -v-1H +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 60 \
  --statistics Sum \
  --region eu-central-1

Sprawdź aktualne kwoty:

# Lista limitów usługi Bedrock
aws service-quotas list-service-quotas \
  --service-code bedrock \
  --region eu-central-1 \
  --query 'Quotas[?contains(QuotaName, `InvokeModel`)].{Name:QuotaName, Value:Value}'

Krok 2: Zaimplementuj exponential backoff z jitter

Jeśli aplikacja ponawia natychmiast, pogarsza throttling. Dodaj prawidłowy backoff:

import time
import random
import boto3
from botocore.exceptions import ClientError

bedrock = boto3.client("bedrock-runtime", region_name="eu-central-1")

def invoke_with_backoff(model_id, body, max_retries=5):
    """Call Bedrock with exponential backoff and jitter."""
    for attempt in range(max_retries):
        try:
            return bedrock.invoke_model(modelId=model_id, body=body)
        except ClientError as e:
            if e.response["Error"]["Code"] == "ThrottlingException":
                if attempt == max_retries - 1:
                    raise
                # Exponential backoff: 1s, 2s, 4s, 8s, 16s + jitter
                wait = (2 ** attempt) + random.uniform(0, 1)
                time.sleep(wait)
            else:
                raise

Krok 3: Złóż wniosek o zwiększenie limitów

# Wniosek o zwiększenie RPM dla Claude 3.5 Haiku
aws service-quotas request-service-quota-increase \
  --service-code bedrock \
  --quota-code L-XXXXXXXX \
  --desired-value 300 \
  --region eu-central-1

Znajdź prawidłowy quota-code:

aws service-quotas list-service-quotas --service-code bedrock --region eu-central-1 \
  --query 'Quotas[?contains(QuotaName, `Haiku`)].{Code:QuotaCode, Name:QuotaName, Value:Value}'

Zwiększenie limitów zajmuje zwykle 1-3 dni roboczych. W pilnych przypadkach otwórz zgłoszenie w AWS Support.

Krok 4: Zaprojektuj architekturę pod stałą przepustowość

Dla systemów produkcyjnych wymagających gwarantowanej przepustowości:

  • Provisioned Throughput - wykupienie dedykowanych jednostek modelu zapewnia stały RPM/TPM bez throttlingu
  • Routing multi-region - rozłożenie requestów między eu-central-1 i eu-west-1 (każdy region ma niezależne limity)
  • Kolejkowanie requestów - użycie SQS do buforowania i przetwarzania ze stałą szybkością poniżej limitu
  • Separacja kont - izolacja produkcji od dev/staging zapobiega rywalizacji o limity

Walidacja

Po wdrożeniu poprawek potwierdź, że throttling ustał:

# Zweryfikuj zero throttle w ostatnich 30 minutach
aws cloudwatch get-metric-statistics \
  --namespace AWS/Bedrock \
  --metric-name InvocationThrottles \
  --dimensions Name=ModelId,Value=anthropic.claude-3-5-haiku-20241022-v1:0 \
  --start-time $(date -u -v-30M +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 60 \
  --statistics Sum \
  --region eu-central-1

Oczekiwany wynik: Sum: 0.0 dla wszystkich punktów danych.

Ustaw alarm, żeby wyłapać przyszły throttling wcześnie:

aws cloudwatch put-metric-alarm \
  --alarm-name bedrock-throttling-alert \
  --metric-name InvocationThrottles \
  --namespace AWS/Bedrock \
  --statistic Sum \
  --period 300 \
  --threshold 10 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 1 \
  --alarm-actions arn:aws:sns:eu-central-1:ACCOUNT:ops-alerts

Powiązane