当你的 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_idmetadata 字段。没有这个标记的文档会被所有租户看到。 - 工具执行的租户过滤——每个工具的执行逻辑必须用 tenant_id 过滤数据源查询。不要依赖 Agent 的"指令"来阻止跨租户访问——指令可以被用户 prompt 轻易绕过。
- 记忆存储的分区——Memory Store 的 session_id 必须包含 tenant_id 前缀,防止跨租户读取对话历史。
- 限流与计费——每个租户的调用频次和 token 消耗独立计量。AgentCore 的 rate_limit 配置要和你的计费模型对齐。
- 回退与降级——当某个租户的 Agent 出错时,不要影响其他租户的会话。共享 Runtime 模式下尤其要注意错误隔离。
多租户 Agent 的架构难点不在"能不能跑起来",而在"跑起来之后能不能守住边界"。Bedrock AgentCore 提供了框架级的隔离机制,但最终的安全性取决于你是否在每个数据接触点都正确使用了 tenant_id 过滤。从第一个租户接入开始就严格执行隔离,比事后修补要便宜得多。