共享基础设施、隔离租户:用 Amazon Bedrock AgentCore 做池化多租户
本文用医疗智能体示例说明 AgentCore 池化多租户:按层级隔离计算与文档,用 JWT、ABAC 和 Cedar 限定权限,并用项目标签与用量日志归属成本。

目录
构建多租户 AI 应用会带来新的架构挑战。你需要在客户之间做到完整的租户隔离,需要能力不同的服务层级,需要按租户的细粒度成本跟踪,以及按租户的可观测性。没有这些,你就可能面临暴露客户数据、无法向客户提供适当服务质量,或产生未预见成本的风险。
在本文中,你将学习使用 Amazon Bedrock AgentCore 实现可用于生产的多租户系统的模式。你将通过为多家诊所和医院提供服务的医疗 AI 智能体看到这些模式。虽然本文以医疗作为示例领域,但架构模式和实现技术广泛适用于各种多租户 AI 应用。无论你是在构建 SaaS 平台、为多个业务单元提供服务的企业解决方案,还是为不同客户组织提供的托管服务,都可以使用这些架构模式来构建你的解决方案。
你将学到什么
- 如何使用原生 AWS 能力,在智能体式应用中实现完整的租户隔离。
- 用最少自定义代码做服务层级差异化的模式。
- 按租户做细粒度成本归属的技术。
- 可扩展多租户 AI 架构的最佳实践。
本博文是系列《用 Amazon Bedrock AgentCore 构建多租户智能体》的第 2 篇。第 1 篇(/blogs/machine-learning/building-multi-tenant-agents-with-amazon-bedrock-agentcore/)探讨架构多租户智能体式应用的设计考量,以及用 Amazon Bedrock AgentCore 应对 SaaS 架构挑战所需的框架。
示例代码的 GitHub 仓库:https://github.com/aws-samples/sample-agentcore-and-multitenancy-blog (https://github.com/aws-samples/sample-agentcore-and-multitenancy-blog)
解决方案概述
本解决方案演示如何使用 Amazon Bedrock AgentCore 的原生能力,借助 AWS 托管服务实现完整的租户隔离。该架构实现三层层次:层级(Tier)→ 租户(Tenant)→ 用户(User),你在每一层通过知识库中的文档、记忆、模型访问和成本跟踪来强制隔离。分层策略是 SaaS 应用中的常见模式:租户根据其需求被分组到不同的服务层级,例如 Basic 和 Premium、使用模式或定价计划。每个层级定义该组内租户可用的一组功能和服务质量。这种方法让 SaaS 提供商能够在保持运营效率的同时,以差异化体验服务多样化的客户群。
医疗 AI 助手示例
为了看清这在实践中如何工作,示例解决方案实现两个服务层级,以做基于层级的差异化:
- Basic 层级:面向主要需要直接文档搜索和检索的小型诊所和执业机构。因为这些任务适合更小、更经济的模型,该层级使用 Mistral Ministral 3 8B Instruct,在为简单查询提供准确结果的同时保持低成本。
- Premium 层级:面向需要复杂临床分析的医院和专科中心。该层级使用具备高级推理能力的 OpenAI GPT OSS 120B,以便准确选择工具,其中包括仅对 Premium 层级客户可用的网络搜索工具。
在每个层级内,本解决方案使用池化隔离模型(pool isolation model)(https://docs.aws.amazon.com/wellarchitected/latest/saas-lens/pool-isolation.html):租户共享同一套底层基础设施和计算资源,而不是每个租户拥有专用、隔离的资源。池化模型最大化资源利用率并简化运营,同时通过限定范围的标识符、访问策略和数据分区等逻辑分离机制强制租户隔离。把分层策略与池化模型结合起来,使你能够在成本效率与提供差异化服务级别的灵活性之间取得平衡。
架构
来看 AgentCore 的原语如何组合起来解决这些多租户挑战。下图说明该解决方案的多租户架构,展示请求如何从已认证用户经过特定层级的智能体,流向隔离的文档存储:
图 1:具有分层隔离的多租户架构(层级 → 租户 → 用户)。
该解决方案由这些关键组件组成:
- Amazon Cognito(/cognito/):管理用户身份验证,并把租户元数据(tier、clinic_id、role)存储在 JSON Web Token(JWT)声明中。这些声明被提取出来,并作为租户上下文通过请求载荷传播,使每个下游组件都能把操作限定到正确的租户。
- Amazon API Gateway(/api-gateway/):路由请求,并通过使用计划强制基于层级的速率限制。
- AWS Lambda(/lambda/):提取租户上下文,并调用相应的 Amazon Bedrock AgentCore 智能体。
- AgentCore(/bedrock/agentcore/)组件:Runtime(智能体执行)、Memory(对话状态)、Identity(智能体身份管理)、Gateway(工具服务器)和 Policy(智能体动作边界)。
- Amazon Simple Storage Service(/s3/)(Amazon S3):把临床文档存储在按层级分开的存储桶中,并用分层前缀结构做租户隔离。
- Amazon Bedrock Knowledge Bases(https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html):提供带元数据过滤的语义搜索,把查询限定到请求租户的文档。
- Amazon Bedrock project(https://docs.aws.amazon.com/bedrock/latest/userguide/cost-mgmt-projects.html):通过成本分配标签实现按层级的成本跟踪。
解决方案演练
本节描述该解决方案的关键方面。你运行部署脚本来为解决方案建立基础设施和应用。本节中的代码摘录只用于描述解决方案的组件如何处理架构的关键方面。不需要运行这里展示的任何命令或执行任何代码片段。
Amazon Bedrock AgentCore 组件
该架构利用六项核心 Bedrock AgentCore 能力来实现多租户:
AgentCore Runtime:AgentCore Runtime 为本解决方案中的智能体提供计算,每个智能体会话在隔离的 micro-VM 中执行,以实现租户级计算隔离。它按层级托管单独的智能体实例,每个实例配置适合该层级的模型和能力。
# Agent configuration
config = TIER_CONFIG.get(tier, TIER_CONFIG["basic"])
model_id = config["default_model"]
# Project ID is fetched from SSM
project_id = get_ssm_parameter(config["project_ssm"])
# Passed to OpenAIModel (premium tier) targeting the inference endpoint
self.model = OpenAIModel(
client_args={"base_url": mantle_base_url, "api_key": api_key, "project": project_id},
model_id=model_id,
)AgentCore Identity:AgentCore Identity 用统一的基于 JWT 的身份验证模型保护多租户架构。Cognito ID 令牌在 Runtime 和 Gateway 边界验证用户,而工具 Lambda 为下游数据访问铸造自己的限定范围凭证。
每个 AgentCore Runtime 都配置了入站 JWT 授权器,在智能体代码执行之前验证 Cognito ID 令牌。ID 令牌以自定义声明携带租户元数据:
| 声明 | 示例值 | 用途 |
|---|---|---|
| sub | a4589458-8011-… | 唯一用户标识符(Cognito UUID) |
| iss | https://cognito-idp.us-east-1.amazonaws.com/us-east-1_AbCdEfG | 令牌签发者,由 AgentCore Runtime 验证 |
| aud | 7rfbikfsm51j… | Web 客户端 ID,由 Runtime 的 allowedAudience 验证 |
| token_use | id | 标识这是 ID 令牌(不是访问令牌) |
| exp | 1745446200 | 过期时间戳(默认:签发后 1 小时) |
| cognito:username | dr.foster@hospital-a.com | 登录用户名,用作记忆隔离的 user_id |
| custom:tier | premium | 路由到正确的模型、知识库和网关 |
| custom:clinic_id | hospital-a | 这是租户 ID。在知识库、记忆和 Amazon DynamoDB 上强制数据隔离 |
| custom:role | physician | 基于角色的访问控制(便于将来扩展) |
授权器在智能体部署期间配置:
AUTHORIZER_CONFIG='{"customJWTAuthorizer":{"discoveryUrl":"'$COGNITO_DISCOVERY_URL'","allowedAudience":["'$COGNITO_WEB_CLIENT_ID'"]}}'
agentcore configure --entrypoint main.py \
--name healthcare_basic \
--authorizer-config "$AUTHORIZER_CONFIG" \
--request-header-allowlist "Authorization"AgentCore Gateway 也配置了 JWT 授权,使用相同的 Cognito 发现 URL 和受众。当智能体调用网关时,它把用户的原始 JWT 作为 Bearer 令牌转发以供验证,并带上租户上下文头(X-Tier、X-Clinic-ID、X-S3-Prefix)。网关验证令牌,然后通过 metadataConfiguration 把租户头传播到目标 Lambda。
目标 Lambda 从不直接接收或处理用户的 JWT。相反,它读取受信任的租户头(之所以受信任,是因为只有已认证请求能通过网关的 CUSTOM_JWT 授权器),并代入一个 TVM(Token Vending Machine,令牌售卖机)角色,会话标签来自这些头。TVM 角色的 ABAC 策略使用 dynamodb:LeadingKeys 条件限制 DynamoDB 访问,确保每个租户只能在 IAM 级别查询自己诊所的数据,而不只是应用级过滤。
AgentCore Memory:对话历史不能在租户之间泄露,也不能在一个租户内的多个用户之间泄露。该解决方案在两层强制记忆隔离:应用级范围限定,以及由 IAM 支持的基于属性的访问控制(Attribute-Based Access Control, ABAC)。
在应用层,AgentCore Memory 使用分层命名空间结构,并用复合 actor_id 按租户组织对话数据:
actor_id = f"{tier}-{clinic_id}-{user_id}"
# Example: "basic-clinic-a-dr.smith@clinic-a.com"命名空间分隔不同类型的记忆:
clinic/{actor_id}/facts/{session_id} # SEMANTIC --- clinical facts
clinic/{actor_id}/preferences # PREFERENCES -- user preferences为了在基础设施层强制隔离,该解决方案使用带 ABAC 的令牌售卖机(Token Vending Machine, TVM)模式。在运行时,智能体代入一个带有 Tier、ClinicId 和 UserId 会话标签的 TVM 角色,收到限定到该租户命名空间的临时凭证:
sts = boto3.client("sts", region_name=region)
response = sts.assume_role(
RoleArn=tvm_role_arn,
RoleSessionName=f"mem-{tier}-{clinic_id}-{user_id}",
DurationSeconds=900,
Tags=[
{"Key": "Tier", "Value": tier},
{"Key": "ClinicId", "Value": clinic_id},
{"Key": "UserId", "Value": user_id},
],
TransitiveTagKeys=["Tier", "ClinicId", "UserId"],
)
# Create a scoped boto3 session from the temporary credentials
scoped_session = boto3.Session(
aws_access_key_id=response["Credentials"]["AccessKeyId"],
aws_secret_access_key=response["Credentials"]["SecretAccessKey"],
aws_session_token=response["Credentials"]["SessionToken"],
)
# Build a MemoryClient backed by scoped credentials
memory_client = MemoryClient(region_name=region)
memory_client.gmcp_client = scoped_session.client("bedrock-agentcore-control")
memory_client.gmdp_client = scoped_session.client("bedrock-agentcore")TVM 角色的信任策略确保只有智能体执行角色可以代入它,并且三个会话标签都存在:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
AWS: !GetAtt RuntimeAgentCoreRole.Arn
Action:
- sts:AssumeRole
- sts:TagSession
Condition:
StringLike:
aws:RequestTag/Tier: "?*"
aws:RequestTag/ClinicId: "?*"
aws:RequestTag/UserId: "?*"AgentCore Gateway:AgentCore Gateway 使用模型上下文协议(Model Context Protocol, MCP)把静态 Lambda 函数转变为动态的、感知上下文的智能体工具。模型上下文协议是把 AI 智能体连接到外部工具的开源标准。
AgentCore Gateway 消除了构建自定义工具编排逻辑的需要。如果没有它,你就需要把 API 手工集成到智能体工作流中。这涉及编写自定义代码来解析 API 规范、处理身份验证、管理转换、实现错误处理,并传播租户上下文。
Lambda 函数通过 Gateway 暴露两个工具:
patient_context:从 PatientMetadata DynamoDB 表检索患者人口统计信息和病史。clinic_config:从 ClinicConfig DynamoDB 表获取诊所配置和提供者信息。
如前所述,租户身份在每个组件中传播。智能体用限定租户范围的头(X-Tier、X-Clinic-ID、X-S3-Prefix)初始化其 MCP Gateway 客户端,因此通过网关的每一次工具调用都自动携带租户上下文,在网关层强制数据隔离,而不需要按工具的过滤逻辑。此链接提供关于网关头的更多信息(https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-headers.html)。
# Define Lambda as MCP target
lambda_target_config = {
"mcp": {
"lambda": {
"lambdaArn": lambda_function_arn,
"toolSchema": {"inlinePayload": api_specification}
}
}
}
# Create gateway with AWS IAM authorization
# The agent's Runtime execution role authenticates via SigV4
gateway = gateway_client.create_gateway(
name="healthcare-basic-gw",
roleArn=execution_role_arn,
protocolType="MCP",
authorizerType="AWS_IAM",
description="Healthcare Clinical Document Processing Gateway",
)
# Add Lambda target with tenant header propagation
metadata_config = {
"allowedRequestHeaders": [
"X-Tier",
"X-Clinic-ID",
"X-S3-Prefix"
]
}
credential_config = [{"credentialProviderType": "GATEWAY_IAM_ROLE"}]
create_target_response = gateway_client.create_gateway_target(
gatewayIdentifier=gateway_id,
name=f"HealthcareLambda-{tier.title()}",
targetConfiguration=lambda_target_config,
credentialProviderConfigurations=credential_config,
metadataConfiguration=metadata_config,
)网关支持三种身份验证机制:
- IAM 角色:用于 AWS 服务集成。
- 自定义 JWT:用于感知租户的工具(本文使用的方式)。
- OAuth:用于第三方 API 集成。
AgentCore Policy:AgentCore Policy 使用 Cedar(https://cedarpolicy.com/en)授权策略,在网关工具上强制特定层级的动作边界。该解决方案创建一个共享策略引擎,以 ENFORCE 模式同时挂到 basic 和 premium 网关。对 basic 层级,一条 Cedar 策略通过评估工具输入中的 request_hour 字段,把 patient_context 工具限制在营业时间(上午 8 点至下午 6 点)。智能体必须先调用 current_time 并传入当前小时,如果小时落在允许窗口之外,策略引擎就拒绝该调用。对 premium 层级,策略无条件允许 patient_context,使医院获得 7×24 访问。两个层级都对 clinic_config 工具给予显式允许,因为它暴露的是非敏感配置数据。这种方法把访问控制移出应用代码,放入在网关层评估的声明式 Cedar 策略,因此层级差异化在 Lambda 函数执行之前就被强制。
# Cedar policy: basic tier --- restrict patient_context to business hours
permit(
principal is AgentCore::OAuthUser,
action == AgentCore::Action::"HealthcareLambda-Basic___patient_context",
resource == AgentCore::Gateway::"{gateway_arn}"
)
when {
context.input has request_hour &&
context.input.request_hour >= 8 &&
context.input.request_hour < 18
};
# Cedar policy: premium tier --- 24/7 patient_context access
permit(
principal is AgentCore::OAuthUser,
action == AgentCore::Action::"HealthcareLambda-Premium___patient_context",
resource == AgentCore::Gateway::"{gateway_arn}"
)
when {
context.input has patient_id
};AgentCore Observability:AgentCore 的可观测性集成使用 OpenTelemetry baggage 在整个请求生命周期中传播租户元数据。OpenTelemetry baggage 是一个键值存储,让你可以在跟踪上下文旁边传播额外数据。该解决方案在 AgentCore Runtime 入口把租户标识符设为 baggage,因此每个下游 span 和日志条目都携带租户归属:
from opentelemetry import baggage, context
# Set tenant context in OTel baggage (at AgentCore Runtime entrypoint)
ctx = baggage.set_baggage("tier", tier)
ctx = baggage.set_baggage("clinic_id", clinic_id, context=ctx)
ctx = baggage.set_baggage("actor_id", actor_id, context=ctx)
context.attach(ctx)例如,你可以使用 Amazon CloudWatch Logs Insights(https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AnalyzingLogData.html)跟踪每个诊所的请求量:
-- Request volume per clinic from the API Gateway Lambda proxy logs
fields @timestamp, @message
| filter @message like /Tier:/
| parse @message *"*Tier: *, Clinic ID: *, User ID: *, Role: **"* as tier, clinic_id, user_id, role
| stats count() as request_count by clinic_id, tier
| sort request_count desc再加上用于按层级成本归属的 Bedrock Projects,以及用于按诊所 token 跟踪的结构化用量日志,你就获得跨模型用量、智能体执行和记忆操作的租户级可见性。
多租户实现的关键模式
本节描述该解决方案如何实现用 Amazon Bedrock AgentCore 做多租户的核心模式。
1. 通过按层级的 S3 存储桶做数据隔离
在医疗解决方案示例中,系统为每个服务层级创建单独的 S3 存储桶,并在每个存储桶内使用特定租户的前缀。每个层级的知识库有自己专用的 S3 存储桶,在层级之间提供存储桶级隔离。在每个存储桶内,分层前缀组织租户数据,通过基于路径的访问控制和知识库元数据过滤实现隔离:
s3://healthcare-basic-kb-{suffix}/
├── basic-tier/
│ ├── clinic-a/
│ │ ├── appointment-notes/
│ │ ├── lab-results/
│ │ ├── patient-intake/
│ │ └── prescriptions/
│ ├── clinic-b/
│ ├── clinic-c/
│ └── clinic-d/
s3://healthcare-premium-kb-{suffix}/
├── premium-tier/
│ ├── hospital-a/
│ │ ├── surgical-notes/
│ │ ├── pathology-reports/
│ │ └── imaging-studies/
│ ├── hospital-b/
│ ├── clinic-e/
│ └── clinic-f/在每个存储桶内,S3 前缀由从 Cognito JWT 声明(custom:tier、custom:clinic_id)提取的租户身份构成。然后这个前缀以两种方式使用:它作为 X-S3-Prefix 头在每一次 MCP Gateway 工具调用上传递,用于网关层强制;文档检索工具通过 Amazon Bedrock Knowledge Base(https://docs.aws.amazon.com/bedrock/latest/userguide/knowledge-base.html)对 clinic_id 的元数据过滤来强制隔离:
response = client.retrieve(
knowledgeBaseId=kb_id,
retrievalQuery={"text": query},
retrievalConfiguration={
"vectorSearchConfiguration": {
"filter": {"equals": {"key": "clinic_id", "value": clinic_id}},
}
},
)2. 通过 Bedrock Projects 和结构化用量日志做成本归属
成本归属在两个级别运作:通过 Bedrock Projects 按层级,通过结构化用量日志按诊所。
用 Bedrock Projects 做按层级归属:每个层级有一个专用的 Bedrock Project,带有成本分配元数据标签(CostCenter、Tier、Application)。项目 ID 通过 Bedrock Mantle 端点在每一次推理请求上传递,因此所有模型调用成本在 AWS Cost Explorer 中按层级自动分段。
# Project configuration per tier
TIER_PROJECTS = {
"basic": {
"name": "Healthcare-Basic",
"tags": {
"Application": "HealthcareDemo",
"Tier": "Basic",
"Environment": "demo",
"CostCenter": "HC-Basic",
},
},
"premium": {
"name": "Healthcare-Premium",
"tags": {
"Application": "HealthcareDemo",
"Tier": "Premium",
"Environment": "demo",
"CostCenter": "HC-Premium",
},
},
}在运行时,智能体通过 Bedrock Mantle(与 OpenAI 兼容)端点,在每一次推理请求上传递项目 ID。这意味着每一次模型调用都自动带上该层级的成本元数据:
# Project ID loaded from SSM at agent initialization
project_id = get_ssm_parameter(config["project_ssm"])
self.model = OpenAIModel(
client_args={
"base_url": mantle_base_url,
"api_key": api_key,
"project": project_id, # Tags every inference call for cost attribution
},
model_id=model_id,
)一旦你在 AWS Billing 中激活成本分配标签(标签传播可能需要最多 24 小时),就可以在 AWS Cost Explorer 中按 CostCenter、Tier 或 Application 筛选并分组推理成本。这给你按层级的成本可见性。例如,比较为 basic 层级诊所运行 Ministral 3 8B Instruct 的成本,与为 premium 层级医院运行 GPT OSS 120B 的成本。
用结构化用量日志做按诊所归属:Bedrock Projects 每个账户限制为 1,000 个,并建议用于应用级边界。为了按诊所的成本粒度,该解决方案在每次智能体调用之后,把 token 用量记录为已带有系统中流转的租户上下文的结构化 JSON:
def _log_usage(self, result) -> None:
usage = result.metrics.accumulated_usage
logger.info(json.dumps({
"event": "inference_usage",
"tier": self.tier,
"clinic_id": self.clinic_id,
"user_id": self.user_id,
"model_id": self.model_id,
"input_tokens": usage.get("inputTokens", 0),
"output_tokens": usage.get("outputTokens", 0),
"total_tokens": usage.get("totalTokens", 0),
}))Strands SDK 通过 AgentResult.metrics 对象,在每一次智能体调用上自动跟踪 token 消耗(输入、输出和缓存指标)。把它与租户上下文中的 clinic_id 配对,每条日志就把 token 用量归属到特定诊所。这些日志进入 CloudWatch,可以用 Logs Insights 查询以计算按诊所的用量:
fields @timestamp, clinic_id, tier, model_id, input_tokens, output_tokens
| filter event = "inference_usage"
| stats sum(input_tokens) as total_input,
sum(output_tokens) as total_output,
count() as invocations
by clinic_id, tier
| sort total_output desc要估算成本,可以把 token 数量乘以每个模型已公布的每 token 价格。
3. 通过 API Gateway 做速率限制
每个层级的速率限制使用 API Gateway 使用计划(https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-usage-plans.html)强制。该解决方案按层级使用单独的使用计划,配置如下:
basic-tier-plan:
throttle: {rate_limit: 2, burst_limit: 5}
quota: {limit: 50, period: DAY}
premium-tier-plan:
throttle: {rate_limit: 10, burst_limit: 20}
quota: {limit: 500, period: DAY}清理
为避免持续费用,当你不再需要已部署的资源时可以删除它们。提供了一个 cleanup.sh 辅助脚本(位于 scripts/ 文件夹下),用于协助清理为本解决方案创建的资源。
结论
构建多租户 AI 应用需要仔细关注数据隔离、服务差异化、成本归属和可扩展性。Amazon Bedrock AgentCore 通过原生平台能力,为应对这些要求提供了稳健基础。这次实现的关键收获是:多租户并不需要复杂的应用级隔离逻辑。通过组合 AWS 服务,例如用 Cognito 做身份、用 S3 前缀做数据隔离、用 API Gateway 做速率限制、用 Bedrock Projects 和结构化日志做成本归属,并用 Bedrock AgentCore 做 AI 编排,你可以用最少的自定义代码构建安全、可扩展且经济的多租户 AI 应用。你可以把这些模式应用到正在构建的任何多租户智能体式应用。
PRIMARY_SOURCE: https://aws.amazon.com/blogs/machine-learning/shared-infrastructure-isolated-tenants-pool-model-multi-tenancy-with-amazon-bedrock-agentcore
- #多租户
- #AgentCore
- #数据隔离
- #成本归属
- #限流


