📢 转载信息
原文作者:Farzin Bagheri, Abel Laura, Arun Km, and Aswath Ram A Srinivasan
在生产的生成式AI应用中,我们时常会遇到一系列错误,其中最常见的是返回 429 ThrottlingException 和 503 ServiceUnavailableException 错误的请求失败。作为一个业务应用,这些错误可能由于应用架构中的多个层次而发生。
大多数情况下,这些错误是可重试的,但这会影响用户体验,因为对应用的调用会延迟。响应延迟会扰乱对话的自然流程,降低用户兴趣,并最终阻碍AI驱动的解决方案在交互式AI应用中的广泛采用。
最常见的挑战之一是多个用户同时对单个模型进行广泛的应用调用。掌握这些错误意味着应用是稳健的还是让用户感到沮丧之间的区别。
本文将向您展示如何实施稳健的错误处理策略,以帮助在使用 Amazon Bedrock 时提高应用程序的可靠性和用户体验。我们将深入探讨优化应用程序性能以应对这些错误的策略。无论您是针对全新的应用程序还是成熟的AI应用程序,在这篇文章中,您都将找到针对这些错误进行操作的实用指南。
前提条件
- 拥有Amazon Bedrock访问权限的AWS账户
- 安装了 Python 3.x 和 boto3
- 对AWS服务的基本了解
- IAM 权限:确保您具有以下最低权限:
- 针对您的特定模型的
bedrock:InvokeModel或bedrock:InvokeModelWithResponseStream - 用于监控的
cloudwatch:PutMetricData,cloudwatch:PutMetricAlarm - 如果使用SNS通知,则为
sns:Publish - 遵循最小权限原则 – 只授予用例所需的权限
- 针对您的特定模型的
示例 IAM 策略:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "bedrock:InvokeModel" ], "Resource": "arn:aws:bedrock:us-east-1:123456789012:model/anthropic.claude-*" } ]
}
注意: 本演练使用可能会产生费用的AWS服务,包括用于监控的 Amazon CloudWatch 和用于通知的 Amazon SNS。详情请参阅AWS定价页面。
快速参考:503 与 429 错误
下表比较了这两种错误类型:
| 方面 | 503 ServiceUnavailable (服务不可用) | 429 ThrottlingException (限流异常) |
|---|---|---|
| 主要原因 | 临时服务容量问题,服务器故障 | 超出了账户配额(RPM/TPM) |
| 与配额相关 | 与配额无关 | 直接与配额相关 |
| 解决时间 | 瞬态,刷新更快 | 需要等待配额刷新 |
| 重试策略 | 带有指数退避的即时重试 | 必须与60秒配额周期同步 |
| 用户操作 | 等待并重试,考虑替代方案 | 优化请求模式,增加配额 |
深入研究 429 ThrottlingException
429 ThrottlingException 意味着 Amazon Bedrock 正在故意拒绝您的部分请求,以确保总体使用量保持在您配置或默认分配的配额之内。在实践中,您最常会看到三种限流情况:基于速率、基于令牌和特定于模型的限流。
1. 基于速率的限流(RPM – 每分钟请求数)
错误消息:
ThrottlingException: Too many requests, please wait before trying again.
或者:
botocore.errorfactory.ThrottlingException: An error occurred (ThrottlingException) when calling the InvokeModel operation: Too many requests, please wait before trying again
这实际指示了什么
基于速率的限流是在发送到给定模型和区域的每分钟总 Bedrock 请求数超过您账户的 RPM 配额时触发的。关键细节是,此限制在调用方之间强制执行,而不仅仅是针对单个应用程序或微服务。
想象一下咖啡店的一个共享队列:哪个团队排队并不重要;咖啡师每分钟只能制作固定数量的饮料。一旦加入队列的人数超过咖啡师可以处理的数量,一些顾客就会被告知稍后再回来。这条“稍后再回来”的消息就是您的 429。
多应用高峰场景
假设您有三个生产应用程序,它们都在同一区域调用相同的 Bedrock 模型:
- 应用 A 通常的峰值约为每分钟 50 个请求。
- 应用 B 的峰值也约为 50 rpm。
- 应用 C 在其自身高峰期通常运行在约 50 rpm。
运维部门已请求此模型的配额为 150 RPM,这看起来很合理,因为 50 + 50 + 50 = 150,并且历史仪表板显示每个应用都保持在预期的峰值水平。
然而,在现实中,您的流量不是完美平稳的。也许在闪购或营销活动期间,应用 A 的 RPM 瞬间飙升至 60,而 B 和 C 保持在 50。该分钟的总和变为 160 rpm,超过了您的 150 rpm 配额,一些请求开始以 ThrottlingException 失败。
当三个应用在更长的时间内同时向上移动时,您也可能遇到麻烦。想象一种新的模式,高峰流量如下所示:
- 应用 A: 75 rpm
- 应用 B: 50 rpm
- 应用 C: 50 rpm
您的新真实峰值为 175 rpm,即使原始配额是按 150 设计的。在这种情况下,您将在那些高峰时段定期看到 429 错误,即使平均日流量仍然看起来“正常”。
缓解策略
对于基于速率的限流,缓解措施有两个方面:客户端行为和配额管理。
在客户端方面:
- 实施请求速率限制,以限制每个应用程序每秒或每分钟可以发送的调用次数。API、SDK 包装器或 API 网关等服务网格可以强制执行每个应用的预算,这样单个“嘈杂的”客户端就不会使其他客户端挨饿。
- 在 429 错误上使用带抖动的指数退避(exponential backoff with jitter),这样重试的频率可以逐渐降低,并且跨实例去同步。
- 将重试窗口与配额刷新周期对齐:由于 RPM 是按 60 秒窗口强制执行的,重试发生在下一分钟开始几秒后更有可能成功。
在配额方面:
- 分析每个应用程序的 CloudWatch 指标,以确定真实的峰值 RPM,而不是仅仅依赖平均值。
- 对同一模型/区域的所有应用的峰值进行汇总,加上安全裕度,如果需要,通过 AWS 服务配额请求增加 RPM。
在前面的示例中,如果应用 A 峰值为 75 rpm,而 B 和 C 峰值为 50 rpm,您应该计划至少 175 rpm,并且现实目标是 200 rpm 左右,以便为增长和意外突发留出空间。
2. 基于令牌的限流(TPM – 每分钟令牌数)
错误消息:
botocore.errorfactory.ThrottlingException: An error occurred (ThrottlingException) when calling the InvokeModel operation: Too many tokens, please wait before trying again.
令牌限制为何重要
即使您的请求数量适中,单个大的提示或生成长输出的模型也会一次消耗数千个令牌。当每分钟处理的输入和输出令牌总数超过您账户对该模型的 TPM 配额时,就会发生基于令牌的限流。
例如,一个每分钟发送 10 个请求,每个请求包含 15,000 个输入令牌和 5,000 个输出令牌的应用程序,每分钟大约消耗 200,000 个令牌,这可能比每分钟发送 200 个微小提示的应用程序更快地超过 TPM 阈值。
在实践中它看起来像什么
您可能会注意到您的应用程序在正常工作负载下运行顺畅,但在用户粘贴大型文档、上传长篇文本记录或运行批量摘要作业时突然开始失败。这些是令牌吞吐量而非请求频率成为瓶颈的症状。
如何应对
要缓解基于令牌的限流:
- 通过跟踪 Bedrock 调用的
InputTokenCount和OutputTokenCount指标和日志来监控令牌使用情况。 - 实施令牌感知的速率限制器,该限制器维护一个滑动 60 秒的已消耗令牌窗口,并且仅在有足够的预算剩余时才发出新请求。
- 将大型任务分解成更小、更顺序的块,以便将令牌消耗分散到多个分钟,而不是在一瞬间耗尽整个预算。
- 在适当的情况下使用流式响应;流式传输通常能让您更好地控制何时停止生成,从而避免产生不必要的长输出。
对于持续高容量、令牌密集型的负载,您还应该评估请求更高的 TPM 配额或使用具有更大上下文窗口和更好吞吐量特性的模型。
3. 特定于模型的限流
错误消息:
botocore.errorfactory.ThrottlingException: An error occurred (ThrottlingException) when calling the InvokeModel operation: Model anthropic.claude-haiku-4-5-20251001-v1:0 is currently overloaded. Please try again later.
幕后发生的事情
特定于模型的限流表明特定的模型端点正经历高需求,并且为了控制延迟和稳定性而暂时限制额外流量。在这种情况下,您自己的配额可能不是限制因素;相反,该模型的共享基础设施暂时饱和了。
如何应对
这里最有效的方法之一是设计优雅降级,而不是将其视为硬性失败。
- 实施模型回退:定义兼容模型的优先级列表(例如,Sonnet → Haiku),如果主模型过载,则自动将流量路由到辅助模型。
- 将回退与跨区域推理相结合,这样如果一个区域暂时受限,您可以在附近的区域使用相同的模型系列。
- 在可观测性堆栈中暴露回退行为,以便您可以知道系统何时处于“降级但功能正常”模式,而不是默默地掩盖问题。
实施稳健的重试和速率限制
了解了限流的类型后,下一步是将该知识编码到可重用的客户端组件中。
带抖动的指数退避
这是一个使用带抖动的指数退避的稳健重试实现。此模式对于优雅地处理限流至关重要:
import time
import random
from botocore.exceptions import ClientError
def bedrock_request_with_retry(bedrock_client, operation, **kwargs):
"""安全重试实现,带有清理过的日志记录。"""
max_retries = 5
base_delay = 1
max_delay = 60
for attempt in range(max_retries):
try:
if operation == 'invoke_model':
return bedrock_client.invoke_model(**kwargs)
elif operation == 'converse':
return bedrock_client.converse(**kwargs)
except ClientError as e:
# 安全性:记录错误代码,但不记录请求/响应主体
# 因为它们可能包含敏感的客户数据
if e.response['Error']['Code'] == 'ThrottlingException':
if attempt == max_retries - 1:
raise
# 带抖动的指数退避
delay = min(base_delay * (2 ** attempt), max_delay)
jitter = random.uniform(0, delay * 0.1)
time.sleep(delay + jitter)
continue
else:
raise
此模式可避免在限流事件后立即向服务发送请求,并有助于防止许多实例在完全相同的时间重试。
令牌感知的速率限制
对于基于令牌的限流,以下类维护了一个滑动令牌使用窗口,并向调用方提供一个简单的“是/否”答案,说明是否可以发出另一个请求:
import time
from collections import deque
class TokenAwareRateLimiter:
def __init__(self, tpm_limit):
self.tpm_limit = tpm_limit
self.token_usage = deque()
def can_make_request(self, estimated_tokens):
now = time.time()
# 移除早于 1 分钟的令牌
while self.token_usage and self.token_usage[0][0] < now - 60:
self.token_usage.popleft()
current_usage = sum(tokens for _, tokens in self.token_usage)
return current_usage + estimated_tokens <= self.tpm_limit
def record_usage(self, tokens_used):
self.token_usage.append((time.time(), tokens_used))
在实践中,您需要在发送请求前估算令牌,调用 can_make_request,只有在返回 True 时才继续,然后在收到响应后调用 record_usage。
理解 503 ServiceUnavailableException
503 ServiceUnavailableException 告诉您 Amazon Bedrock 暂时无法处理您的请求,通常是由于容量压力、网络问题或连接池耗尽。与 429 不同,这与您的配额无关;这与底层服务在该时刻的健康状况或可用性有关。
连接池耗尽
看起来是这样:
botocore.errorfactory.ServiceUnavailableException: An error occurred (ServiceUnavailableException) when calling the ConverseStream operation (reached max retries: 4): Too many connections, please wait before trying again.
在许多实际场景中,此错误并非由 Bedrock 本身引起,而是由客户端的配置方式引起:
- 默认情况下,
boto3的 HTTP 连接池大小相对较小(例如,10 个连接),这很容易被高并发的工作负载耗尽。 - 为每次请求创建新的客户端而不是重用单个客户端(每个进程或容器),可能会不必要地增加打开的连接数量。
为了解决这个问题,请共享单个 Bedrock 客户端实例并增加连接池大小:
import boto3
from botocore.config import Config
# 安全最佳实践:切勿硬编码凭据
# boto3 自动使用以下凭据:
# 1. 环境变量 (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
# 2. IAM 角色(EC2、Lambda、ECS 推荐)
# 3. AWS 凭据文件 (~/.aws/credentials)
# 4. 服务账户的 IAM 角色(EKS 推荐)
# 为并行执行配置更大的连接池
config = Config(
max_pool_connections=50, # 从默认的 10 增加
retries={'max_attempts': 3}
)
bedrock_client = boto3.client('bedrock-runtime', config=config)
此配置允许通过单个、调优良好的客户端进行更多并行请求,而不是达到客户端限制。
临时服务资源问题
看起来是这样:
botocore.errorfactory.ServiceUnavailableException: An error occurred (ServiceUnavailableException) when calling the InvokeModel operation: Service temporarily unavailable, please try again.
在这种情况下,Bedrock 服务正在发出瞬态容量或基础设施问题的信号,通常是在需求高峰期影响按需模型。在这里,您应该将错误视为临时中断,并专注于智能重试和优雅地故障转移:
- 使用指数退避重试,类似于 429 处理,但参数应针对较慢的恢复进行调整。
- 考虑使用跨区域推理或不同的服务层级,以便为您最关键的工作负载获得更可预测的容量包络。
高级弹性策略
当您运行任务关键型系统时,简单的重试是不够的;您还希望避免使情况恶化。
断路器模式
断路器模式有助于防止您的应用程序持续调用已失败的服务。相反,在重复失败后,它会迅速切换到“打开”状态,并在冷却期间阻止新请求。
- CLOSED (关闭) (正常): 请求正常流动。
- OPEN (打开) (失败): 在重复失败后,新请求立即被拒绝,有助于减轻服务压力并节省客户端资源。
- HALF_OPEN (半开) (测试): 超时后,允许少量试探性请求;如果成功,电路再次关闭。
这对 Bedrock 为什么重要
当 Bedrock 由于容量问题返回 503 错误时,继续用请求猛击服务只会使情况更糟。断路器模式有助于:
- 减少挣扎中的服务的负载,帮助它更快恢复
- 快速失败而不是浪费时间在可能失败的请求上
- 通过定期测试服务是否再次健康来提供自动恢复
- 通过快速返回错误而不是超时来改善用户体验
以下代码实现了这一点:
import time
from enum import Enum
class CircuitState(Enum):
CLOSED = "closed" # 正常操作
OPEN = "open" # 失败,拒绝请求
HALF_OPEN = "half_open" # 测试服务是否恢复
class CircuitBreaker:
def __init__(self, failure_threshold=5, timeout=60):
self.failure_threshold = failure_threshold
self.timeout = timeout
self.failure_count = 0
self.last_failure_time = None
self.state = CircuitState.CLOSED
def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
if time.time() - self.last_failure_time > self.timeout:
self.state = CircuitState.HALF_OPEN
else:
raise Exception("Circuit breaker is OPEN")
try:
result = func(*args, **kwargs)
self.on_success()
return result
except Exception as e:
self.on_failure()
raise
def on_success(self):
self.failure_count = 0
self.state = CircuitState.CLOSED
def on_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = CircuitState.OPEN
# 用法
circuit_breaker = CircuitBreaker()
def make_bedrock_request():
return circuit_breaker.call(bedrock_client.invoke_model, **request_params)
带有 CRIS 的跨区域故障转移策略
Amazon Bedrock 跨区域推理 (CRIS) 通过提供一种托管方式在区域之间路由流量,从而增加了一层弹性。
- 全局 CRIS 配置文件:可以将流量发送到 AWS 商业区域,通常提供吞吐量和成本的最佳组合(通常节省约 10%)。
- 地理 CRIS 配置文件:CRIS 配置文件将流量限制在特定地理区域内(例如,仅限美国、仅限欧盟、仅限 APAC),以帮助满足严格的数据驻留或监管要求。
对于没有数据驻留要求的应用程序,全局 CRIS 可以提高可用性、可靠性和成本效率。
从架构角度来看:
- 对于不受监管的工作负载,使用全局配置文件可以显著提高可用性并吸收区域性高峰。
- 对于受监管的工作负载,配置与您的合规边界对齐的地理配置文件,并在治理工件中记录这些决定。
Bedrock 默认使用 TLS 在传输中加密数据,并且默认不存储客户提示或输出;将此与 CloudTrail 日志记录相结合,以实现合规性态势。
429 和 503 错误的监控和可观测性
您无法管理看不见的东西,因此在处理由配额驱动的错误和服务可用性时,强大的监控至关重要。设置全面的 Amazon CloudWatch 监控对于主动错误管理和保持应用程序可靠性至关重要。
注意: CloudWatch 自定义指标、警报和仪表板会根据使用情况产生费用。请参阅CloudWatch 定价了解详情。
必要的 CloudWatch 指标
监控以下 CloudWatch 指标:
- Invocations:成功的模型调用
- InvocationClientErrors:4xx 错误,包括限流
- InvocationServerErrors:5xx 错误,包括服务不可用
- InvocationThrottles:429 限流错误
- InvocationLatency:响应时间
- InputTokenCount/OutputTokenCount:用于 TPM 监控的令牌使用情况
为了获得更好的洞察力,请创建以下仪表板:
- 将 429 和 503 分开到不同的窗口小部件中,这样您就可以看到高峰是与配额相关的还是与服务相关的。
- 按
ModelId和Region细分指标,以找到出现问题的特定模型或区域。 - 并排显示当前流量与前几周的比较,以便在问题成为事件之前发现新兴趋势。
关键警报
不要等到用户发现故障时才采取行动。根据以下阈值配置基于 Amazon SNS 通知(Amazon Simple Notification Service)的 CloudWatch 警报:
对于 429 错误:
- 在 5 分钟窗口内出现大量限流事件。
- 连续出现非零限流计数,表明持续的压力。
- 配额利用率高于选定阈值(例如,RPM/TPM 的 80%)。
对于 503 错误:
- 服务成功率低于您的 SLO(例如,10 分钟内低于 95%)。
- 与特定区域或模型相关的 503 计数突然激增。
- 服务可用性(例如,<95% 的成功率)
- 客户端指标上出现连接池饱和的迹象。
警报配置最佳实践
- 使用 Amazon Simple Notification Service (Amazon SNS) 主题将警报路由到团队的通信渠道(Slack、PagerDuty、电子邮件)
- 设置不同的严重性级别:关键(立即采取行动)、警告(尽快调查)、信息(趋势问题)
- 配置警报操作以在适当情况下触发自动化响应
- 在详细的警报描述中包含故障排除步骤和运行手册链接
- 定期测试您的警报以确保通知正常工作
- 不要在警报消息中包含敏感客户数据
日志分析查询
CloudWatch Logs Insights 查询可帮助您从“我们看到了错误”转变为“我们了解了模式”。示例如下:
查找 429 错误模式:
fields @timestamp, @message
| filter @message like /ThrottlingException/
| stats count() by bin(5m)
| sort @timestamp desc
分析 503 错误与请求量的相关性:
fields @timestamp, @message
| filter @message like /ServiceUnavailableException/
| stats count() as error_count by bin(1m)
| sort @timestamp desc
总结:构建弹性应用
我们在本文中涵盖了很多内容,让我们把它们整合起来。成功处理 Bedrock 错误需要:
- 理解根本原因:区分配额限制(429)和容量问题(503)
- 实施适当的重试:对每种错误类型使用带有不同参数的指数退避
- 为规模化设计:使用连接池、断路器和跨区域故障转移
- 主动监控:设置全面的 CloudWatch 监控和警报
- 规划增长:请求配额增加并实施回退策略
结论
有效处理 429 ThrottlingException 和 503 ServiceUnavailableException 错误是使用 Amazon Bedrock 运行生产级生成式 AI 工作负载的关键部分。通过结合配额感知的设计、智能重试、客户端弹性模式、跨区域策略和强大的可观测性,即使在不可预测的负载下,您也可以保持应用程序的响应能力。
作为下一步,请确定您最关键的 Bedrock 工作负载,启用此处描述的重试和速率限制模式,并构建仪表板和警报,以显示您的真实峰值而不是仅仅显示平均值。随着时间的推移,使用真实流量数据来完善配额、回退模型和区域部署,以便您的 AI 系统在扩展时也能保持强大和可靠。
对于希望加速事件解决的团队,请考虑启用 AWS DevOps Agent——一个人工智能驱动的代理,它通过关联 CloudWatch 指标、日志和警报来调查 Bedrock 错误,就像经验丰富的 DevOps 工程师一样。它会学习您的资源关系,与您的可观测性工具和运行手册配合工作,并通过自动识别根本原因和建议的补救措施,显著减少 429 和 503 错误的平均解决时间 (MTTR)。
了解更多
- Amazon Bedrock 文档
- Amazon Bedrock 配额
- 跨区域推理
- 跨区域推理安全
- SNS 安全性
- AWS 日志记录最佳实践
- AWS Bedrock 安全最佳实践
- AWS IAM 最佳实践 – 最小权限
关于作者
🚀 想要体验更好更全面的AI调用?
欢迎使用青云聚合API,约为官网价格的十分之一,支持300+全球最新模型,以及全球各种生图生视频模型,无需翻墙高速稳定,文档丰富,小白也可以简单操作。
评论区