Lambda レイヤーで Bedrock Mantle を使う — Docker 不要で 47 モデルを呼び出してみた

Lambda レイヤーで Bedrock Mantle を使う — Docker 不要で 47 モデルを呼び出してみた

Amazon Bedrock Mantle を Lambda から利用するには、Bearer Token 生成や OpenAI / Anthropic 互換 API 用のSDKが必要です。本記事では、必要な3パッケージを Lambda レイヤーにまとめることで、Docker / ECR を使わずに Bedrock Mantle を呼び出す構成を検証しました。あわせて、4 つのAPIルートの使い分けと 47 モデルの動作確認結果を紹介します。
2026.07.07

はじめに

Amazon Bedrock Mantleは、Bedrock内でOpenAI / Anthropic互換のAPIを提供する新しいサービスレイヤーです。GPT-5、Gemma 4、Grok、DeepSeek v3.2など、従来のBedrock(bedrock-runtime)では提供されていないモデル群がMantle経由で利用できます。

従来のBedrockとBedrock Mantleの主な違いを整理します。

観点 従来の Bedrock Bedrock Mantle
認証方式 SigV4(SDK が自動処理) Bearer Token(aws-bedrock-token-generator
IAM アクション bedrock:InvokeModel bedrock-mantle:CreateInference / bedrock-mantle:CallWithBearerToken
API 互換性 独自 API OpenAI / Anthropic 互換
SDK AWS SDK(boto3) OpenAI SDK / Anthropic SDK
エンドポイント bedrock-runtime.{region}.amazonaws.com bedrock-mantle.{region}.api.aws

本記事では、Lambda(Python 3.13 / arm64)+ レイヤー構成でBedrock Mantleを利用する実装を順に示します。Docker / ECRは不要です。

https://github.com/aws/aws-bedrock-token-generator-python

検証内容

認証の仕組み

Bedrock MantleのAPI呼び出しでは、AWS SDKによるSigV4署名リクエストではなく、IAM認証情報から生成したBearer TokenをSDKのapi_keyとして渡します。トークンの生成にはaws-bedrock-token-generatorパッケージを使います。

from aws_bedrock_token_generator import provide_token

token = provide_token(region="us-east-1")
# → "bedrock-api-key-<base64-encoded-presigned-url>&Version=1"

provide_token()は呼び出しのたびに新しいトークンを生成します(内部キャッシュはありません)。トークンのデフォルト有効期限は12時間で、expiryパラメータで最大12時間まで設定できます。実際の有効期間は、設定した有効期限とIAM認証情報の有効期限のうち短い方です(Lambdaでは実行ロールの一時認証情報の残り時間が上限になります)。

IAM 権限

Bedrock Mantleの利用には、従来のbedrock:InvokeModelとは別のIAMアクションが必要です。検証時にエラーメッセージから特定した必要なアクションは以下の2つです。

  • bedrock-mantle:CreateInference
  • bedrock-mantle:CallWithBearerToken

いずれもAPI呼び出し(推論実行)時にサーバー側で評価されます。provide_token()自体はローカルで署名済みURLを生成する処理であり、APIコールを伴いません。

本記事では以下のIAMポリシーを使用しています。AWS管理ポリシーのbedrock-mantle:*全リソース許可よりは絞っていますが、必要最小限を保証するものではありません。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "bedrock-mantle:CreateInference",
        "bedrock-mantle:CallWithBearerToken"
      ],
      "Resource": "*"
    }
  ]
}

レイヤーの準備

LambdaでBedrock Mantleを利用するために必要なパッケージは3つです。

aws-bedrock-token-generator==1.1.0
openai==2.40.0
anthropic==0.56.0

arm64ランタイム向けのネイティブビルドが必要なため、CodeBuild(ARM64コンテナ)でレイヤーを作成します。

ビルド構成

CloudFormationテンプレートでS3バケット + CodeBuildプロジェクト + Lambdaレイヤーの3リソースを管理します。デプロイは2段階で行います。

  1. スタック作成(DeployLayer=false)→ CodeBuildでビルド → zipをS3に配置
  2. スタック更新(DeployLayer=true)→ S3上のzipからレイヤーを作成
CloudFormation テンプレート(レイヤービルド用)
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
  Bedrock Mantle SDK Lambda Layer (ARM64) - CodeBuild でビルドし S3 経由でレイヤー公開

Parameters:
  ProjectName:
    Type: String
    Default: bedrock-mantle-sdk-layer
  LayerZipKey:
    Type: String
    Default: layers/bedrock-mantle-sdk/layer-arm64.zip
  DeployLayer:
    Type: String
    Default: "false"
    AllowedValues: ["true", "false"]

Conditions:
  CreateLayer: !Equals [!Ref DeployLayer, "true"]

Resources:
  ArtifactBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub "${ProjectName}-${AWS::AccountId}-${AWS::Region}"
      LifecycleConfiguration:
        Rules:
          - Id: CleanupOldArtifacts
            Status: Enabled
            ExpirationInDays: 90

  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${ProjectName}-codebuild-role"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: codebuild.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: CodeBuildPolicy
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetObject
                  - s3:GetBucketLocation
                Resource:
                  - !GetAtt ArtifactBucket.Arn
                  - !Sub "${ArtifactBucket.Arn}/*"
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource:
                  - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/${ProjectName}-arm64:*"

  CodeBuildProjectArm64:
    Type: AWS::CodeBuild::Project
    Properties:
      Name: !Sub "${ProjectName}-arm64"
      Description: Build Lambda Layer zip (ARM64 native)
      ServiceRole: !GetAtt CodeBuildRole.Arn
      TimeoutInMinutes: 15
      Environment:
        Type: ARM_CONTAINER
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-aarch64-standard:3.0
        EnvironmentVariables:
          - Name: S3_BUCKET
            Value: !Ref ArtifactBucket
          - Name: S3_KEY
            Value: !Ref LayerZipKey
      Source:
        Type: S3
        Location: !Sub "${ArtifactBucket}/${ProjectName}/source.zip"
        BuildSpec: buildspec.yml
      Artifacts:
        Type: NO_ARTIFACTS

  LambdaLayerArm64:
    Type: AWS::Lambda::LayerVersion
    Condition: CreateLayer
    Properties:
      LayerName: !Sub "${ProjectName}-arm64"
      Description: "Bedrock Mantle SDK Layer (arm64) - aws-bedrock-token-generator, openai, anthropic"
      Content:
        S3Bucket: !Ref ArtifactBucket
        S3Key: !Ref LayerZipKey
      CompatibleRuntimes:
        - python3.13
        - python3.12
      CompatibleArchitectures:
        - arm64

Outputs:
  ArtifactBucketName:
    Value: !Ref ArtifactBucket
  CodeBuildProjectArm64Name:
    Value: !Ref CodeBuildProjectArm64
  LayerArn:
    Condition: CreateLayer
    Value: !Ref LambdaLayerArm64
  SourceUploadPath:
    Value: !Sub "s3://${ArtifactBucket}/${ProjectName}/source.zip"

buildspec.yml

version: 0.2

phases:
  install:
    runtime-versions:
      python: 3.13
  build:
    commands:
      - mkdir -p python
      - pip install -r requirements.txt -t python/
      - zip -r "layer-arm64.zip" python/
  post_build:
    commands:
      - aws s3 cp "layer-arm64.zip" "s3://${S3_BUCKET}/${S3_KEY}"

ビルドの実行

CodeBuildの実行前に、requirements.txtbuildspec.ymlを含むsource.zipSourceUploadPath(スタック出力値)にアップロードしておきます。

CodeBuildの実行時に--host-kernel-override LINUX_KERNEL_6を指定します。これによりAL2023(kernel 6.1)環境でビルドが行われ、Lambdaのarm64ランタイムと互換性のあるレイヤーを作成できます。

https://dev.classmethod.jp/articles/codebuild-host-kernel-selection/

aws codebuild start-build \
  --project-name bedrock-mantle-sdk-layer-arm64 \
  --host-kernel-override LINUX_KERNEL_6 \
  --region us-east-1

ビルド完了後、スタックをDeployLayer=trueで更新するとレイヤーが作成されます。

aws cloudformation deploy \
  --template-file template.yaml \
  --stack-name bedrock-mantle-sdk-layer \
  --capabilities CAPABILITY_NAMED_IAM \
  --parameter-overrides DeployLayer=true

ビルドされたレイヤーのzipサイズは約25.5 MBでした。なお、Lambdaの250 MB制限は関数コードと全レイヤーの展開後サイズ合計に対する制限です。

Lambda 関数のコードとルーティング

Bedrock Mantleには4つのAPIルートがあります。モデルIDのプレフィックスで振り分けます。

ルート エンドポイントパス SDK / API 対象モデル例
1 /v1/chat/completions OpenAI Chat Completions DeepSeek, Mistral, Qwen 等(大多数)
2 /openai/v1/chat/completions OpenAI Chat Completions Gemma 4, xAI Grok
3 /openai/v1/responses OpenAI Responses GPT-5.x
4 /anthropic/v1/messages Anthropic Messages Claude

ルート2・3はbase_url/openai/v1で共通し、使用するSDKメソッド(chat.completions.create / responses.create)によって実際のパスが分かれます。

ルーティングの判定はモデルIDのプレフィックスで行います。

OPENAI_V1_PREFIXES = ("google.gemma-4", "xai.")
RESPONSES_PREFIXES = ("openai.gpt-5.",)
ANTHROPIC_PREFIXES = ("anthropic.",)

def _get_route(model_id: str) -> str:
    if any(model_id.startswith(p) for p in ANTHROPIC_PREFIXES):
        return "anthropic"
    if any(model_id.startswith(p) for p in RESPONSES_PREFIXES):
        return "responses"
    if any(model_id.startswith(p) for p in OPENAI_V1_PREFIXES):
        return "openai_v1"
    return "v1"

各ルートの呼び出しコード

ルート 1・2: OpenAI Chat Completions/v1 または /openai/v1

from openai import OpenAI
from aws_bedrock_token_generator import provide_token

REGION = "us-east-1"
BASE_URL = f"https://bedrock-mantle.{REGION}.api.aws"

token = provide_token(region=REGION)
client = OpenAI(base_url=f"{BASE_URL}/v1", api_key=token)

response = client.chat.completions.create(
    model="deepseek.v3.2",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Say hello in Japanese, one sentence only."},
    ],
    max_tokens=4096,
)
print(response.choices[0].message.content)

ルート2(Gemma 4、Grok)の場合はbase_url/openai/v1に変更します。

token = provide_token(region=REGION)
client = OpenAI(base_url=f"{BASE_URL}/openai/v1", api_key=token)

response = client.chat.completions.create(
    model="google.gemma-4-31b",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Say hello in Japanese, one sentence only."},
    ],
    max_tokens=4096,
)
print(response.choices[0].message.content)

ルート 3: OpenAI Responses API/openai/v1

token = provide_token(region=REGION)
client = OpenAI(base_url=f"{BASE_URL}/openai/v1", api_key=token)

response = client.responses.create(
    model="openai.gpt-5.4",
    input=[
        {"role": "developer", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Say hello in Japanese, one sentence only."},
    ],
    max_output_tokens=4096,
)
print(response.output_text)

ルート 4: Anthropic Messages API/anthropic

import anthropic
from aws_bedrock_token_generator import provide_token

REGION = "us-east-1"
BASE_URL = f"https://bedrock-mantle.{REGION}.api.aws"

token = provide_token(region=REGION)
client = anthropic.Anthropic(base_url=f"{BASE_URL}/anthropic", api_key=token)

response = client.messages.create(
    model="anthropic.claude-haiku-4-5",
    max_tokens=4096,
    system="You are a helpful assistant.",
    messages=[{"role": "user", "content": "Say hello in Japanese, one sentence only."}],
)
print(response.content[0].text)

Lambda 関数の全体コード(CloudFormation テンプレート)

以下のCloudFormationテンプレートでLambda関数をデプロイできます。コードはZipFileでインライン記述しているため、追加のzipアップロードは不要です。

CloudFormation テンプレート(Lambda 関数)
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
  Bedrock Mantle Lambda (Layer版) — Docker/ECR不要、標準Pythonランタイム + レイヤーのみ

Parameters:
  FunctionName:
    Type: String
    Default: bedrock-mantle-layer-test
  LayerArn:
    Type: String
    Default: arn:aws:lambda:us-east-1:123456789012:layer:bedrock-mantle-sdk-layer-arm64:1

Resources:
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${FunctionName}-role"
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
      Policies:
        - PolicyName: BedrockMantle
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - bedrock-mantle:CreateInference
                  - bedrock-mantle:CallWithBearerToken
                Resource: "*"

  MantleFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Ref FunctionName
      Runtime: python3.13
      Handler: index.handler
      Architectures:
        - arm64
      MemorySize: 512
      Timeout: 120
      Role: !GetAtt LambdaRole.Arn
      Layers:
        - !Ref LayerArn
      Environment:
        Variables:
          BEDROCK_REGION: us-east-1
      Code:
        ZipFile: |
          import json
          import os
          import time

          import anthropic
          from aws_bedrock_token_generator import provide_token
          from openai import OpenAI

          REGION = os.environ.get("BEDROCK_REGION", "us-east-1")
          BASE_URL = f"https://bedrock-mantle.{REGION}.api.aws"

          OPENAI_V1_PREFIXES = ("google.gemma-4", "xai.")
          RESPONSES_PREFIXES = ("openai.gpt-5.",)
          ANTHROPIC_PREFIXES = ("anthropic.",)

          def _get_route(model_id: str) -> str:
              if any(model_id.startswith(p) for p in ANTHROPIC_PREFIXES):
                  return "anthropic"
              if any(model_id.startswith(p) for p in RESPONSES_PREFIXES):
                  return "responses"
              if any(model_id.startswith(p) for p in OPENAI_V1_PREFIXES):
                  return "openai_v1"
              return "v1"

          def _openai_client(base_path: str) -> OpenAI:
              token = provide_token(region=REGION)
              return OpenAI(base_url=f"{BASE_URL}{base_path}", api_key=token)

          def _anthropic_client() -> anthropic.Anthropic:
              token = provide_token(region=REGION)
              return anthropic.Anthropic(
                  base_url=f"{BASE_URL}/anthropic", api_key=token
              )

          def invoke_anthropic(model_id, system, user_message):
              client = _anthropic_client()
              response = client.messages.create(
                  model=model_id,
                  max_tokens=4096,
                  system=system,
                  messages=[{"role": "user", "content": user_message}],
              )
              text = "".join(b.text for b in response.content if b.type == "text")
              return {
                  "text": text,
                  "usage": {
                      "inputTokens": response.usage.input_tokens,
                      "outputTokens": response.usage.output_tokens,
                      "totalTokens": response.usage.input_tokens + response.usage.output_tokens,
                  },
              }

          def invoke_responses(model_id, system, user_message):
              client = _openai_client("/openai/v1")
              response = client.responses.create(
                  model=model_id,
                  input=[
                      {"role": "developer", "content": system},
                      {"role": "user", "content": user_message},
                  ],
                  max_output_tokens=4096,
              )
              text = response.output_text or ""
              return {
                  "text": text,
                  "usage": {
                      "inputTokens": response.usage.input_tokens,
                      "outputTokens": response.usage.output_tokens,
                      "totalTokens": response.usage.total_tokens,
                  },
              }

          def invoke_chat(model_id, system, user_message, base_path):
              client = _openai_client(base_path)
              response = client.chat.completions.create(
                  model=model_id,
                  messages=[
                      {"role": "system", "content": system},
                      {"role": "user", "content": user_message},
                  ],
                  max_tokens=4096,
              )
              text = response.choices[0].message.content or ""
              usage = None
              if response.usage:
                  usage = {
                      "inputTokens": response.usage.prompt_tokens,
                      "outputTokens": response.usage.completion_tokens,
                      "totalTokens": response.usage.total_tokens,
                  }
              return {"text": text, "usage": usage}

          def handler(event, context):
              body = event if isinstance(event, dict) else json.loads(event.get("body", "{}"))

              model_id = body["modelId"]
              system = body.get("system", "You are a helpful assistant.")
              user_message = body.get("message", "Hello!")

              route = _get_route(model_id)
              start = time.time()

              try:
                  if route == "anthropic":
                      result = invoke_anthropic(model_id, system, user_message)
                      path = "/anthropic/v1/messages"
                  elif route == "responses":
                      result = invoke_responses(model_id, system, user_message)
                      path = "/openai/v1/responses"
                  elif route == "openai_v1":
                      result = invoke_chat(model_id, system, user_message, "/openai/v1")
                      path = "/openai/v1/chat/completions"
                  else:
                      result = invoke_chat(model_id, system, user_message, "/v1")
                      path = "/v1/chat/completions"

                  elapsed_ms = int((time.time() - start) * 1000)

                  return {
                      "modelId": model_id,
                      "route": path,
                      "text": result["text"],
                      "elapsedMs": elapsed_ms,
                      "usage": result.get("usage"),
                  }

              except Exception as e:
                  elapsed_ms = int((time.time() - start) * 1000)
                  return {
                      "modelId": model_id,
                      "route": route,
                      "error": str(e),
                      "elapsedMs": elapsed_ms,
                  }

Outputs:
  FunctionArn:
    Value: !GetAtt MantleFunction.Arn
  FunctionName:
    Value: !Ref MantleFunction

動作結果

aws lambda invoke --function-name bedrock-mantle-layer-test --region us-east-1 \
  --cli-binary-format raw-in-base64-out \
  --payload '{"modelId": "deepseek.v3.2", "message": "Say hello in Japanese, one sentence only."}' \
  output.json

ルート 1: /v1/chat/completions(DeepSeek v3.2)

{
  "modelId": "deepseek.v3.2",
  "route": "/v1/chat/completions",
  "text": "こんにちは。",
  "elapsedMs": 541,
  "usage": {
    "inputTokens": 19,
    "outputTokens": 7,
    "totalTokens": 26
  }
}

ルート 2: /openai/v1/chat/completions(Gemma 4)

{
  "modelId": "google.gemma-4-31b",
  "route": "/openai/v1/chat/completions",
  "text": "こんにちは (Konnichiwa).",
  "elapsedMs": 401,
  "usage": {
    "inputTokens": 34,
    "outputTokens": 8,
    "totalTokens": 42
  }
}

ルート 3: /openai/v1/responses(GPT-5.4)

{
  "modelId": "openai.gpt-5.4",
  "route": "/openai/v1/responses",
  "text": "こんにちは。",
  "elapsedMs": 346,
  "usage": {
    "inputTokens": 25,
    "outputTokens": 6,
    "totalTokens": 31
  }
}

ルート 4: /anthropic/v1/messages(Claude Haiku 4.5)

{
  "modelId": "anthropic.claude-haiku-4-5",
  "route": "/anthropic/v1/messages",
  "text": "こんにちは。",
  "elapsedMs": 729,
  "usage": {
    "inputTokens": 22,
    "outputTokens": 9,
    "totalTokens": 31
  }
}

4つのルートすべてで正常にレスポンスを取得できました。レイテンシは参考値です。

全モデルテスト結果

47モデルを一括テストし、すべて正常動作を確認しました。Claude Fable 5は別記事で紹介しているため本テストから除外しています。

https://dev.classmethod.jp/articles/bedrock-mantle-claude-fable-5/

本記事の検証はus-east-1で実施しています。以下は2026年7月時点の検証結果です。レイテンシは各モデル1回の計測値であり、参考値です。

全モデルテスト結果(47モデル)
モデル ID ルート レイテンシ 結果
openai.gpt-oss-120b /v1/chat/completions 2427ms OK
openai.gpt-oss-20b /v1/chat/completions 723ms OK
openai.gpt-oss-safeguard-120b /v1/chat/completions 723ms OK
openai.gpt-oss-safeguard-20b /v1/chat/completions 810ms OK
google.gemma-4-31b /openai/v1/chat/completions 481ms OK
google.gemma-4-26b-a4b /openai/v1/chat/completions 392ms OK
google.gemma-4-e2b /openai/v1/chat/completions 475ms OK
google.gemma-3-27b-it /v1/chat/completions 528ms OK
google.gemma-3-12b-it /v1/chat/completions 608ms OK
google.gemma-3-4b-it /v1/chat/completions 817ms OK
xai.grok-4.3 /openai/v1/chat/completions 2624ms OK
deepseek.v3.2 /v1/chat/completions 474ms OK
deepseek.v3.1 /v1/chat/completions 301ms OK
zai.glm-5 /v1/chat/completions 2614ms OK
zai.glm-4.7 /v1/chat/completions 311ms OK
zai.glm-4.7-flash /v1/chat/completions 367ms OK
zai.glm-4.6 /v1/chat/completions 439ms OK
minimax.minimax-m2 /v1/chat/completions 1916ms OK
minimax.minimax-m2.1 /v1/chat/completions 3625ms OK
minimax.minimax-m2.5 /v1/chat/completions 1687ms OK
mistral.devstral-2-123b /v1/chat/completions 299ms OK
mistral.magistral-small-2509 /v1/chat/completions 286ms OK
mistral.mistral-large-3-675b-instruct /v1/chat/completions 389ms OK
mistral.ministral-3-14b-instruct /v1/chat/completions 403ms OK
mistral.ministral-3-8b-instruct /v1/chat/completions 361ms OK
mistral.ministral-3-3b-instruct /v1/chat/completions 350ms OK
mistral.voxtral-mini-3b-2507 /v1/chat/completions 210ms OK
mistral.voxtral-small-24b-2507 /v1/chat/completions 377ms OK
nvidia.nemotron-super-3-120b /v1/chat/completions 341ms OK
nvidia.nemotron-nano-3-30b /v1/chat/completions 447ms OK
nvidia.nemotron-nano-9b-v2 /v1/chat/completions 5749ms OK
nvidia.nemotron-nano-12b-v2 /v1/chat/completions 280ms OK
qwen.qwen3-coder-next /v1/chat/completions 5547ms OK
qwen.qwen3-coder-480b-a35b-instruct /v1/chat/completions 410ms OK
qwen.qwen3-coder-30b-a3b-instruct /v1/chat/completions 327ms OK
qwen.qwen3-32b /v1/chat/completions 3108ms OK
qwen.qwen3-235b-a22b-2507 /v1/chat/completions 277ms OK
qwen.qwen3-vl-235b-a22b-instruct /v1/chat/completions 443ms OK
qwen.qwen3-next-80b-a3b-instruct /v1/chat/completions 284ms OK
moonshotai.kimi-k2-thinking /v1/chat/completions 913ms OK
moonshotai.kimi-k2.5 /v1/chat/completions 5989ms OK
anthropic.claude-sonnet-5 /anthropic/v1/messages 1970ms OK
anthropic.claude-opus-4-8 /anthropic/v1/messages 2334ms OK
anthropic.claude-opus-4-7 /anthropic/v1/messages 940ms OK
anthropic.claude-haiku-4-5 /anthropic/v1/messages 670ms OK
openai.gpt-5.4 /openai/v1/responses 1506ms OK
openai.gpt-5.5 /openai/v1/responses 3308ms OK

計測条件:Lambda 512MB / arm64 / warm start / 入力 "Say hello in Japanese, one sentence only." / ストリーミングなし

まとめ

Bedrock Mantle経由で提供されるモデルが増えている現状を踏まえ、LambdaからBedrock Mantleを利用する手順を整理しました。Bedrock Mantleでは、Bearer Token認証とOpenAI / Anthropic互換APIを使ってモデルを呼び出します。aws-bedrock-token-generator、OpenAI SDK、Anthropic SDKをLambdaレイヤーとして用意しておけば、DockerイメージやECRを使わず、関数コードから呼び出せます。

本検証では、Gemma 4、Grok、GPT-5系、DeepSeek、Claudeなど47モデルを対象に、4つのAPIルートの呼び分けを確認しました。モデルIDのプレフィックスでルートを判定することで、同一のLambda関数からOpenAI Chat Completions、OpenAI Responses、Anthropic Messagesの各APIを統一的に扱えます。

Bedrock Runtimeに加えてBedrock Mantleも選択肢に入れることで、Lambdaから利用できるモデルの幅が広がります。マルチモデルなLLMアプリケーションをLambdaで構成する際の参考になれば幸いです。

参考リンク

https://pypi.org/project/aws-bedrock-token-generator/

https://github.com/aws/aws-bedrock-token-generator-python


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事