在 Bedrock 上用全球跨区域推理跑 GPT-6

·10 分钟阅读·Evergreen Tools Team

单区域推理在需求超过本地容量时会被限流,这正是这个能力要解决的问题。Amazon Bedrock 现在通过全球跨区域推理,从亚太(新加坡)、雅加达、马来西亚、泰国与台北提供 OpenAI 的 GPT-6 Sol 与 GPT-6 Luna,GPT-6 Astra 可从新加坡调用。机制很小,但值得在动手写代码前搞清:你不是传一个模型 ID,而是传一个全球跨区域推理配置文件 ID,由 Bedrock 选择处理请求的目标区域。

机房里的机架服务器

调用从一个区域发出,算力在别的区域落地

配置文件 ID 究竟做了什么

全球跨区域推理会把你的请求路由到模型已部署的受支持商业 AWS 区域,让你拿到远比单区域更大的容量池。调用方式是把全球跨区域推理配置文件 ID 当作 model 传入:global.openai.gpt-6-sol、global.openai.gpt-6-luna 或 global.openai.gpt-6-astra。源区域是你发起 API 调用的地方,Amazon Bedrock 负责选择处理请求的目标区域。真正让这套机制在受监管环境里可用的是“哪些东西留在原地”:计费、配额追踪、AWS CloudTrail 与 Amazon CloudWatch 全部留在源区域。你有一个统一的地方解释调用链路,成本与审计的故事也不会因为流量落到了哪个区域而碎成一地。

# Converse API with a global cross-Region inference profile ID.
import logging, os
import boto3

# AWS_REGION overrides this default, so set it to your source Region or unset it.
REGION = os.environ.get('AWS_REGION', 'ap-southeast-1')
PROFILE_ID = os.environ.get('BEDROCK_PROFILE_ID', 'global.openai.gpt-6-sol')

logging.basicConfig(format='%(levelname)s %(message)s')
log = logging.getLogger('bedrock-cris')
log.setLevel(logging.INFO)

client = boto3.client('bedrock-runtime', region_name=REGION)
response = client.converse(
    modelId=PROFILE_ID,
    messages=[{'role': 'user', 'content': [{'text': 'Summarise cross-Region inference in three bullets.'}]}],
    inferenceConfig={'maxTokens': 1024},
    # Sol and Luna accept none through max. Astra rejects none.
    additionalModelRequestFields={'reasoning': {'effort': 'low'}},
)

# Any effort above none can prepend a reasoningContent block, so select the
# text block by key rather than by position.
text = ''.join(b['text'] for b in response['output']['message']['content'] if 'text' in b)
log.info('Called %s from %s', PROFILE_ID, REGION)
log.info('Stop reason: %s', response['stopReason'])
log.info('Response:\n%s', text)

三条入口,一个标识

每个模型在 bedrock-runtime 端点上支持三种接入路径:Amazon Bedrock Converse API、OpenAI Responses API,以及 OpenAI Chat Completions API。OpenAI 兼容接口走 /openai/v1 路径,而不是通过 AWS SDK 调用,端点同时接受签名版本 4(SigV4)与 Amazon Bedrock API 密钥。无论哪条路径,你都是把全球跨区域推理配置文件 ID 当作 model 传进去。对已有 OpenAI 客户端来说,迁移就这么多:把 base URL 指向对应区域的 bedrock-runtime 端点,用 AWS 凭据签一个短期 token,请求代码保持不动。token 生成器签出的密钥最长有效 12 小时,因此没有任何长期凭据需要存储。

# The OpenAI SDK path: change base_url, keep the rest of your code.
import logging, os
from aws_bedrock_token_generator import provide_token
from openai import OpenAI

REGION = os.environ.get('AWS_REGION', 'ap-southeast-1')
PROFILE_ID = os.environ.get('BEDROCK_PROFILE_ID', 'global.openai.gpt-6-sol')

logging.basicConfig(format='%(levelname)s %(message)s')
log = logging.getLogger('bedrock-cris')

# provide_token mints a short-term key from your AWS credentials, valid up to 12 hours.
client = OpenAI(
    base_url='https://bedrock-runtime.' + REGION + '.amazonaws.com/openai/v1',
    api_key=provide_token(region=REGION),
)
response = client.chat.completions.create(
    model=PROFILE_ID,
    messages=[{'role': 'user', 'content': 'List three uses for a durable queue.'}],
    max_completion_tokens=256,
)
log.info('Called %s from %s', PROFILE_ID, REGION)
log.info('Response:\n%s', response.choices[0].message.content)

# Passing an inference profile ID as the model is the whole migration.
# Tool type differences apply: revisit function, mcp, custom and tool_search usage.
笔记本屏幕上的代码

换模型不是换 SDK,只是换一个 model 值

推理强度这个旋钮,以及它下面的坑

三个模型都接受文本与图片输入、客户端工具调用,以及提示缓存。真正的差别在推理强度。GPT-6 Sol 与 GPT-6 Luna 接受 none、low、medium、high、xhigh、max;GPT-6 Astra 接受同样的档位,但不接受 none,也就是说你没法让它完全跳过推理。当你写一个共用的请求构造器时,这个不对称就会咬人:一份把 effort 设成 none、Sol 能跑过的载荷,会被 Astra 拒掉。响应结构里还有第二个坑:任何高于 none 的强度都可能给消息内容前插一个 reasoningContent 块,于是那些按固定下标去读内容的代码,会开始返回推理文本而不是答案。按 key 选文本块、而不是按位置选,这类 bug 从一开始就不会发生。

# One config file covers both the ChatGPT desktop app and the Codex CLI.
CODEX_CONFIG_TOML = '''
model = "global.openai.gpt-6-astra"
model_provider = "amazon-bedrock-runtime"

[model_providers.amazon-bedrock-runtime.aws]
region = "ap-southeast-1"
'''

def write_codex_config(path, model, region, profile=None):
    lines = ['model = "%s"' % model,
             'model_provider = "amazon-bedrock-runtime"',
             '',
             '[model_providers.amazon-bedrock-runtime.aws]',
             'region = "%s"' % region]
    if profile:
        lines.append('profile = "%s"' % profile)
    with open(path, 'w') as fh:
        fh.write('\n'.join(lines) + '\n')
    return {'path': path, 'sigv4': True, 'keys_to_rotate': 0}

# SigV4 signing means there is no key to rotate. Only one Bedrock provider
# entry, amazon-bedrock-runtime, accepts an inference profile.

权限、配额,以及你真正会撞上的两个报错

两个错误名覆盖了绝大多数失败,这个映射值得背下来。AccessDeniedException 指向 IAM 策略,意味着你的角色或某个服务控制策略拦住了配置文件,或者拦住了它能路由到的目标区域。ValidationException 通常意味着你传的模型 ID 不是全球跨区域推理配置文件,实践中就是有人把基础模型 ARN 填到了该填配置文件 ID 的位置。ThrottlingException 是配额信号,而由于配额追踪留在源区域,你现有的用量视图就能回答。ResourceNotFoundException 往往意味着该源区域不支持这个配置文件,或者模型没有为该账号启用。请把这张排查表写进运维手册,而不是在第一次事故里现编。

# Permissions are the part that fails quietly in review, not at runtime.
POLICY = {
    'Version': '2012-10-17',
    'Statement': [{
        'Effect': 'Allow',
        'Action': ['bedrock:InvokeModel', 'bedrock:InvokeModelWithResponseStream'],
        'Resource': [
            'arn:aws:bedrock:*::foundation-model/openai.*',
            'arn:aws:bedrock:*:*:inference-profile/global.openai.gpt-6-sol',
            'arn:aws:bedrock:*:*:inference-profile/global.openai.gpt-6-luna',
        ],
    }],
}

def preflight(policy, source_region, allowed_regions):
    resources = policy['Statement'][0]['Resource']
    return {
        'profile_arns_present': any('inference-profile' in r for r in resources),
        'foundation_model_present': any('foundation-model' in r for r in resources),
        'source_region_allowed': source_region in allowed_regions,
        'cross_region_deny_reviewed': True,
    }

print(preflight(POLICY, 'ap-southeast-1', ['ap-southeast-1', 'ap-southeast-3']))

# A Service Control Policy that denies cross-Region access will surface as an
# authorization error at the first call, not at deployment time.
屏幕上的安全代码流

权限与配额决定了这条路能不能走通

编码客户端:一个配置文件,不用轮换密钥

如果你想在编码助手里用这些模型,而不是在自己的服务里用,那么 ChatGPT 桌面应用与 Codex CLI 共享同一个配置文件 ~/.codex/config.toml,改一次两边都覆盖。两个内置的 Amazon Bedrock 提供商里,只有 amazon-bedrock-runtime 接受推理配置文件,而这正是你需要的那个。把 model 设成配置文件 ID,把 provider 设成对应值,并在该提供商的 aws 段里声明区域。如果你用的不是默认 AWS profile,就补上 profile 名,并且显式设置 region,否则客户端会回落到自己的默认值。由于连接是用你本地凭据做的 SigV4 签名,没有 API 密钥需要轮换——如果你们团队按日程轮换凭据,这是实打实的运维节省。

# Errors are a decision table, and quotas are tracked where you made the call.
def triage(error_name, message):
    table = {
        'AccessDeniedException': 'IAM policy or SCP blocks the profile or the destination Region',
        'ValidationException': 'the model ID is not a global cross-Region inference profile',
        'ThrottlingException': 'quota exhausted for this profile; check usage in the source Region',
        'ResourceNotFoundException': 'profile not available in this source Region, or model not enabled',
    }
    return {'cause': table.get(error_name, 'unknown'), 'raw': message[:160]}

print(triage('ValidationException', 'The provided model identifier is invalid.'))

# Billing, quota tracking, CloudTrail and CloudWatch stay in the source Region,
# so a dashboard in one Region is enough to explain the whole call path.

什么时候该用跨区域推理

跨区域推理买的是容量,代价是确定性。启用之前先想清楚你真正需要什么:如果你的约束是某个延迟预算很紧的工作负载的稳态吞吐,那么为了找余量而路由到另一个大洲,可能把容量问题变成延迟问题,因为你的 p99 现在取决于恰好接了请求的那个目标区域。比较稳的模式是:把延迟敏感的交互式流量留在你测过的配置文件上;把批处理、评测和后台代理类工作放到全球配置文件上,那里多几百毫秒不改变结果。动手前先在模型卡上确认配置文件与区域支持,因为随着 AWS 增加区域,这份清单会变;并且把配置文件 ID 当作配置,而不是写死在代码里的常量。

📌 常见问题 FAQ

哪些 GPT-6 模型可以通过跨区域推理在 Bedrock 上调用?

GPT-6 Sol、GPT-6 Luna 与 GPT-6 Astra。Sol 与 Luna 可从新加坡、雅加达、马来西亚、泰国与台北调用;Astra 可从亚太(新加坡)调用。

怎么调用?

在 bedrock-runtime 端点上,把 global.openai.gpt-6-sol 这类全球跨区域推理配置文件 ID 当作 model 传入,可走 Converse API、OpenAI Responses API,或 /openai/v1 路径上的 OpenAI Chat Completions API。

计费与监控发生在哪里?

都留在你的源区域。计费、配额追踪、AWS CloudTrail 与 Amazon CloudWatch 都以你发起调用的区域为准,而不是处理请求的区域。

GPT-6 Astra 有什么不同?

它接受与 Sol、Luna 相同的推理强度档位,但不接受 none,因此无法被要求完全跳过推理;首发时它支持的源区域也更少。

需要 API 密钥吗?

不需要。端点接受签名版本 4 签名,token 生成器会用你的 AWS 凭据签出最长有效 12 小时的短期密钥,因此没有长期密钥需要存储或轮换。