用 Amazon Bedrock 和 LLM Gateway 给生成式 AI 应用加上韧性

2026-07-01 24 预计阅读时间: 1 分钟
来源: aws.amazon.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:11 分钟

生成式 AI 应用上线后,最先暴露的往往不是 prompt 写得好不好,而是系统能不能扛住流量、限额和依赖波动:突发访问把模型配额打满,单一区域推理不可用,多租户场景里某个客户把大家的吞吐吃光。围绕 Amazon Bedrock,可以从托管能力开始做韧性设计,再逐步引入 LLM gateway,把多模型、多区域、多租户策略收敛到统一入口。

下面按工程落地顺序拆成五个模式。

1. 先把 Bedrock 调用做成“可失败”的客户端

很多团队第一次接入 LLM 时,会把模型调用当成普通 HTTP API:请求、等待、返回。但 LLM 推理的失败形态更多:限流、超时、上游繁忙、输出过长、模型暂时不可用。客户端层应该显式处理这些情况。

可以这样实践:

  • 设置超时,避免请求长时间挂住。
  • 对可重试错误做指数退避。
  • 区分用户错误和系统错误,prompt 太长、模型 ID 错误不应盲目重试。
  • 记录 tenant、model、region、latency、retry_count,后续才能做容量判断。

下面是一个最小可改造的 Python 示例:优先调用主区域模型,遇到限流或临时故障时退避重试,再切到备用区域。

运行前需要:

  • 配好 AWS 凭证和权限。
  • 安装 boto3
  • MODEL_ID 换成你账号已启用的 Bedrock 模型 ID。
import json
import random
import time
from typing import Dict, Any

import boto3
from botocore.config import Config
from botocore.exceptions import ClientError, ReadTimeoutError, ConnectTimeoutError

MODEL_ID = 'anthropic.claude-3-haiku-20240307-v1:0'
REGIONS = ['us-east-1', 'us-west-2']

RETRYABLE_CODES = {
    'ThrottlingException',
    'TooManyRequestsException',
    'ServiceUnavailableException',
    'InternalServerException',
    'ModelTimeoutException',
}


def bedrock_client(region: str):
    return boto3.client(
        'bedrock-runtime',
        region_name=region,
        config=Config(
            connect_timeout=3,
            read_timeout=30,
            retries={'max_attempts': 1},  # 自己控制重试,避免多层重试放大流量
        ),
    )


def build_body(prompt: str) -> Dict[str, Any]:
    # 示例以 Anthropic Claude Messages API 形态演示;其他模型需要替换 body schema。
    return {
        'anthropic_version': 'bedrock-2023-05-31',
        'max_tokens': 300,
        'messages': [
            {
                'role': 'user',
                'content': [{'type': 'text', 'text': prompt}],
            }
        ],
    }


def invoke_with_resilience(prompt: str, max_attempts_per_region: int = 3) -> str:
    last_error = None

    for region in REGIONS:
        client = bedrock_client(region)

        for attempt in range(1, max_attempts_per_region + 1):
            started = time.time()
            try:
                resp = client.invoke_model(
                    modelId=MODEL_ID,
                    body=json.dumps(build_body(prompt)),
                    contentType='application/json',
                    accept='application/json',
                )
                payload = json.loads(resp['body'].read())
                latency_ms = int((time.time() - started) * 1000)
                print(f'region={region} attempt={attempt} latency_ms={latency_ms}')
                return payload['content'][0]['text']

            except (ReadTimeoutError, ConnectTimeoutError) as exc:
                last_error = exc
                retryable = True
            except ClientError as exc:
                last_error = exc
                code = exc.response.get('Error', {}).get('Code', 'Unknown')
                retryable = code in RETRYABLE_CODES
                print(f'region={region} attempt={attempt} error_code={code}')
                if not retryable:
                    raise

            if attempt < max_attempts_per_region and retryable:
                sleep_seconds = min(2 ** attempt, 8) + random.random()
                time.sleep(sleep_seconds)

        print(f'failover_to_next_region from={region}')

    raise RuntimeError(f'All Bedrock regions failed: {last_error}')


if __name__ == '__main__':
    answer = invoke_with_resilience('用三句话解释为什么 LLM 调用需要重试和降级。')
    print(answer)

这个示例不是完整网关,但它体现了韧性设计的底线:失败是正常路径,不是异常路径。

2. 用多区域推理降低单点风险

当流量突然上涨时,单一区域的模型配额可能先被打满。摘要中提到的一个核心问题就是 quota exhaustion:不是代码坏了,而是你在某个区域、某个模型、某个账号维度的可用吞吐不够。

在 AWS 上可以优先利用 Amazon Bedrock 的原生能力做地域分布和推理弹性;如果业务对可用性要求更高,还可以在应用层维护区域优先级和健康状态。

工程上建议把区域策略做成配置,而不是写死在代码里:

# llm-routing.yaml
models:
  chat-fast:
    primary:
      provider: bedrock
      region: us-east-1
      model_id: anthropic.claude-3-haiku-20240307-v1:0
    fallbacks:
      - provider: bedrock
        region: us-west-2
        model_id: anthropic.claude-3-haiku-20240307-v1:0
    timeout_ms: 30000
    retry:
      max_attempts: 2
      backoff_ms: 500

这种配置化的好处是:

  • 变更备用区域时不需要重新发版。
  • 可以给不同业务线配置不同模型和区域。
  • 方便后续接入 LLM gateway,由网关读取同一套路由策略。

需要注意,多区域不是免费午餐。跨区域调用会引入额外延迟、数据驻留和合规问题。对含敏感数据的 prompt,应先确认数据是否允许离开当前区域。

3. 用容量隔离处理“吵闹邻居”

多租户应用里最容易出现 noisy neighbor:一个租户做批量任务,导致其他租户的在线请求也被限流。仅靠全局重试会让问题更糟,因为所有租户都在失败后继续放大请求。

更稳妥的做法是把限额放在业务边界上:

  • 按 tenant 设置 QPS、并发数或 token 预算。
  • 在线请求和后台批处理分池。
  • 高优先级租户使用独立路由或预留容量。
  • 当租户超过预算时,返回明确的 429,而不是拖慢所有人。

可以这样在网关前做一个简单的租户限流配置:

# tenant-limits.yaml
tenants:
  free:
    max_concurrent_requests: 2
    requests_per_minute: 30
    monthly_token_budget: 1000000
  pro:
    max_concurrent_requests: 10
    requests_per_minute: 300
    monthly_token_budget: 20000000
  enterprise-a:
    max_concurrent_requests: 50
    requests_per_minute: 2000
    monthly_token_budget: 500000000
    preferred_route: chat-fast-dedicated

这类策略的关键不是 YAML,而是观测维度:每次调用都要带上 tenant id、route id、model id、region 和 token usage。否则你只能看到“Bedrock 被限流了”,看不到是谁制造了尖峰。

4. 把降级路径设计成产品能力,而不是事故脚本

LLM 应用的降级不应该只有“报错”。根据场景不同,可以准备不同级别的退路:

  • 从大模型降到小模型,牺牲部分质量换取可用性。
  • 从跨区域备用模型返回结果。
  • 对非实时任务进入队列,稍后处理。
  • 对可缓存问题返回最近一次结果。
  • 对低优先级功能关闭生成,只保留检索或规则结果。

例如客服总结、标签生成、文档分类这类任务,很多时候可以接受延迟或较小模型;但交易确认、医疗建议、合规审查等场景不能随意降级,需要更严格的策略和人工兜底。

一个实用判断标准是:如果降级结果可能被用户当成高置信事实,就不要静默降级。要么标注“系统繁忙,已使用简化模型”,要么直接失败并提示重试。

5. 用 LLM Gateway 统一多模型编排

当应用只有一个服务、一个模型时,客户端重试足够。但一旦进入多模型、多区域、多租户阶段,韧性逻辑散落在各个业务服务里会非常难维护。

LLM gateway 的价值在这里体现出来:它把调用策略从业务代码中抽离出来,集中处理:

  • 模型路由和 fallback。
  • per-tenant 限流和预算。
  • 熔断、重试、超时。
  • 请求日志、token 计量和审计。
  • 多模型供应商或多区域调度。

可以这样设计一个内部调用协议,让业务服务不直接关心 Bedrock 细节:

curl -X POST http://localhost:8080/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -H 'X-Tenant-Id: enterprise-a' \
  -d '{
    "route": "chat-fast",
    "messages": [
      {"role": "user", "content": "把这段客服对话总结成三个要点。"}
    ],
    "max_tokens": 300
  }'

网关后面可以先只接 Amazon Bedrock。等业务增长后,再把多区域路由、不同模型档位、租户隔离逐步加进去。不要一开始就做成复杂平台,否则网关本身会成为新的单点和运维负担。

落地检查清单

把 Bedrock 应用做稳,可以按这个顺序推进:

  • 客户端有超时、退避重试和错误分类。
  • 模型、区域、超时、fallback 都配置化。
  • 关键指标按 tenant、model、region 维度打点。
  • 对 quota exhaustion 有预案:备用区域、备用模型或排队。
  • 多租户系统有租户级预算和并发隔离。
  • 降级行为经过产品确认,不让用户误解结果质量。
  • 当调用策略开始复制到多个服务时,引入 LLM gateway。

韧性不是把错误全部藏起来,而是让系统在配额、区域和模型波动时仍然可控。Amazon Bedrock 提供了托管推理基础,LLM gateway 则把更复杂的路由和隔离策略变成工程化能力。两者配合,生成式 AI 应用才更接近可生产、可运营的系统。


相关推荐