Почему ваше приложение падает в 3 часа ночи
Вы развернули AI-сервис на Amazon Bedrock. Он работает прекрасно две недели. Потом в 3:17 ночи приложение падает. В логах - ThrottlingException. Клиенты недовольны. Вы пытаетесь понять, что случилось.
Оказывается, ваш бот начал обрабатывать ночные запросы от азиатских пользователей. Bedrock ограничил количество запросов. Приложение не умеет обрабатывать ошибки 429. Все сломалось.
Та же история с 503 ошибкой (ServiceUnavailableException). AWS проводит технические работы. Ваше приложение думает, что сервис умер навсегда. Паника.
Важно: Bedrock - управляемый сервис. Вы не контролируете его инфраструктуру. Но вы контролируете, как ваше приложение реагирует на его проблемы.
Две ошибки, которые разрушат ваш день
Давайте разберемся, что означают эти ошибки и почему они появляются.
| Ошибка | Код HTTP | Исключение в boto3 | Причина |
|---|---|---|---|
| Rate limiting | 429 | ThrottlingException | Превышен лимит запросов |
| Service unavailable | 503 | ServiceUnavailableException | Сервис временно недоступен |
ThrottlingException - это не ошибка. Это механизм защиты. Bedrock говорит: "Слишком быстро, притормози". Если вы игнорируете это сообщение и продолжаете слать запросы, AWS может заблокировать ваш аккаунт.
ServiceUnavailableException - это временная проблема. Может длиться секунды. Может минуты. В глобальном выводе моделей такие ошибки случаются чаще, чем вы думаете.
Как НЕ надо обрабатывать ошибки
Посмотрите на этот код. Я видел его в десятках проектов:
import boto3
import json
client = boto3.client('bedrock-runtime')
def generate_text(prompt):
try:
response = client.invoke_model(
modelId='anthropic.claude-3-5-sonnet-20241022',
contentType='application/json',
body=json.dumps({
"prompt": prompt,
"max_tokens": 1000
})
)
return json.loads(response['body'].read())
except Exception as e:
print(f"Error: {e}")
return None
Что здесь не так? Все.
- Одна попытка и сдался
- Нет задержки между ретраями
- Нет дифференциации ошибок
- Нет ограничения количества попыток
Этот код сломается при первой же 429 ошибке. В продакшене такое недопустимо.
Правильная стратегия ретраев
Хорошая обработка ошибок строится на трех принципах:
- Экспоненциальная задержка (exponential backoff)
- Джокер (jitter - случайная добавка)
- Разные стратегии для разных ошибок
1 Базовый ретрай с экспоненциальной задержкой
Вот как это должно выглядеть:
import boto3
import json
import time
import random
from botocore.exceptions import ClientError
class BedrockClientWithRetry:
def __init__(self, max_retries=5, base_delay=1):
self.client = boto3.client('bedrock-runtime')
self.max_retries = max_retries
self.base_delay = base_delay
def invoke_with_retry(self, model_id, body):
last_exception = None
for attempt in range(self.max_retries):
try:
response = self.client.invoke_model(
modelId=model_id,
contentType='application/json',
body=json.dumps(body)
)
return json.loads(response['body'].read())
except ClientError as e:
last_exception = e
# Проверяем тип ошибки
error_code = e.response['Error']['Code']
if error_code == 'ThrottlingException':
# Для 429 ошибки используем экспоненциальную задержку
delay = self.base_delay * (2 ** attempt)
# Добавляем случайность (jitter)
jitter = random.uniform(0, 0.1 * delay)
total_delay = delay + jitter
print(f"Throttling detected. Retry {attempt + 1}/{self.max_retries} in {total_delay:.2f}s")
time.sleep(total_delay)
elif error_code == 'ServiceUnavailableException':
# Для 503 ошибки - фиксированная задержка
delay = 5 # секунд
print(f"Service unavailable. Retry {attempt + 1}/{self.max_retries} in {delay}s")
time.sleep(delay)
else:
# Другие ошибки не ретраим
raise
# Если все попытки исчерпаны
raise last_exception
Почему экспоненциальная задержка? Потому что она:
- Дает сервису время восстановиться
- Предотвращает лавину запросов при восстановлении
- Снижает нагрузку на ваше приложение
2 Умные ретраи с circuit breaker
Экспоненциальные задержки - это хорошо, но недостаточно. Нужен circuit breaker (предохранитель).
Circuit breaker отслеживает количество ошибок. Если их слишком много, он "разрывает цепь" и временно блокирует запросы. Это защищает и ваш сервис, и Bedrock.
class CircuitBreaker:
def __init__(self, failure_threshold=5, reset_timeout=60):
self.failure_threshold = failure_threshold
self.reset_timeout = reset_timeout
self.failure_count = 0
self.last_failure_time = None
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
def can_execute(self):
if self.state == "OPEN":
# Проверяем, не истек ли таймаут
if time.time() - self.last_failure_time > self.reset_timeout:
self.state = "HALF_OPEN"
return True
return False
return True
def on_success(self):
if self.state == "HALF_OPEN":
self.state = "CLOSED"
self.failure_count = 0
def on_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "OPEN"
print(f"Circuit breaker OPENED. Waiting {self.reset_timeout}s")
class ResilientBedrockClient:
def __init__(self):
self.client = BedrockClientWithRetry()
self.circuit_breaker = CircuitBreaker()
def generate(self, prompt):
if not self.circuit_breaker.can_execute():
raise Exception("Circuit breaker is OPEN")
try:
result = self.client.invoke_with_retry(
model_id='anthropic.claude-3-5-sonnet-20241022',
body={"prompt": prompt, "max_tokens": 1000}
)
self.circuit_breaker.on_success()
return result
except Exception as e:
self.circuit_breaker.on_failure()
raise
Что делать, когда все падает
Иногда ретраи не помогают. Bedrock недоступен. Что тогда?
У вас должен быть план Б. Несколько вариантов:
- Фолбэк на другую модель. Claude недоступен? Попробуйте Titan или Jurassic.
- Фолбэк на другой регион. Используйте глобальную архитектуру с репликацией в нескольких регионах.
- Кеширование ответов. Для частых запросов сохраняйте ответы в Redis.
- Деградация функциональности. Вместо сложного анализа покажите простой ответ.
class FallbackBedrockClient:
def __init__(self):
self.primary_model = 'anthropic.claude-3-5-sonnet-20241022'
self.fallback_models = [
'amazon.titan-text-premier-v1:0',
'ai21.j2-ultra-v1'
]
self.current_model_index = 0
def generate_with_fallback(self, prompt):
models_to_try = [self.primary_model] + self.fallback_models
for model_id in models_to_try:
try:
client = BedrockClientWithRetry(max_retries=3)
return client.invoke_with_retry(model_id, {"prompt": prompt})
except Exception as e:
print(f"Model {model_id} failed: {e}")
continue
# Все модели упали
return {"completion": "Извините, сервис временно недоступен. Попробуйте позже."}
Мониторинг и алерты
Обработка ошибок - это не только код. Это еще и мониторинг.
Что нужно мониторить:
- Количество 429 ошибок в минуту
- Количество 503 ошибок
- Среднее время ответа
- Процент успешных запросов (SLA)
- Состояние circuit breaker
Внимание: Если у вас больше 1% 429 ошибок, вы делаете что-то не так. Лишком много запросов. Нужно оптимизировать.
Настройте CloudWatch алерты:
{
"AlarmName": "Bedrock-Throttling-High",
"MetricName": "ThrottledRequests",
"Namespace": "AWS/Bedrock",
"Statistic": "Sum",
"Period": 300,
"EvaluationPeriods": 2,
"Threshold": 10,
"ComparisonOperator": "GreaterThanThreshold"
}
Частые ошибки и как их избежать
Ошибка 1: Бесконечные ретраи
Без ограничения попыток приложение может пытаться бесконечно. Установите разумный лимит (3-5 попыток).
Ошибка 2: Отсутствие jitter
Все инстансы вашего приложения падают одновременно и восстанавливаются одновременно. Добавьте случайность.
Ошибка 3: Игнорирование заголовков retry-after
Bedrock иногда возвращает заголовок Retry-After. Используйте его!
def get_retry_delay_from_response(response):
if 'Retry-After' in response.headers:
try:
return int(response.headers['Retry-After'])
except:
pass
return None
Ошибка 4: Одинаковая стратегия для всех ошибок
429 и 503 требуют разных подходов. 429 - экспоненциальная задержка. 503 - фиксированная пауза.
Когда это особенно важно
Есть сценарии, где ошибки Bedrock особенно критичны:
- Мультиагентные системы. Каждый агент делает запросы. Ошибка одного ломает всю цепочку. Читайте про продвинутые техники для мультиагентных систем.
- RAG-системы. Поиск + генерация. Если Bedrock падает, вся система бесполезна. Узнайте о проблемах RAG-систем.
- Корпоративные чат-боты. Тысячи пользователей. SLA 99.9%. Без правильной обработки ошибок не достичь.
Что изменилось в 2025-2026
Bedrock постоянно развивается. На 11 февраля 2026 года:
- Новые модели имеют более высокие лимиты запросов
- Появилась встроенная поддержка ретраев в некоторых SDK
- Улучшен мониторинг и алертинг
- Но основные принципы обработки ошибок не изменились
Последние версии моделей (актуально на 11.02.2026):
- anthropic.claude-3-5-sonnet-20250220 (новая версия)
- amazon.titan-text-premier-v2:0
- meta.llama-3-3-70b-instruct-v2:0
- cohere.command-r-plus-20250206
Финальный совет
Тестируйте обработку ошибок. Специально вызывайте 429 и 503 в тестовой среде. Используйте инструменты вроде AWS Fault Injection Simulator.
Помните: Bedrock - это чужой сервис. Вы не контролируете его доступность. Но вы контролируете, как ваше приложение ведет себя, когда Bedrock недоступен.
Хорошая обработка ошибок превращает "катастрофу" в "временную проблему". Плохая обработка ошибок превращает временную проблему в катастрофу.
И последнее: не пытайтесь обойти rate limiting. Если Bedrock говорит "стоп", он это серьезно. Лучше оптимизируйте ваши запросы. Используйте эффективное управление контекстом, кеширование, батчинг. Это дешевле, чем разбираться с заблокированным аккаунтом.