饮墨

子安饮墨馀三斗,留与卿儿作赋来

AWS Bedrock 生产级 AI Agent 部署:3 个必踩的坑与解法

13 views

痛点

你的 AI Agent 在本地跑得很顺,调用 OpenAI API 响应飞快。老板一句"上生产",你开始考虑:API Key 泄露风险、调用成本失控、跨境网络延迟……

AWS Bedrock 作为托管式大模型服务,解决了 API Key 管理和网络问题,但生产部署时坑也不少。本文分享 3 个真实踩坑场景和可落地的解决方案。

方案概述

问题 本地开发 Bedrock 生产方案
认证 API Key 硬编码 IAM Role + STS 临时凭证
成本 按 Token 后付费 Provisioned Throughput + 预算告警
延迟 单区域 多区域 Failover + 流式响应

实操步骤

第一步:IAM 权限最小化配置

很多人图省事给 bedrock:*,生产环境绝对不行。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "BedrockInvokeOnly",
      "Effect": "Allow",
      "Action": [
        "bedrock:InvokeModel",
        "bedrock:InvokeModelWithResponseStream"
      ],
      "Resource": [
        "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-*",
        "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-*"
      ]
    }
  ]
}

关键点: - 只开 InvokeModel 和流式调用权限 - Resource 限定具体模型,不要用 * - 多区域部署时,把备用区域也加进去

第二步:Python 客户端封装

import boto3
from botocore.config import Config
import json

class BedrockAgent:
    def __init__(self, region="us-east-1", fallback_region="us-west-2"):
        self.config = Config(
            retries={"max_attempts": 3, "mode": "adaptive"},
            connect_timeout=5,
            read_timeout=60,
        )
        self.primary = boto3.client(
            "bedrock-runtime", 
            region_name=region, 
            config=self.config
        )
        self.fallback = boto3.client(
            "bedrock-runtime", 
            region_name=fallback_region, 
            config=self.config
        )

    def invoke(self, prompt: str, model_id: str = "anthropic.claude-3-sonnet-20240229-v1:0"):
        body = json.dumps({
            "anthropic_version": "bedrock-2023-05-31",
            "max_tokens": 4096,
            "messages": [{"role": "user", "content": prompt}]
        })

        try:
            response = self.primary.invoke_model(
                modelId=model_id,
                body=body,
                contentType="application/json"
            )
        except self.primary.exceptions.ThrottlingException:
            # 主区域限流,自动切备用区
            response = self.fallback.invoke_model(
                modelId=model_id,
                body=body,
                contentType="application/json"
            )

        result = json.loads(response["body"].read())
        return result["content"][0]["text"]

第三步:成本控制三板斧

1. 设置预算告警

aws budgets create-budget \
  --account-id 123456789012 \
  --budget '{
    "BudgetName": "bedrock-monthly",
    "BudgetLimit": {"Amount": "500", "Unit": "USD"},
    "TimeUnit": "MONTHLY",
    "BudgetType": "COST",
    "CostFilters": {"Service": ["Amazon Bedrock"]}
  }' \
  --notifications-with-subscribers '[{
    "Notification": {
      "NotificationType": "ACTUAL",
      "ComparisonOperator": "GREATER_THAN",
      "Threshold": 80
    },
    "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "ops@example.com"}]
  }]'

2. Token 用量监控

import boto3
from datetime import datetime, timedelta

cloudwatch = boto3.client("cloudwatch")

def get_token_usage(hours=24):
    response = cloudwatch.get_metric_statistics(
        Namespace="AWS/Bedrock",
        MetricName="InputTokenCount",
        Dimensions=[{"Name": "ModelId", "Value": "anthropic.claude-3-sonnet-20240229-v1:0"}],
        StartTime=datetime.utcnow() - timedelta(hours=hours),
        EndTime=datetime.utcnow(),
        Period=3600,
        Statistics=["Sum"]
    )
    return sum(dp["Sum"] for dp in response["Datapoints"])

3. Provisioned Throughput(高频场景)

如果日调用量稳定超过 10 万次,购买预置吞吐量可省 30-50%:

aws bedrock create-provisioned-model-throughput \
  --model-units 1 \
  --provisioned-model-name "claude-prod" \
  --model-id "anthropic.claude-3-sonnet-20240229-v1:0"

3 个常见坑

坑 1:ThrottlingException 没处理好

Bedrock 按区域限流,默认配额不高。很多人只配了重试,没配跨区 Failover,一限流业务就挂。

解法: 上面代码的 fallback 客户端就是应对方案,同时提前申请提升配额:

aws service-quotas request-service-quota-increase \
  --service-code bedrock \
  --quota-code L-XXXXXXXX \
  --desired-value 100

坑 2:流式响应超时

Agent 场景经常需要流式输出,但 ALB 默认 60 秒超时,长对话容易断。

解法: 调整 ALB idle timeout,并在客户端用 invoke_model_with_response_stream:

def invoke_stream(self, prompt: str, model_id: str):
    body = json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 4096,
        "messages": [{"role": "user", "content": prompt}]
    })

    response = self.primary.invoke_model_with_response_stream(
        modelId=model_id,
        body=body
    )

    for event in response["body"]:
        chunk = json.loads(event["chunk"]["bytes"])
        if chunk["type"] == "content_block_delta":
            yield chunk["delta"]["text"]

坑 3:跨账号调用权限混乱

多团队共用 Bedrock 时,经常出现 A 团队的 Agent 误用了 B 团队的配额。

解法: 用 Resource Tag 隔离 + SCP 策略限制:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Deny",
    "Action": "bedrock:InvokeModel",
    "Resource": "*",
    "Condition": {
      "StringNotEquals": {
        "aws:ResourceTag/Team": "${aws:PrincipalTag/Team}"
      }
    }
  }]
}

总结

AWS Bedrock 生产部署的核心要点:

  1. 权限最小化 — 只开 InvokeModel,Resource 限定到具体模型
  2. 多区域 Failover — 主区域限流自动切备用区
  3. 成本可控 — 预算告警 + Token 监控 + 高频场景用 Provisioned Throughput
  4. 流式响应 — 长对话场景必须用 invoke_model_with_response_stream

比起直接调用 OpenAI API,Bedrock 的优势在于:无需管理 API Key、天然支持 IAM 权限体系、网络走 AWS 内网。但代价是配置复杂度更高,限流和配额需要提前规划。

如果你的 Agent 已经在生产跑了,建议先加上成本监控,很多团队第一个月账单出来才发现 Token 用量超预期 3 倍。

分享:

相关文章