用 Amazon Bedrock AgentCore 构建多租户智能体:隔离、配置与落地实践

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

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

预计阅读时间:14 分钟

当你的 AI Agent 从内部工具走向 SaaS 产品,租户隔离就成了架构的第一道门槛。单租户场景下,Agent 共享同一个知识库、同一套工具权限、同一个上下文窗口,一切都很简单。但一旦多个客户接入,问题立刻浮现:A 客户的订单数据不能被 B 客户的 Agent 读到;不同客户对同一工具的调用策略可能完全不同;按租户计费和限流也需要精确到每个 tenant_id。Amazon Bedrock AgentCore 正是为这类多租户 SaaS 场景设计的框架层,它把 Agent 的编排、隔离、工具注册和上下文管理统一收口,让开发者不用从零拼装基础设施。

多租户 Agent 的核心挑战:不只是"加个 tenant_id"

传统多租户 SaaS 的隔离维度主要是数据层——行级隔离或 schema 隔离就够了。但 Agent 系统的隔离要复杂得多,至少涉及四个层面:

数据与知识隔离——Agent 依赖的知识库(Knowledge Base)和检索增强(RAG)数据必须按租户切分。如果 A 客户上传了内部 FAQ,B 客户的 Agent 绝不能在检索结果里看到它。

工具与权限隔离——同一个"查询订单"工具,A 客户只能查自己租户的订单,B 客户连这个工具都不该被启用。工具的注册、发现和执行权限都需要租户级控制。

上下文与记忆隔离——Agent 的对话历史、长期记忆(Memory Store)属于租户私有数据。跨租户共享上下文意味着泄露业务意图。

计费与限流隔离——每个租户的 Agent 调用次数、模型推理 token 消耗、工具调用频次都要独立计量,否则无法做按租户计费和公平调度。

Bedrock AgentCore 的设计思路是:在 Agent 编排层统一注入租户上下文,让下游的知识库、工具网关、记忆存储都自动按 tenant_id 路由和过滤,而不是在每个组件里手动拼隔离逻辑。

AgentCore 的多租户架构拆解

AgentCore 把一个多租户 Agent 的运行拆成几个关键组件,每个组件都有租户感知能力:

  • Agent Runtime——Agent 的核心编排引擎,负责推理循环(reasoning loop)、工具调用决策和上下文管理。每次请求携带 tenant_id,Runtime 会把它注入到所有下游调用中。
  • Tool Registry——工具注册中心,按租户管理工具的可用性和权限。不同租户可以绑定不同的工具集,同一个工具也可以按租户配置不同的执行策略(比如限流、参数校验)。
  • Memory Store——租户隔离的长期记忆存储。Agent 的对话摘要、关键实体记忆都按 tenant_id 分区存放。
  • Knowledge Base Gateway——知识库检索网关,在向量检索阶段自动按租户过滤,确保 RAG 结果只包含当前租户的数据。

关键设计点在于:租户上下文的注入发生在 Runtime 层,而不是在每个工具或知识库的调用方手动传递。这意味着开发者只需要在创建 Agent 会话时指定 tenant_id,后续所有操作自动带租户标签。

实践:一个多租户客服 Agent 的配置与代码

下面用一个具体场景演示:假设你正在构建一个多租户客服 Agent,每个租户有自己的产品知识库和订单查询工具,Agent 需要根据租户上下文自动路由到正确的数据源。

1. 定义租户级 Agent 配置(YAML)

每个租户有自己的 Agent 配置文件,指定可用的工具、知识库和行为策略:

# tenant_config_tenant_a.yaml
tenant_id: "tenant-a"
agent:
  name: "CustomerServiceAgent-A"
  foundation_model: "anthropic.claude-3-5-sonnet"
  instruction: |
    你是 tenant-a 的客服助手。只回答 tenant-a 相关的产品问题。
    查询订单时,只返回 tenant-a 的订单数据。
    如果用户问到其他租户的信息,明确拒绝并说明原因。

tools:
  - name: "QueryOrders"
    description: "查询 tenant-a 的订单状态"
    # 工具执行时自动注入 tenant_id 作为查询过滤条件
    execution_params:
      tenant_filter: "tenant-a"
      max_results: 10
  - name: "SearchProductFAQ"
    description: "搜索 tenant-a 的产品常见问题"
    knowledge_base_id: "kb-tenant-a-products"

memory:
  store_id: "memory-tenant-a"
  session_ttl: 3600  # 1小时会话超时

rate_limit:
  max_invocations_per_minute: 30
  max_token_per_invocation: 4096
# tenant_config_tenant_b.yaml
tenant_id: "tenant-b"
agent:
  name: "CustomerServiceAgent-B"
  foundation_model: "anthropic.claude-3-5-sonnet"
  instruction: |
    你是 tenant-b 的客服助手。只处理 tenant-b 的退货和退款问题。
    不提供订单查询功能。

tools:
  - name: "ProcessRefund"
    description: "处理 tenant-b 的退款申请"
    execution_params:
      tenant_filter: "tenant-b"
      require_approval: true
  - name: "SearchRefundPolicy"
    description: "搜索 tenant-b 的退款政策"
    knowledge_base_id: "kb-tenant-b-refund"

memory:
  store_id: "memory-tenant-b"
  session_ttl: 7200  # 2小时会话超时

rate_limit:
  max_invocations_per_minute: 15
  max_token_per_invocation: 2048

注意两个租户的差异:tenant-a 有订单查询和产品 FAQ,tenant-b 只有退款处理和退款政策检索。工具集、知识库、指令、限流策略都不同——这就是多租户 Agent 配置的核心。

2. 用 Python SDK 启动租户隔离的 Agent 会话

import boto3
import json
from bedrock_agentcore import AgentRuntimeClient, TenantContext

# 初始化 AgentCore 客户端
client = AgentRuntimeClient(
    region="us-east-1",
    # 可选:指定统一的 IAM role,AgentCore 会按租户自动切换权限
    execution_role="arn:aws:iam::123456789012:role/AgentCoreExecutionRole"
)

def create_tenant_session(tenant_id: str, user_id: str) -> str:
    """为指定租户创建隔离的 Agent 会话"""

    # 加载租户配置(实际项目中可从 DynamoDB/S3 动态加载)
    config = load_tenant_config(tenant_id)

    # 创建租户上下文——这是隔离的关键
    tenant_context = TenantContext(
        tenant_id=tenant_id,
        user_id=user_id,
        # 租户级限流配置
        rate_limits=config["rate_limit"],
        # 租户级工具权限
        allowed_tools=[tool["name"] for tool in config["tools"]],
        # 租户级知识库绑定
        knowledge_base_ids=[
            tool["knowledge_base_id"]
            for tool in config["tools"]
            if "knowledge_base_id" in tool
        ],
    )

    # 创建 Agent 会话,自动注入租户上下文
    session = client.create_session(
        agent_name=config["agent"]["name"],
        model_id=config["agent"]["foundation_model"],
        instruction=config["agent"]["instruction"],
        tenant_context=tenant_context,
        memory_store_id=config["memory"]["store_id"],
        session_ttl=config["memory"]["session_ttl"],
    )

    return session.session_id


def invoke_agent(session_id: str, tenant_id: str, user_input: str) -> str:
    """在租户隔离会话中调用 Agent"""

    response = client.invoke(
        session_id=session_id,
        input_text=user_input,
        # 每次调用都携带 tenant_id,确保 Runtime 在工具调用和
        # 知识库检索时自动注入租户过滤
        tenant_id=tenant_id,
    )

    # 返回 Agent 的回复(已自动过滤跨租户数据)
    return response.output_text


# ---- 使用示例 ----
# tenant-a 的用户发起客服对话
session_a = create_tenant_session("tenant-a", "user-001")
reply = invoke_agent(session_a, "tenant-a", "我的订单 A-1234 现在什么状态?")
print(f"Agent 回复: {reply}")
# Agent 会调用 QueryOrders 工具,自动过滤 tenant_id=tenant-a

# tenant-b 的用户发起退款对话
session_b = create_tenant_session("tenant-b", "user-002")
reply = invoke_agent(session_b, "tenant-b", "我想退款订单 B-5678")
print(f"Agent 回复: {reply}")
# Agent 会调用 ProcessRefund 工具,自动过滤 tenant_id=tenant-b
# 注意:tenant-b 的 Agent 没有 QueryOrders 工具,无法查订单

说明:上述代码基于 Bedrock AgentCore 的设计模式编写。SDK 的具体 API 名称和参数可能随版本更新调整,使用前请对照 AWS 官方文档确认最新接口。核心思路不变——通过 TenantContext 在会话创建时注入租户信息,后续所有工具调用和知识库检索自动按租户隔离。

3. 知识库的租户过滤(向量检索层)

知识库隔离的关键在于向量检索阶段的 metadata filter。Bedrock Knowledge Base 支持在检索时注入 metadata 过滤条件:

# AgentCore 在内部自动执行类似以下逻辑
# 开发者通常不需要手动写这段代码,但理解它有助于排查隔离问题

import boto3

bedrock_kb = boto3.client("bedrock-agent-runtime")

def search_with_tenant_filter(kb_id: str, tenant_id: str, query: str):
    """按租户过滤的知识库检索"""

    response = bedrock_kb.retrieve(
        knowledgeBaseId=kb_id,
        retrievalQuery={"text": query},
        retrievalConfiguration={
            "vectorSearchConfiguration": {
                # 关键:metadata filter 确保只返回当前租户的文档
                "filter": {
                    "andAll": [
                        {
                            "equals": {
                                "key": "tenant_id",
                                "value": tenant_id
                            }
                        }
                    ]
                },
                "numberOfResults": 5
            }
        }
    )

    return response["retrievalResults"]


# 验证隔离效果
results_a = search_with_tenant_filter("kb-tenant-a-products", "tenant-a", "退货政策")
results_b = search_with_tenant_filter("kb-tenant-a-products", "tenant-b", "退货政策")
# results_b 应返回空列表——tenant-b 无权访问 tenant-a 的知识库

三种租户隔离模式的选择

AgentCore 支持三种隔离粒度,选择取决于你的业务规模和安全要求:

隔离模式 数据隔离 工具隔离 成本 适用场景
共享 Agent + 逻辑隔离 metadata filter 工具权限表 租户数量大、数据敏感度中等
共享 Agent + 独立知识库 每个 KB 独立 工具权限表 租户数据敏感度高、需要物理隔离
独立 Agent 实例 完全独立 完全独立 大客户、合规要求严格(如金融)

大多数 SaaS 从"共享 Agent + 逻辑隔离"起步,等大客户提出合规要求后再升级到独立实例。关键是在架构初期就设计好租户上下文的注入路径,这样切换隔离模式只需要改配置,不需要改代码。

落地前的检查清单

把多租户 Agent 推上线之前,逐项确认:

  • 租户上下文注入点——确认 tenant_id 在 Agent Runtime 入口就注入,而不是在工具调用时才手动传递。漏掉任何一层都会导致数据泄露。
  • 知识库 metadata 标记——所有上传到 Knowledge Base 的文档必须带 tenant_id metadata 字段。没有这个标记的文档会被所有租户看到。
  • 工具执行的租户过滤——每个工具的执行逻辑必须用 tenant_id 过滤数据源查询。不要依赖 Agent 的"指令"来阻止跨租户访问——指令可以被用户 prompt 轻易绕过。
  • 记忆存储的分区——Memory Store 的 session_id 必须包含 tenant_id 前缀,防止跨租户读取对话历史。
  • 限流与计费——每个租户的调用频次和 token 消耗独立计量。AgentCore 的 rate_limit 配置要和你的计费模型对齐。
  • 回退与降级——当某个租户的 Agent 出错时,不要影响其他租户的会话。共享 Runtime 模式下尤其要注意错误隔离。

多租户 Agent 的架构难点不在"能不能跑起来",而在"跑起来之后能不能守住边界"。Bedrock AgentCore 提供了框架级的隔离机制,但最终的安全性取决于你是否在每个数据接触点都正确使用了 tenant_id 过滤。从第一个租户接入开始就严格执行隔离,比事后修补要便宜得多。


相关推荐