用三层架构在 Bedrock 上搭建自驱式 AI 运维告警系统

2026-06-04 18 预计阅读时间:1 分钟
来源:aws.amazon.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:11 分钟

大规模运行 Bedrock 上的生成式 AI 应用时,运维团队很快会遇到一个棘手问题:告警太多、阈值太死、工单重复创建,SRE 散落在各处的通知缺乏上下文。Amazon Bedrock Ops Alert 正是为了解决这些痛点而设计的三层自动化监控方案——它不只是"检测→通知"的流水线,而是把阈值自适应、告警分类、工单去重和上下文推送串成一条自驱闭环。

三层架构拆解

整个方案分为三个职责明确的层级,每一层都可以独立替换或扩展。

第一层:智能检测与阈值自适应

传统监控用固定阈值,但 Bedrock 推理延迟、Token 消耗、模型调用错误率这些指标天然随负载波动。Ops Alert 的第一层引入了动态基线计算:

  • 基于历史滑动窗口自动计算正常区间,无需手动设定上下限。
  • 当指标偏离动态基线时触发原始告警事件,写入 EventBridge。
  • 同时支持手动覆盖——某些关键指标(如模型调用 5xx 错误率)可以设硬阈值兜底。

这意味着你不再需要为"延迟到底该设 2 秒还是 5 秒"反复调参,系统会根据过去 N 小时的真实表现自行判断。

第二层:告警分类与工单去重

原始告警进入第二层后,Bedrock 上的大模型做两件事:

  1. 分类归档——根据告警内容(涉及哪个模型、哪类资源、什么症状)自动打上类别标签,比如 ThrottlingLatencySpikeTokenBudgetExhaustion
  2. 去重判断——查询现有工单库,如果同一类别下已有未解决工单,新告警会追加到已有工单而非新建一条。这直接解决了"同一个限流问题一晚上开了 15 个工单"的灾难。

分类结果和去重决策都通过 Lambda 函数执行,模型只负责理解和推荐动作。

第三层:上下文通知与工单创建

第三层是"最后一公里":

  • 自动创建 Support Case,工单描述中包含:触发时间、指标数值、动态基线参考值、相关模型 ID、受影响的 Region。
  • 通过 SNS 或 Slack Webhook 推送通知给 AI SRE 团队,通知内容不是冷冰冰的"CPU > 90%",而是"us-east-1 区域 Claude 3 调用延迟升至 4.2s,基线为 1.8s,已有工单 #42 正在处理"。

SRE 收到通知后可以直接跳转工单,不需要再去翻仪表盘拼凑上下文。

部署实战:用 CloudFormation 一键拉起

下面给出一个最小可运行的 CloudFormation 模板,部署核心的 EventBridge 规则、Lambda 函数和 SNS 通知通道。实际生产环境中你还需要加上 DynamoDB 工单表和 Bedrock 调用层,但这个骨架足以验证三层架构的流转逻辑。

AWSTemplateFormatVersion: '2010-09-09'
Description: Bedrock Ops Alert - minimal three-layer skeleton

Parameters:
  SnsEmail:
    Type: String
    Description: SRE team email for notifications
    Default: sre-team@example.com

  BedrockModelId:
    Type: String
    Description: Bedrock model used for alarm classification
    Default: anthropic.claude-3-haiku-20240307-v1:0

Resources:

  # ── Layer 1: EventBridge rule for Bedrock operational events ──
  OpsAlarmBus:
    Type: AWS::Events::EventBus
    Properties:
      Name: bedrock-ops-alarm-bus

  HighLatencyRule:
    Type: AWS::Events::Rule
    Properties:
      EventBusName: !Ref OpsAlarmBus
      Description: Detect Bedrock invocation latency spikes
      EventPattern:
        source:
          - aws.bedrock
        detail-type:
          - Bedrock Invocation Metric
        detail:
          metricName:
            - InvocationLatency
          value:
            - greaterThan: 3000   # hard threshold fallback (ms)
      Targets:
        - Arn: !GetAtt ClassifyAlarmFunction.Arn
          Id: ClassifyTarget

  # ── Layer 2: Lambda that calls Bedrock to classify & deduplicate ──
  ClassifyAlarmFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: bedrock-ops-alert-classify
      Runtime: python3.12
      Handler: index.handler
      Timeout: 30
      Role: !GetAtt LambdaExecRole.Arn
      Environment:
        Variables:
          MODEL_ID: !Ref BedrockModelId
          DEDUP_TABLE: !Ref AlarmDedupTable
      Code:
        ZipFile: |
          import json, os, boto3, urllib.parse

          bedrock = boto3.client('bedrock-runtime')
          dynamodb = boto3.resource('dynamodb')
          table = dynamodb.Table(os.environ['DEDUP_TABLE'])
          MODEL_ID = os.environ['MODEL_ID']

          def handler(event, context):
              # Extract alarm detail
              detail = event.get('detail', {})
              metric = detail.get('metricName', 'Unknown')
              value = detail.get('value', 0)
              model_id = detail.get('modelId', 'unknown')

              # ── Call Bedrock to classify ──
              prompt = f"""Classify this Bedrock operational alarm into one of:
              Throttling, LatencySpike, TokenBudgetExhaustion, ModelError, Other.
              Alarm: metric={metric}, value={value}, model={model_id}.
              Reply with only the category name."""

              body = json.dumps({
                  "anthropic_version": "bedrock-2023-05-31",
                  "max_tokens": 20,
                  "messages": [{"role": "user", "content": prompt}]
              })

              resp = bedrock.invoke_model(
                  modelId=MODEL_ID,
                  body=body,
                  contentType='application/json',
                  accept='application/json'
              )
              category = json.loads(resp['body'].read())['content'][0]['text'].strip()

              # ── Dedup check ──
              existing = table.get_item(Key={'category': category, 'status': 'OPEN'})
              if 'Item' in existing:
                  # Append to existing case instead of creating new one
                  case_id = existing['Item']['caseId']
                  action = 'APPEND'
              else:
                  case_id = f"CASE-{category}-{context.aws_request_id[:8]}"
                  table.put_item(Item={
                      'category': category,
                      'status': 'OPEN',
                      'caseId': case_id,
                      'modelId': model_id,
                      'metricValue': str(value)
                  })
                  action = 'CREATE'

              # ── Return enriched alarm for Layer 3 ──
              return {
                  'category': category,
                  'action': action,
                  'caseId': case_id,
                  'metric': metric,
                  'value': value,
                  'modelId': model_id
              }

  LambdaExecRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: BedrockAndDynamo
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - bedrock:InvokeModel
                Resource: !Sub 'arn:aws:bedrock:${AWS::Region}::model/${BedrockModelId}'
              - Effect: Allow
                Action:
                  - dynamodb:GetItem
                  - dynamodb:PutItem
                  - dynamodb:UpdateItem
                Resource: !GetAtt AlarmDedupTable.Arn

  # ── Dedup state table ──
  AlarmDedupTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: bedrock-ops-alarm-dedup
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: category
          AttributeType: S
        - AttributeName: status
          AttributeType: S
      KeySchema:
        - AttributeName: category
          KeyType: HASH
        - AttributeName: status
          KeyType: RANGE

  # ── Layer 3: SNS notification topic ──
  SreNotificationTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: bedrock-ops-alert-notifications
      DisplayName: Bedrock Ops Alert SRE Channel

  SreSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      TopicArn: !Ref SreNotificationTopic
      Protocol: email
      Endpoint: !Ref SnsEmail

  # ── Lambda permission for EventBridge ──
  ClassifyAlarmPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref ClassifyAlarmFunction
      Action: lambda:InvokeFunction
      Principal: events.amazonaws.com
      SourceArn: !GetAtt HighLatencyRule.Arn

Outputs:
  EventBusName:
    Value: !Ref OpsAlarmBus
  SnsTopicArn:
    Value: !Ref SreNotificationTopic
  LambdaFunctionArn:
    Value: !GetAtt ClassifyAlarmFunction.Arn

部署命令:

# 替换为你自己的 SRE 邢箱
aws cloudformation deploy \
  --template-file bedrock-ops-alert.yaml \
  --stack-name bedrock-ops-alert \
  --parameter-overrides SnsEmail=your-sre@company.com \
  --capabilities CAPABILITY_IAM \
  --region us-east-1

# 部署完成后,手动发一条测试告警验证流转
aws events put-events \
  --entries '[{
    "eventBusName": "bedrock-ops-alert-bus",
    "source": "aws.bedrock",
    "detailType": "Bedrock Invocation Metric",
    "detail": "{\"metricName\":\"InvocationLatency\",\"value\":4200,\"modelId\":\"anthropic.claude-3-haiku\"}"
  }]'

注意:SNS 邢箱订阅需要收件人点击确认链接才能激活。部署后检查你的邮箱。

几个落地时的关键决策点

模型选型:分类用 Haiku 就够了

告警分类是低延迟、短输出的任务,用 Claude 3 Haiku(或同等轻量模型)性价比最高。不需要 Sonnet 或 Opus——每条告警的分类推理成本控制在几分钱,日处理上千条告警也不会把 Bedrock 费用拉爆。

去重窗口要设 TTL

DynamoDB 去重表中的 OPEN 状态记录必须设过期时间(比如 24 小时)。否则一旦工单在外部系统被关闭但状态没同步回来,后续同类告警会永远被错误地归为"追加"。生产部署时建议在 Lambda 中加上 update_item 调用,定期同步外部工单系统的关闭状态。

动态基线 vs 硬阈值的取舍

第一层的动态基线适合波动型指标(延迟、吞吐),但错误率类指标建议保留硬阈值。5xx 错误率从 0.1% 跳到 2% 在任何基线下都该立即告警,不该等"历史窗口认为还行"。

通知渠道不要只靠邮件

SNS 邮件是最低门槛的通道,但生产环境建议同时接入 Slack / PagerDuty / Teams Webhook。可以在第三层 Lambda 中根据告警类别路由:Throttling 走 Slack 频道,ModelError 跑 PagerDuty 自动升级,TokenBudgetExhaustion 只发邮件摘要。

上线前的检查清单

检查项 说明
Bedrock 模型访问权限 确认账号已启用对应模型的访问,否则 invoke_model 会 403
EventBridge 事件源 如果 Bedrock 指标来自 CloudWatch 而非原生事件,需要加 CloudWatch → EventBridge 桥接
DynamoDB TTL 去重表必须开启 TTL 属性,防止僵尸记录阻塞新工单
Lambda 超时 分类推理 + DynamoDB 写入,设 30 秒足够;如果模型响应慢可调到 60
SNS 确认 部署后逐个确认订阅,否则通知静默丢失
工单系统对接 如果用 AWS Support Center 而非自建工单库,需替换 DynamoDB 为 Support Case API 调用

三层架构的核心价值不在于某个单点功能,而是把"检测→理解→行动"串成一条不需要人工介入的闭环。SRE 的角色从"逐条看告警、手动开工单"变成"审核模型分类结果、处理真正需要判断力的异常"。先从延迟和限流两类告警跑通闭环,再逐步扩展到 Token 预算和模型错误——这是最稳的推进路径。


相关推荐