大规模运行 Bedrock 上的生成式 AI 应用时,运维团队很快会遇到一个棘手问题:告警太多、阈值太死、工单重复创建,SRE 散落在各处的通知缺乏上下文。Amazon Bedrock Ops Alert 正是为了解决这些痛点而设计的三层自动化监控方案——它不只是"检测→通知"的流水线,而是把阈值自适应、告警分类、工单去重和上下文推送串成一条自驱闭环。
三层架构拆解
整个方案分为三个职责明确的层级,每一层都可以独立替换或扩展。
第一层:智能检测与阈值自适应
传统监控用固定阈值,但 Bedrock 推理延迟、Token 消耗、模型调用错误率这些指标天然随负载波动。Ops Alert 的第一层引入了动态基线计算:
- 基于历史滑动窗口自动计算正常区间,无需手动设定上下限。
- 当指标偏离动态基线时触发原始告警事件,写入 EventBridge。
- 同时支持手动覆盖——某些关键指标(如模型调用 5xx 错误率)可以设硬阈值兜底。
这意味着你不再需要为"延迟到底该设 2 秒还是 5 秒"反复调参,系统会根据过去 N 小时的真实表现自行判断。
第二层:告警分类与工单去重
原始告警进入第二层后,Bedrock 上的大模型做两件事:
- 分类归档——根据告警内容(涉及哪个模型、哪类资源、什么症状)自动打上类别标签,比如
Throttling、LatencySpike、TokenBudgetExhaustion。 - 去重判断——查询现有工单库,如果同一类别下已有未解决工单,新告警会追加到已有工单而非新建一条。这直接解决了"同一个限流问题一晚上开了 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 预算和模型错误——这是最稳的推进路径。