用 Amazon Bedrock 构建可审计、受策略约束的灾难恢复智能体

2026-09-05 41 预计阅读时间: 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 分钟

大规模灾难恢复的难点,不只是把流量切到备用区域,而是在高压环境下同时保证决策正确、操作可控、过程可追溯。Intuit 构建的 EWOK Agent 展示了一种实践方向:值班工程师用自然语言提出生产故障转移请求,智能体负责理解意图和协调执行,同时让每一步操作保持可审计、符合策略并具备安全边界。

这类系统的关键不是让大模型直接操作生产环境,而是把它放在受约束的控制平面里:模型负责解释请求和生成计划,确定性代码负责检查、审批和执行。

自然语言只是入口,不是授权凭证

一条看似简单的请求可能是:

将支付服务从 us-east-1 故障转移到 us-west-2,变更单号 CHG-4821。

智能体需要把它转换成机器可验证的结构,例如:

{
  "service": "payments",
  "source_region": "us-east-1",
  "target_region": "us-west-2",
  "change_ticket": "CHG-4821",
  "requested_action": "failover"
}

结构化并不等于获准执行。后续仍应由传统程序完成以下检查:

  • 服务和目标区域是否在允许列表中。
  • 调用者是否拥有生产故障转移权限。
  • 变更单是否有效,是否处于允许执行的时间窗口。
  • 备用区域的容量、复制延迟和健康检查是否达标。
  • 当前是否存在冻结窗口或冲突中的部署。
  • 高风险操作是否已经获得双人审批。

这种分工缩小了模型的权限范围。即使模型误解请求,它也只能生成候选计划,不能绕过身份认证、策略引擎或执行器。

把恢复过程设计成有状态工作流

生产故障转移通常不是一次 API 调用,而是一条包含等待、验证和回滚的状态机。可以将流程拆成以下阶段:

  1. 解析请求:Bedrock 将自然语言转换成固定 schema。
  2. 收集证据:查询服务目录、监控指标、复制状态和变更系统。
  3. 生成计划:列出预检查、流量切换、验证及回滚步骤。
  4. 策略判定:由确定性规则决定拒绝、等待审批或允许执行。
  5. 人工确认:向值班工程师展示目标环境、风险和影响范围。
  6. 受控执行:调用预先注册的 runbook,而不是执行模型生成的任意命令。
  7. 持续验证:检查错误率、延迟、依赖健康度和业务指标。
  8. 完成或回滚:记录结果,并在阈值越界时触发预定义回滚。

Amazon Bedrock 可以承担自然语言理解和计划生成,AWS Step Functions 可以编排长时间运行的状态,Lambda 或内部自动化平台则执行受限动作。这里描述的是一种可以落地的参考架构,不代表 EWOK Agent 未公开的具体内部实现。

审计记录应覆盖每个阶段,而不只是最终结果。至少需要记录原始请求、调用者身份、模型与提示词版本、结构化计划、策略判断、审批人、工具调用参数、执行结果和时间戳。敏感字段应在写入日志前脱敏,并使用不可随意修改的存储和保留策略。

一个最小的 Bedrock 计划生成器

下面的 Python 示例演示“模型生成计划,代码执行策略检查”的边界。它默认只生成并验证计划,不连接真正的故障转移执行器。

运行前需要 AWS 凭证、可访问的 Bedrock 模型,以及 Python 3.10 以上版本。将 BEDROCK_MODEL_ID 替换为账户和区域中已启用、支持 Converse API 的模型 ID。

python -m venv .venv
source .venv/bin/activate
pip install boto3
export AWS_REGION=us-east-1
export BEDROCK_MODEL_ID='your-enabled-model-id'
python dr_planner.py 'Fail over payments from us-east-1 to us-west-2 under CHG-4821'

创建 dr_planner.py

import json
import os
import sys
from datetime import datetime, timezone

import boto3

ALLOWED_SERVICES = {"payments", "billing", "identity"}
ALLOWED_TARGETS = {
    "payments": {"us-west-2"},
    "billing": {"us-east-2"},
    "identity": {"us-west-2"},
}

SYSTEM_PROMPT = """You convert disaster-recovery requests into JSON plans.
Return JSON only, with exactly these string fields:
service, source_region, target_region, change_ticket, requested_action.
requested_action must be failover. Never invent missing values; use an empty string.
Do not emit commands, scripts, credentials, or explanatory text.
"""


def build_plan(request_text: str) -> dict:
    client = boto3.client(
        "bedrock-runtime",
        region_name=os.environ.get("AWS_REGION", "us-east-1"),
    )
    response = client.converse(
        modelId=os.environ["BEDROCK_MODEL_ID"],
        system=[{"text": SYSTEM_PROMPT}],
        messages=[{"role": "user", "content": [{"text": request_text}]}],
        inferenceConfig={"temperature": 0, "maxTokens": 300},
    )
    text = response["output"]["message"]["content"][0]["text"]
    return json.loads(text)


def evaluate_policy(plan: dict) -> list[str]:
    required = {
        "service",
        "source_region",
        "target_region",
        "change_ticket",
        "requested_action",
    }
    errors = []
    if set(plan) != required:
        errors.append("plan schema does not match the approved schema")
    if plan.get("service") not in ALLOWED_SERVICES:
        errors.append("service is not approved for automated failover")
    if plan.get("target_region") not in ALLOWED_TARGETS.get(plan.get("service"), set()):
        errors.append("target region is not approved for this service")
    if plan.get("requested_action") != "failover":
        errors.append("unsupported action")
    if not plan.get("change_ticket", "").startswith("CHG-"):
        errors.append("a valid change ticket is required")
    if plan.get("source_region") == plan.get("target_region"):
        errors.append("source and target regions must differ")
    return errors


def main() -> None:
    if len(sys.argv) != 2:
        raise SystemExit("usage: python dr_planner.py '<request>'")

    request_text = sys.argv[1]
    plan = build_plan(request_text)
    errors = evaluate_policy(plan)
    audit_event = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "request": request_text,
        "plan": plan,
        "decision": "REJECTED" if errors else "AWAITING_APPROVAL",
        "policy_errors": errors,
    }
    print(json.dumps(audit_event, indent=2))


if __name__ == "__main__":
    main()

这段代码刻意不执行故障转移。生产实现还应增加身份校验、变更单 API 查询、模型输出 schema 校验、提示词注入防护、请求幂等键、审批签名和集中审计存储。通过策略检查后,系统也应该只传递一个已验证的 runbook ID 及受限参数,而不是运行模型生成的 shell 命令。

安全性来自纵深防御

灾难恢复智能体拥有较大的潜在影响范围,因此不能把安全性寄托在提示词上。更可靠的设计应包含多层独立控制:

  • 最小权限:计划生成组件没有生产写权限,执行器只拥有特定 runbook 所需权限。
  • 明确的工具目录:智能体只能调用版本化、带参数 schema 的操作,不接受任意代码。
  • 默认试运行:先展示将要修改的资源、流量比例和验证条件。
  • 风险分级审批:演练环境可以自动执行,生产全量切换要求人工确认或双人审批。
  • 幂等与互斥:重复请求不会重复切流,同一服务不能并发执行冲突工作流。
  • 自动止损:错误率或关键业务指标越过阈值时暂停或回滚。
  • 完整审计:把模型输入输出、策略版本和工具结果关联到同一个执行 ID。

还要防范自然语言入口带来的提示词注入。来自告警、工单和聊天记录的文本都应视为不可信数据。模型不能因为文本中出现“忽略审批”就改变权限,真正的授权必须由身份系统和策略引擎判定。

从演练助手逐步走向生产执行

引入这类系统时,可以按风险逐级开放能力:先让智能体解释 runbook 和收集证据,再让它生成只读计划;随后在隔离环境中执行,最后才允许经过审批的生产故障转移。

上线前应确认以下事项:

  • 故障转移和回滚 runbook 已经独立验证,不依赖智能体才能运行。
  • 每个生产动作都有清晰的前置条件、超时和失败语义。
  • 模型异常、Bedrock 不可用或审计系统故障时,流程默认停止。
  • 团队定期进行演练,并验证手工接管路径。
  • 指标同时衡量恢复速度和安全性,包括误触发、策略拒绝、回滚次数与审计完整率。

EWOK Agent 的价值不只是让值班工程师少写几条命令,而是把自然语言、标准化 runbook、策略控制和审计证据连接成一个统一入口。真正适合生产的 agentic 灾难恢复系统,应当让操作更快,但绝不能让权限边界变得模糊。


相关推荐