AgentCore Runtime Instances がリリースされました!

AgentCore Runtime Instances がリリースされました!

Amazon Bedrock AgentCore Runtime のInstances機能がリリースされました!
2026.08.09

はじめに

こんにちは、ドライブ練習中のコンサルティング部の神野です。

2026年8月7日、Amazon Bedrock AgentCore Runtimeの新しいコンピュートタイプであるInstancesが一般利用可能になりました!

https://aws.amazon.com/jp/about-aws/whats-new/2026/08/aws-bedrock-agentcore-runtime-instances-generally-available/

自分のAWSアカウント内のEC2インスタンス上でエージェントを動かせるコンピュートタイプで、プロビジョニングやパッチ適用はAgentCore側が管理してくれるというものです。
LambdaのManaged Instance機能に近そうな感覚ですね。

発表を読んで最初に思ったのが、これはどういうときに使うんだろう?と気になりました。
AgentCore Runtimeは元々サーバーレスなmicroVMでエージェントが動く仕組みで、API経由で呼び出され、数分で処理が完了するエージェントであればそれで十分だと思います。永続ストレージや共有ファイルシステムもmicroVMで使えます。Managed session storage(Preview)を使えば停止・再開をまたいでファイルを保持できますし、EFSやS3 Filesをマウントすれば複数のエージェントから同じデータを共有できます。

https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-filesystem-configurations.html

では何がInstances固有なのかをドキュメントから整理すると、下記になります。

  • 複数のRuntimeを同一のEC2インスタンスに配置できる
    • ファイル共有自体はmicroVM + EFS等でも可能。同じマシンのローカルディスクを直接共有しながら同居できるのはInstancesのみ
  • セッションのコンピュートが最大14日間動き続けられる(microVMは8時間)
    • 14日はすごい・・・!!!!長時間動かしたい処理に使う・・・??
  • GPUインスタンスを使える。ドライバはAgentCoreが自動構成
  • EC2とEBSが自分のAWSアカウント内で起動するため、RI/SPが適用可能

今回は、同一インスタンスへの同居、アカウント内EBSによる中断再開、そして同居したエージェント同士の直接通信(A2A)の3つを実際に動かして確かめます。いずれも「microVMでも似たことができるのでは?」と思われそうな項目なので、何が違うのかを意識しながら検証します。

前提

検証環境は下記の通りです。

項目 内容
リージョン us-east-1(バージニア北部)
Python 3.13
boto3 / botocore 1.43.66以降
デプロイ方式 S3ソース(zipパッケージ)
インスタンスタイプ m7g.large(ARM64)

対応リージョンは、米国東部(バージニア北部、オハイオ)、米国西部(オレゴン)、アジアパシフィック(ムンバイ、シンガポール、シドニー、東京)、欧州(フランクフルト、アイルランド)です。東京リージョンも初期対応しています。

料金は、EC2インスタンスとEBSが自分のアカウントに直接課金され、それに加えてAgentCoreの管理料金がかかるモデルです。Savings PlansやRI、ODCRが適用されるのはEC2部分のみで、管理料金は公開オンデマンド料金を基準に計算されます。また、セッション停止中もEBSの料金は継続する点に注意してください。

https://aws.amazon.com/bedrock/agentcore/pricing/

Instancesの仕組み

概念が少し複雑なので、今回作る検証環境の全体像を図にしました。

CleanShot 2026-08-08 at 22.41.24@2x

Capacity Providerは、EC2インフラの定義(OS、インスタンスタイプ、VPC、EBSボリューム)を保持する再利用可能なテンプレートです。図の中央にいるのがこれで、複数のAgent Runtimeから参照できます。

Agent Runtimeは動かすエージェントそのものの定義です。作成時にCapacity Providerを紐付けるとコンピュートタイプがInstancesになります。図ではwriterとreviewerの2つを作っています。

Sessionは呼び出し時に指定するruntimeSessionIdごとの単位で、初回呼び出し時にセッションごとに1台のEC2インスタンスが自分のアカウント内にプロビジョニングされます。図の①②のように同じセッションIDで2つのRuntimeを呼び出すと、両方のエージェントが同じインスタンスに同居し、③④のように共有EBSボリューム経由でファイルを受け渡せたりもします。

https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-instances-how-it-works.html

microVMと比較すると違いは次の通りです。

項目 microVM Instances
セッション最大時間 8時間 14日
アーキテクチャ arm64のみ x86_64 / arm64
GPU 非対応 対応(ドライバ自動構成)
エージェント数 1ランタイム1エージェント 1セッションに複数エージェント
ネットワーク PUBLIC or VPC VPC
課金 AgentCoreの従量課金 EC2が自アカウント課金(EC2部分にSavings Plansも適用可)

x86_64も選べるのは面白いですね。x86_64が選べるから・・・!とはならないかもしれないですが、確かにEC2だから選択できるかと思いました。実際に動かした結果は補足に載せています。

構築

図の構成を作っていきます。エージェントは、Bedrockで俳句を詠んで共有ボリュームに書き込むwriterと、それを読み取って講評するreviewerの2つです。コード生成とコードレビューのような、成果物ファイルを受け渡すパイプラインの最小構成という見立てです。

IAMロールの作成

Instancesでは通常の実行ロールに加えて、2つのロールが必要になります。

ロール 用途
インフラロール(Operatorロール) AgentCoreがEC2インスタンスを起動・管理するために引き受けるロール
インスタンスプロファイル EC2インスタンスにアタッチされ、システムログ収集に使われるロール
実行ロール エージェントコード自体の権限(Bedrock呼び出しなど)

コンソールから作成する場合はデフォルトロールを自動作成してくれるのですが、今回はCLIで進めるため自分で作成しました。ここで一度つまずきました。インフラロールにEC2権限とPassRoleだけ付けてCapacity Providerを作成したところ、CREATE_FAILED になり、下記のエラーが出ていました。

statusReason
EventBridge managed rule creation failed. User: arn:aws:sts::123456789012:assumed-role/agentcore-cp-operator-role/AgentCore is not authorized to perform: events:PutRule on resource: arn:aws:events:us-east-1:123456789012:rule/agentcore-lifecycle-events-123456789012

AgentCoreはインスタンスのライフサイクルイベントを監視するためにEventBridgeのマネージドルールを作るようで、インフラロールにEventBridge権限も必要でした。最終的なロール定義は下記の折りたたみに置いておきます。

IAMロールの作成コマンドとポリシー全体

インフラロールと実行ロールの信頼ポリシーは共通で、bedrock-agentcore.amazonaws.com を信頼させます。

trust-agentcore.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "bedrock-agentcore.amazonaws.com"},
    "Action": "sts:AssumeRole",
    "Condition": {
      "StringEquals": {"aws:SourceAccount": "123456789012"},
      "ArnLike": {"aws:SourceArn": "arn:aws:bedrock-agentcore:us-east-1:123456789012:*"}
    }
  }]
}

インフラロールにはEC2の管理権限と、PassRole・EventBridge権限を付与します。

実行コマンド
aws iam create-role --role-name agentcore-cp-operator-role \
  --assume-role-policy-document file://trust-agentcore.json
aws iam attach-role-policy --role-name agentcore-cp-operator-role \
  --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess
aws iam put-role-policy --role-name agentcore-cp-operator-role \
  --policy-name operator-extra --policy-document file://operator-extra.json
operator-extra.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["iam:PassRole", "iam:GetInstanceProfile", "iam:CreateServiceLinkedRole"],
      "Resource": "*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "events:PutRule", "events:PutTargets", "events:RemoveTargets",
        "events:DeleteRule", "events:DescribeRule", "events:TagResource",
        "events:ListTargetsByRule"
      ],
      "Resource": "arn:aws:events:us-east-1:123456789012:rule/agentcore-*"
    }
  ]
}

インスタンスプロファイルは信頼先がEC2で、ロール作成後にインスタンスプロファイルへの登録が必要です。

trust-ec2.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "ec2.amazonaws.com"},
    "Action": "sts:AssumeRole"
  }]
}
インスタンスプロファイルの作成
aws iam create-role --role-name agentcore-cp-instance-role \
  --assume-role-policy-document file://trust-ec2.json
aws iam attach-role-policy --role-name agentcore-cp-instance-role \
  --policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
aws iam attach-role-policy --role-name agentcore-cp-instance-role \
  --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
aws iam create-instance-profile --instance-profile-name agentcore-cp-instance-profile
aws iam add-role-to-instance-profile \
  --instance-profile-name agentcore-cp-instance-profile \
  --role-name agentcore-cp-instance-role

実行ロールはインフラロールと同じ信頼ポリシー(trust-agentcore.json)で作成します。権限は通常のAgentCore Runtime実行ロールと同じで、CloudWatch Logs / X-Ray / Bedrockモデル呼び出しあたりを付与しています。

https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-permissions.html

エージェントの実装とデプロイ

エージェントの書き方は通常のAgentCore Runtimeと同じで、@app.entrypoint を付けたハンドラーを用意する形となります。

agents/writer/main.py(抜粋)
@app.entrypoint
def handler(event, context):
    haiku = compose_haiku(event.get("theme", "クラウド"))  # Bedrockで俳句を生成

    # 共有ボリュームに書き込む(同じセッションのreviewerが読む)
    session_dir = SHARED_DIR / context.session_id  # SHARED_DIR = /mnt/shared
    session_dir.mkdir(parents=True, exist_ok=True)
    (session_dir / "haiku.txt").write_text(haiku, encoding="utf-8")

    return {
        "agent": "writer",
        "haiku": haiku,
        "hostname": socket.gethostname(),   # 同一インスタンスの確認用
        "arch": platform.machine(),          # CPUアーキテクチャの確認用
    }

/mnt/shared が、Capacity Providerで定義した永続EBSボリュームのマウントパスです。reviewerは逆にこのファイルを読んで講評を返すだけで、エージェント間の通信処理は書いていません。検証で使うため、どのホストで動いたかのhostnameとCPUアーキテクチャのarchも返すようにしています。

writer / reviewer のコード全体
agents/writer/main.py
"""Writer agent: 成果物を生成して共有ボリュームに書き込む"""
import platform
import socket
from pathlib import Path

import boto3
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()

SHARED_DIR = Path("/mnt/shared")
MODEL_ID = "us.anthropic.claude-haiku-4-5-20251001-v1:0"

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")

@app.entrypoint
def handler(event, context):
    theme = event.get("theme", "クラウド")
    session_id = context.session_id

    # Bedrockで成果物(俳句)を生成
    response = bedrock.converse(
        modelId=MODEL_ID,
        messages=[
            {
                "role": "user",
                "content": [
                    {"text": f"「{theme}」をテーマに俳句を1句詠んでください。俳句のみ出力してください。"}
                ],
            }
        ],
    )
    haiku = response["output"]["message"]["content"][0]["text"]

    # 共有ボリュームに書き込む(同じセッションのreviewerが読む)
    session_dir = SHARED_DIR / session_id
    session_dir.mkdir(parents=True, exist_ok=True)
    (session_dir / "haiku.txt").write_text(haiku, encoding="utf-8")

    return {
        "agent": "writer",
        "haiku": haiku,
        "hostname": socket.gethostname(),
        "arch": platform.machine(),
        "wrote_to": str(session_dir / "haiku.txt"),
    }

if __name__ == "__main__":
    app.run()
agents/reviewer/main.py
"""Reviewer agent: 共有ボリュームから成果物を読み取ってレビューする"""
import platform
import socket
from pathlib import Path

import boto3
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()

SHARED_DIR = Path("/mnt/shared")
MODEL_ID = "us.anthropic.claude-haiku-4-5-20251001-v1:0"

bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")

@app.entrypoint
def handler(event, context):
    session_id = context.session_id
    haiku_path = SHARED_DIR / session_id / "haiku.txt"

    if not haiku_path.exists():
        return {
            "agent": "reviewer",
            "error": f"{haiku_path} が見つかりません。先にwriterを呼び出してください。",
            "hostname": socket.gethostname(),
        }

    # writerが書いた成果物を共有ボリュームから読み取る
    haiku = haiku_path.read_text(encoding="utf-8")

    response = bedrock.converse(
        modelId=MODEL_ID,
        messages=[
            {
                "role": "user",
                "content": [
                    {"text": f"次の俳句を俳人の視点で講評してください。3文以内で。\n\n{haiku}"}
                ],
            }
        ],
    )
    review = response["output"]["message"]["content"][0]["text"]

    return {
        "agent": "reviewer",
        "haiku_read_from_shared_volume": haiku,
        "review": review,
        "hostname": socket.gethostname(),
        "arch": platform.machine(),
    }

if __name__ == "__main__":
    app.run()

プロジェクトはuvでセットアップします。依存パッケージは bedrock-agentcore と boto3 の2つだけです。

プロジェクトのセットアップ
uv init agentcore-instances-demo --python 3.13
cd agentcore-instances-demo
uv add bedrock-agentcore boto3

デプロイはS3ソース(zip)にしました。ARM64インスタンスなので、uv で aarch64 向けの依存関係を集めてパッケージングし、S3にアップロードします。

実行コマンド
uv pip install \
  --python-platform aarch64-manylinux2014 \
  --python-version 3.13 \
  --target build/deps \
  --only-binary=:all: \
  bedrock-agentcore boto3

cd build/deps && zip -qr ../writer.zip . && cd ../..
cd agents/writer && zip -q ../../build/writer.zip main.py && cd ../..

aws s3 cp build/writer.zip s3://bedrock-agentcore-code-123456789012-us-east-1/writer/deployment_package.zip

Capacity ProviderとAgent Runtimeの作成

Capacity Providerを作成します。

Capacity Providerの作成(抜粋)
response = client.create_capacity_provider(
    name="haiku_demo_cp",
    permissionsConfiguration={"capacityProviderOperatorRoleArn": OPERATOR_ROLE_ARN},
    computeConfiguration={
        "ec2Configuration": {
            "launchTemplateSource": {
                "launchParameters": {
                    "operatingSystem": "LINUX_ARM64",
                    "instanceRequirements": {"allowedInstanceTypes": ["m7g.large"]},
                    "instanceProfileArn": INSTANCE_PROFILE_ARN,
                }
            },
            "vpcConfiguration": {"subnets": [SUBNET_ID], "securityGroups": [SG_ID]},
            "volumes": [{"ebsConfiguration": {"name": "shared", "sizeGiB": 10, "volumeType": "gp3"}}],
        }
    },
)

パラメータとしては下記のように設定します。

設定項目 設定値 説明
operatingSystem LINUX_ARM64 LINUX_X86_64 も選択可能
allowedInstanceTypes m7g.large 最大30タイプまで指定可能。GPU系(g5, g6等)もここで指定
vpcConfiguration サブネット・SG Instancesはネットワークが必ずVPCになる
volumes shared / 10GiB / gp3 名前付き永続ボリューム。最大5つ定義可能

1〜2分ほどで READY になるので、これを紐付けてRuntimeを2つ作成します。

Agent Runtimeの作成(抜粋)
for name in ["writer", "reviewer"]:
    client.create_agent_runtime(
        agentRuntimeName=f"haiku_{name}",
        roleArn=EXEC_ROLE_ARN,
        agentRuntimeArtifact={
            "codeConfiguration": {
                "code": {"s3": {"bucket": BUCKET, "prefix": f"{name}/deployment_package.zip"}},
                "runtime": "PYTHON_3_13",
                "entryPoint": ["main.py"],
            }
        },
        capacityProviderConfiguration={"capacityProviderArn": CP_ARN},
        filesystemConfigurations=[
            {"capacityProviderVolume": {"volumeName": "shared", "mountPath": "/mnt/shared"}}
        ],
    )

capacityProviderConfiguration にARNを渡すとコンピュートタイプがInstancesになります(作成後の変更は不可)。filesystemConfigurations で shared ボリュームを /mnt/shared にマウントしています(マウントパスは /mnt 直下の1階層のみ)。数十秒で READY になり、構築は完了です。

検証1 同一インスタンスでの複数エージェント連携

図の①〜④の流れを実際に動かします。writerとreviewerを同じ runtimeSessionId(33文字以上)で呼び出しています。セッションIDごとに1台のEC2インスタンスが対応するため、同じIDで呼べば2つのエージェントが同じインスタンスに載り、別のIDにすると別インスタンスに分かれて共有ボリュームの中身も見えません。

scripts/03_invoke_agents.py
import json

import boto3

client = boto3.client("bedrock-agentcore", region_name="us-east-1")

# 33文字以上のセッションID。同じIDを使い回すことで同一EC2インスタンスで実行
session_id = "haiku-collab-session-2026-08-07-demo-001"

response = client.invoke_agent_runtime(
    agentRuntimeArn=WRITER_ARN,
    runtimeSessionId=session_id,
    payload=json.dumps({"theme": "夏のEC2インスタンス"}).encode(),
    qualifier="DEFAULT",
)
print(json.loads(response["response"].read()))

response = client.invoke_agent_runtime(
    agentRuntimeArn=REVIEWER_ARN,
    runtimeSessionId=session_id,
    payload=json.dumps({}).encode(),
    qualifier="DEFAULT",
)
print(json.loads(response["response"].read()))
実行コマンド
uv run python scripts/03_invoke_agents.py
writerの実行結果(所要時間68.2秒)
{
  "agent": "writer",
  "haiku": "クラウド冷え\nワーカー立ちて\n夏涼し",
  "hostname": "ip-172-31-3-249.ec2.internal",
  "arch": "aarch64",
  "wrote_to": "/mnt/shared/haiku-collab-session-2026-08-07-demo-001/haiku.txt"
}
reviewerの実行結果(所要時間8.5秒)
{
  "agent": "reviewer",
  "haiku_read_from_shared_volume": "クラウド冷え\nワーカー立ちて\n夏涼し",
  "review": "この句は季語「夏涼し」に現代的な「クラウド」「ワーカー」を組み合わせた意欲的な試みですが、造語的な措辞が唐突で、俳句の伝統的な余韻に欠けます。(以下略)",
  "hostname": "ip-172-31-3-249.ec2.internal",
  "arch": "aarch64"
}

結果はこの2つのJSONに集約されています。両方のhostnameが同じ ip-172-31-3-249 なので、別々のRuntimeとしてデプロイした2つのエージェントが同一EC2インスタンスに同居しています。reviewerはwriterの書いたファイルを共有ボリューム経由でそのまま読めていますね。初回のwriter呼び出しはEC2のプロビジョニングが走るため68秒、2回目のreviewerは同じインスタンスを使い回すので8.5秒でした。

誤解のないように書いておくと、ファイル共有自体はmicroVM + EFSやS3 Filesでも実現できます。Instancesとの違いは、同一インスタンスにアタッチされたEBSをネットワーク越しではなくローカルファイルシステムとして共有できる点が違いとしてあります。中間生成物の読み書きが大量に発生するビルドやデータ変換のパイプラインではI/O性能差が出る可能性があります(今回は計測していませんが・・・)。

https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-instances-security.html

EC2コンソールでのインスタンスの見え方

このインスタンスはEC2側からも確認できます。ただし EC2 managed instances という扱いで、デフォルトでは一覧に表示されないため、アカウント設定の変更が必要です。

実行コマンド
aws ec2 modify-managed-resource-visibility --default-visibility visible
実行結果(describe-instances)
|  Id       |  i-0cb7e904f61796067              |
|  Ip       |  172.31.3.249                     |
|  Operator |  bedrock-agentcore.amazonaws.com  |
|  State    |  running                          |
|  Type     |  m7g.large                        |

Operator フィールドでAgentCore管理のインスタンスだとわかります。EBSもルートボリューム16GiBと共有ボリューム10GiBがアタッチされていました。自分のAWSアカウント内で起動しているインスタンスなので、EC2部分がSavings Plansの適用対象になるという理屈も確認できます。

検証2 セッションの中断と再開

次に、処理を途中で止めてコストを抑え、後日同じ状態から再開できるかを確かめます。仕様上は、セッション上のすべてのRuntimeが停止するとEC2インスタンスも停止しますが、EBSボリュームは保持され、同じセッションIDで再呼び出しすると新しいインスタンスに再アタッチされます。

reviewer RuntimeをStopRuntimeSessionで停止し、EC2インスタンスが stopped になったのを確認してから、同じセッションIDで再度呼び出しました。

実行コマンド
aws bedrock-agentcore stop-runtime-session \
  --agent-runtime-arn arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/haiku_reviewer-XAGNbiCcJJ \
  --runtime-session-id haiku-collab-session-2026-08-07-demo-001
再呼び出し(セッションIDは最初と同じものを指定)
response = client.invoke_agent_runtime(
    agentRuntimeArn=REVIEWER_ARN,
    runtimeSessionId="haiku-collab-session-2026-08-07-demo-001",
    payload=json.dumps({}).encode(),
    qualifier="DEFAULT",
)
再呼び出しの実行結果(所要時間107.5秒)
haiku: クラウド冷え / ワーカー立ちて / 夏涼し
hostname: ip-172-31-15-99.ec2.internal

writerが最初に書き込んだデータがそのまま読めました。hostnameが前回の ip-172-31-3-249 から変わっている通り、新しいインスタンスが立ち上がってEBSが再アタッチされています。復帰まで約107秒なので、数日にまたがるバッチや承認待ちで長時間止まるワークフローを、待機中はコンピュート料金を払わずに保持する使い方は現実的だと思います。

停止・再開をまたいだファイル保持だけならmicroVMのManaged session storage(Preview)でもできますが、あちらは14日間呼び出しがないとリセットされ、Runtimeのバージョン更新でも消えます。InstancesのEBSはセッションを削除するまで自分の管理下に残る点が違いです。

検証3 エージェント間の直接通信とA2A

最後に、同居できるならA2Aのようなエージェント間通信も1台の中で完結できるのでは?という疑問を確かめます。

ソケット直結はできない

バックグラウンドで9000番ポートにHTTPサーバーを起動するエージェント(pingserver)と、同じセッションIDで同居してそこへ接続を試みるエージェント(pingclient)を用意しました。呼び出し方は検証1と同じ形式です。

呼び出し(同じセッションIDでpingserver → pingclientの順に実行)
session_id = "a2a-demo-session-2026-08-07-test-00001"
for arn in [PINGSERVER_ARN, PINGCLIENT_ARN]:
    response = client.invoke_agent_runtime(
        agentRuntimeArn=arn,
        runtimeSessionId=session_id,
        payload=json.dumps({}).encode(),
        qualifier="DEFAULT",
    )
    print(json.loads(response["response"].read()))

結果がこちらです。

クライアント側エージェントの実行結果
{
  "agent": "pingclient",
  "hostname": "ip-172-31-5-39.ec2.internal",
  "listening_ports_visible": [8080],
  "via_localhost": {"ok": false, "error": "URLError: <urlopen error [Errno 111] Connection refused>"},
  "via_private_ip": {"ok": false, "error": "URLError: <urlopen error [Errno 111] Connection refused>"}
}

サーバー側も同じhostnameを返しているので同居は成立していますが、localhostもプライベートIPも Connection refused でした。サーバー自身からの自己接続は成功し、クライアントから見えるLISTENポートは自分の8080だけです。

つまり今回の検証環境では、同居していてもエージェントごとにネットワーク名前空間が分離されており、ソケットの直結はできないと考えられますね。ドキュメントの「同居エージェント間に境界はない」はファイルシステムやクレデンシャルの話で、ネットワークは互いに見えない作りに見えました。

A2AプロトコルのRuntimeとして呼び出す

一方、AgentCore RuntimeのserverProtocolにはA2Aがあり、Instancesでも指定できます。テーマを受け取って俳句を詠むA2Aエージェント(0.0.0.0:9000でJSON-RPC 2.0を受ける実装)をデプロイして呼び出しました。

A2Aランタイムの作成(抜粋)
client.create_agent_runtime(
    agentRuntimeName="a2a_haiku_poet",
    protocolConfiguration={"serverProtocol": "A2A"},
    capacityProviderConfiguration={"capacityProviderArn": CP_ARN},
    ...
)

InvokeAgentRuntimeはA2Aのペイロードをそのまま透過するので、JSON-RPC 2.0のメッセージを組み立てて渡します。

JSON-RPCペイロードでの呼び出し
payload = {
    "jsonrpc": "2.0",
    "id": "req-001",
    "method": "message/send",
    "params": {
        "message": {
            "role": "user",
            "parts": [{"kind": "text", "text": "秋のデータセンター"}],
            "messageId": "msg-001",
        }
    },
}
response = client.invoke_agent_runtime(
    agentRuntimeArn=POET_ARN,
    runtimeSessionId="a2a-jsonrpc-session-2026-08-07-poc-0002",
    payload=json.dumps(payload).encode(),
    qualifier="DEFAULT",
)
JSON-RPCでの呼び出し結果(テーマ: 秋のデータセンター)
{
  "jsonrpc": "2.0",
  "id": "req-001",
  "result": {
    "artifacts": [
      {
        "name": "haiku",
        "parts": [{"kind": "text", "text": "秋風吹く サーバー室より 熱気立つ"}]
      }
    ]
  }
}

ARNを直接指定して実行します。

エージェントからエージェントをA2Aで呼ぶ

クライアントエージェントが自分と同じセッションIDで俳句エージェントをA2A呼び出しする構成を試します。クライアント側の実行ロールには、呼び出し先のRuntimeに絞った bedrock-agentcore:InvokeAgentRuntime を追加しています。

agents/a2aclient/main.py(抜粋)
@app.entrypoint
def handler(event, context):
    payload = {
        "jsonrpc": "2.0",
        "id": str(uuid.uuid4()),
        "method": "message/send",
        "params": {"message": {"role": "user",
                               "parts": [{"kind": "text", "text": event.get("theme")}],
                               "messageId": str(uuid.uuid4())}},
    }
    response = agentcore.invoke_agent_runtime(
        agentRuntimeArn=POET_ARN,
        runtimeSessionId=context.session_id,  # 自分と同じセッションID → 同一インスタンスに同居
        payload=json.dumps(payload).encode(),
        qualifier="DEFAULT",
    )
    ...
実行結果(所要時間73.8秒、テーマ: 夜のバッチ処理)
{
  "agent": "a2aclient",
  "client_hostname": "ip-172-31-3-93.ec2.internal",
  "poet_hostname": "ip-172-31-3-93.ec2.internal",
  "haiku": "夜更けまで データ流れて 朝を待つ"
}

同一インスタンスに同居した2つのエージェント間でA2A通信が成立しました・・・!

が、ただしパケットが1台の中で完結しているわけではなく、いったんAgentCoreエンドポイントを経由して戻ってくる経路となります。SigV4認証やセッション管理をサービス側で一元的に通す設計と考えれば妥当ですが、A2A通信もAgentCoreのデータプレーンを経由するため、同居させるだけで通信レイテンシが短縮されるとは言えないように思いますね。

クリーンアップ

検証が終わったら、EC2とEBSの課金が続かないよう、セッション、Runtime、Capacity Providerの順に削除します。

scripts/04_cleanup.py
# 1. セッション削除(EC2・ENI・EBSをまとめて解放)
data.delete_capacity_provider_session(capacityProviderId=CP_ID, sessionId=SESSION_ID)

# 2. Runtime削除
for rid in RUNTIME_IDS:
    control.delete_agent_runtime(agentRuntimeId=rid)

# 3. Runtimeの削除完了を待ってからCapacity Provider削除
control.delete_capacity_provider(capacityProviderId=CP_ID)

Capacity ProviderはRuntimeから参照されている間は削除できず、ValidationException になります。また、Capacity Providerを削除すると配下のセッションと永続ストレージもすべて削除されるので、残したいデータがある場合は注意してください。

補足 LINUX_X86_64でも動かしてみた

比較表に書いた通り、InstancesはmicroVMと違ってx86_64も選べます。本当に動くのか気になったので、operatingSystemを LINUX_X86_64、インスタンスタイプを m7i.large にしたCapacity Providerでもwriterエージェントを動かしてみました。

Capacity Providerの変更点は構築で作ったARM64版との差分で2行だけです。Agent Runtimeの作成は、参照するzipをx86_64版に差し替える以外は同じです。

Capacity Providerの作成(ARM64版との差分)
 response = client.create_capacity_provider(
-    name="haiku_demo_cp",
+    name="x86_demo_cp",
     permissionsConfiguration={"capacityProviderOperatorRoleArn": OPERATOR_ROLE_ARN},
     computeConfiguration={
         "ec2Configuration": {
             "launchTemplateSource": {
                 "launchParameters": {
-                    "operatingSystem": "LINUX_ARM64",
-                    "instanceRequirements": {"allowedInstanceTypes": ["m7g.large"]},
+                    "operatingSystem": "LINUX_X86_64",
+                    "instanceRequirements": {"allowedInstanceTypes": ["m7i.large"]},
                     "instanceProfileArn": INSTANCE_PROFILE_ARN,
                 }
             },
             "vpcConfiguration": {"subnets": [SUBNET_ID], "securityGroups": [SG_ID]},
             "volumes": [{"ebsConfiguration": {"name": "shared", "sizeGiB": 10, "volumeType": "gp3"}}],
         }
     },
 )

zipに入れるライブラリもx86_64向けに作り直します。エージェントコードは無変更です。

x86_64向けのパッケージング(--python-platformを変えるだけ)
uv pip install \
  --python-platform x86_64-manylinux2014 \
  --python-version 3.13 \
  --target build/deps-x86 \
  --only-binary=:all: \
  bedrock-agentcore boto3
LINUX_X86_64での実行結果
{
  "agent": "writer",
  "haiku": "電算機\nシリコン奏でし\nビット舞う",
  "hostname": "ip-172-31-15-215.ec2.internal",
  "arch": "x86_64"
}

arch が x86_64 になっており、同じコードがそのまま動きました。arm64のライブラリを提供していないネイティブ依存のライブラリを使いたい場合や、x86_64前提のバイナリを同梱したい場合に選択肢になりそうです。

補足 セッションIDが増えるとインスタンスも増える?

セッションとEC2インスタンスは1:1なので、新しいセッションIDで呼び出すたびに新しいインスタンスが立ち上がります。検証中も、同じRuntimeを別のセッションIDで呼んだところ、既存のインスタンスが動いたまま2台目がプロビジョニングされ、それぞれ別のhostnameを返しました。エンドユーザーごとにセッションIDを割り当てるような設計にすると、同時利用者の数だけインスタンスとEBSが並ぶことになります。

放置したセッションが動き続けるわけではなく、自動停止の仕組みはあります。

  • 呼び出しが途絶えたRuntimeは、lifecycle設定のアイドルタイムアウト(デフォルト15分)で自動停止します。検証でも、放置していたwriterはStopRuntimeSessionを呼ぶ前にアイドル停止していました
  • セッション上の全Runtimeが停止すると、EC2インスタンスも停止します(検証2の通り、停止までは10分程度のラグがありました)
  • セッションが14日の上限に達した場合も自動停止します

EBSもセッションごとです。CloudTrailを確認すると、ライフサイクルの動きがそのまま残っていました。

CloudTrailイベント(時系列、Usernameは実行主体)
11:46:15  CreateAutoScalingGroup   AgentCore   ← Capacity Provider作成時(裏側はASG管理)
11:47:13  RunInstances             AgentCore   ← セッション初回呼び出しでEC2起動
11:47:30  CreateVolume             AgentCore   ← 共有ボリュームもセッションごとに新規作成
11:47:36  AttachVolume             AgentCore
(検証2でRuntime停止 → インスタンスはstoppedに)
12:22:52  RunInstances             AgentCore   ← 同じセッションIDで再呼び出し、新インスタンス起動
12:24:06  TerminateInstances       AutoScaling ← 旧インスタンスは終了
12:24:10  AttachVolume             AgentCore   ← EBSを新インスタンスに再アタッチ
12:25:12  TerminateInstances       AgentCore   ← セッション削除
12:25:18  DeleteVolume             AgentCore   ← このタイミングで初めてEBSが消える

一方で、セッションを自動で削除する仕組みはありません。自動で止まるのはEC2(コンピュート)だけで、EBSは再開に備えて保持され続けます。放置したセッションのEBSは、自分で DeleteCapacityProviderSession を呼ぶか、Capacity Providerごと削除するまで課金され続け、セッションIDの数だけ積み上がります。上のCloudTrailログでDeleteVolumeが記録されているのも、自動ではなく検証後にセッション削除を実行したタイミングです。マネージドインスタンス扱いのためEC2コンソールから直接インスタンスやボリュームを消すことはできず、削除の入口はAgentCore側のこの2つのAPIだけです。

コストはざっくり「セッション数 ×(EC2稼働時間 + 管理料金 + EBS)」になるので、不要になったセッションを削除する運用までセットで考えておくのがよさそうです。

どういうワークロードに向くか考えてみる

検証を通して見えた特性を整理すると、セッションの起動が重く、維持が長い、というのがInstancesの特徴です。新しいセッションIDでの初回呼び出しは毎回44〜107秒かかり、セッションごとにEC2が1台とEBSが1セット立ち上がります。EBSはセッション自体を削除する(DeleteCapacityProviderSession、またはCapacity Providerごと削除する)まで課金が続きます。

なので、チャットアプリのようにユーザーごとへ大量のセッションを短時間で作る用途とはちょっと違うように感じがしました。初回応答に1分待たされる上に、コストが同時利用者数に比例してしまいます。そういう「大量・短命・軽量」なセッションは、リクエスト単位の従量課金で起動待ちのないmicroVMが良いように感じます。

Instancesが合うのはその逆の「少数・長命・重量級」な印象で、数日回し続けるバッチやシミュレーション、GPUでの推論、複数エージェントが作業ディレクトリを共有する開発パイプラインのように、エージェントに専用の作業マシンを持たせたいケースなのかなと推測しています。

おわりに

永続ストレージや共有ファイルシステム自体はmicroVMでも使えるので、そこはInstancesを選ぶ理由にならない印象ですが・・・!

Instances固有のメリットは、複数のRuntimeを同一EC2インスタンスに配置できること、コンピュートが14日間動き続けられること、GPUを含むEC2のインスタンスタイプを選べること、そしてEC2・EBSが自分のアカウント内にあるため既存のEC2料金契約とアカウント統制がそのまま使えることだと感じました。という意味ではまだ私自身もドキュメント以上のユースケースを感じられていないです・・・

先述したユースケースに書いたセクションのようにまだ気軽には使いずらいなーとちょっと思いました・・・!
今後具体的に使うケースがあれば試していきたいです!

本記事が少しでも参考になりましたら幸いです。最後までご覧いただきありがとうございました!

この記事をシェアする

関連記事