これは同一のクロスクラウド通貨ベンチマークの3回目の実行で、矢印の向きが逆になった
初の試みです。Cloud Run 上の Google ADK マスター(Gemini 2.5 Flash、us-central1)がベンチマークポリシーを所有し、
Amazon Bedrock AgentCore ワーカー(Strands Agents on Nova Micro、us-east-1)を
A2A v1.0 で呼び出し、MCP 為替レートツールと照合します。

前回の実行では Bedrock がマスター、ADK がワーカーでした。逆転は単なるデプロイ変更のはずでした — ドメインコアはフレームワーク非依存で、比較ロジックは一切移動していません。実際に起きたのは、逆転により6つの相互運用性欠陥が露呈したことで、そのうち5つはローカルテストでは発見できなかったものです。それが本稿の核心であり、まずは失敗から取り上げます。

このプロジェクトは何を目指しているのか?

ほとんどの A2A デモは「HTTP 200 が返ってきた」止まりです。それはスモークテストであり、相互運用性ベンチマークではありません。

本ベンチマークは3つのモードを実行し、リモート検証のコストと作業コストを分離します:

モード 実行内容 目的
mcp_only ADK マスターが MCP stdio でレートツールを呼び出す ベースライン
a2a_only ADK マスターが AgentCore ワーカーに委譲する リモートエージェントのコスト
verified 両方を並行実行し、決定論的に比較する 精度・オーバーヘッド

比較は算術演算であり、モデルの判断ではありません。金額とレートは Decimal で扱い、2つの数値が一致するかをモデルに問うことはありません。


破綻した6つの事象

1. Strands A2A extra が誤ったプロトコルバージョンを話す

この問題はデプロイ前に発覚し、ワーカー設計全体を決定づけました。

strands-agents[a2a]a2a-sdk<0.4 を固定し、A2A v0.3 のワイヤーメソッド(message/send)を使用します。google-adk[a2a]==2.5.0a2a-sdk 1.x(v1.0、SendMessage)を使用します。v1.0 クライアントは v0.3 サーバーを呼び出せず、エージェントカードにもバージョン交渉はありません。

これは本プロジェクトの初回実行で A2UI 拡張が ADK エージェントから外された原因と同じ分断が、逆方向から再発したものです。

修正:エージェントループには strands-agents のみを使用し、A2A v1.0 のサーバー面は a2a-sdk から直接構築します。

# app/CurrencyWorker/main.py -- NOT strands.multiagent.a2a.A2AServer
from a2a.server.request_handlers import DefaultRequestHandler
from a2a.server.routes import create_agent_card_routes, create_jsonrpc_routes

app = Starlette(routes=[
    *create_agent_card_routes(AGENT_CARD),
    *create_jsonrpc_routes(_request_handler, rpc_url="/"),
])

Enter fullscreen mode Exit fullscreen mode

テストでは lockfile が a2a-sdk を 1.x に固定していることを検証し、依存関係の更新で不整合が再発しないようにしています。

2. google-adk[a2a] は自身のサーバー依存関係をインストールしない

Cloud Run コンテナはビルドに成功し、ローカルテストもすべて通過しましたが、起動時にクラッシュしました:

ModuleNotFoundError: No module named 'sse_starlette'
  File ".../google/adk/a2a/_compat.py", line 769, in attach_a2a_routes_to_app

Enter fullscreen mode Exit fullscreen mode

ADK の to_a2a()a2a.server.routes をインポートしており、これには sse_starlette が必要ですが、これは a2a-sdk[http-server] エクストラでしか提供されません。google-adk[a2a]a2a-sdk 単体もこれを引き込みません。

コスト:ロールアウト1回の失敗。両側とも a2a-sdk[http-server] を明示的に宣言するようになりました。

3. IAM 信頼ポリシーは決して一致し得なかった

コーディネータは AWS 鍵を持たず、フェデレーションを使用します。Cloud Run のメタデータサーバーが Google OIDC トークンを発行し、STS の AssumeRoleWithWebIdentity で一時的認証情報と交換、SigV4 署名でリクエストを送信します。

信頼ポリシーは一見正しそうに見えましたが、実際には不可能でした:

"Condition": { "StringEquals": {
  "accounts.google.com:aud": "currencybench-agentcore-worker",
  "accounts.google.com:sub": "1019138736740282766.."
}}

Enter fullscreen mode Exit fullscreen mode

AWS はこれらの条件キーを、名前が示すクレームにマッピングしません:

条件キー 実際の Google クレーム
accounts.google.com:oaud トークンの aud
accounts.google.com:aud トークンの azp(数値のクライアント ID)
accounts.google.com:sub トークンの sub

オーディエンスの文字列数値と比較していました。オーディエンスは aud ではなく oaud で固定してください。

4. Google 用 OIDC プロバイダーの作成が Google フェデレーションを破壊する

次の自然な手順 — accounts.google.com を IAM OIDC ID プロバイダーとして登録 — は誤りでした。AWS は Google とネイティブにフェデレーションしており、明示的なプロバイダーを作成すると STS がそのプロバイダーのサムプリントで検証するようになり、すべての交換が失敗しました:

<Code>InvalidIdentityToken</Code>
<Message>The web identity token provided could not be validated.<Message>

Enter fullscreen mode Exit fullscreen mode

プロバイダーを削除すると修復されました。プリンシパルはドメイン名そのものです:

"Principal": { "Federated": "accounts.google.com" }

Enter fullscreen mode Exit fullscreen mode

失敗モードに注意:無効なトークンと拒否された信頼ポリシーは異なるエラー(InvalidIdentityTokenAccessDenied)であり、この区別が2つのバグを最速で見分ける方法です。

5. AgentCore が A2A バージョン ヘッダーを削除し、SDK が v0.3 にデフォルトする

フェデレーションが機能し、SigV4 署名が受け入れられた状態で、ワーカーは正しくバージョン指定された自身のクライアントを拒否しました:

A2A version '0.3' is not supported by this handler. Expected version '1.0'.

Enter fullscreen mode Exit fullscreen mode

クライアントは a2a-sdk 1.1.2 で、正しいヘッダー(client_factory.pyA2A-Version: 1.0 を設定)を送信していました。ワーカーは v1.0 で、カードも "protocolVersion": "1.0" を広告していました。

ヘッダーは到着しませんでした。AgentCore の A2A ランタイムは許可リスト登録済みのリクエストヘッダーのみ転送し、サーバー側のバリデータは欠落したヘッダーを v0.3 とみなします:

# a2a/utils/version_validator.py
if not actual_version:
    return constants.PROTOCOL_VERSION_0_3

Enter fullscreen mode Exit fullscreen mode

欠落したヘッダーと古いクライアントはサーバーにとって区別できません。修正はランタイム設定の1行です:

"requestHeaderAllowlist": ["A2A-Version"]

Enter fullscreen mode Exit fullscreen mode

これはプロジェクト全体のバージョン不整合テーマが3つ目の形で現れたものです:最初は推移的固定、次にフレームワークのエクストラ、そして今度はプロキシ経由です。

6. エージェントカードが双方向にバインドアドレスを広告する

AgentCore ワーカーのカードはコンテナ自身のバインドアドレス http://127.0.0.1:9000 を広告します。ADK の to_a2a()http://127.0.0.1:8080 で同様です。a2a-sdk クライアントはカード URL でルーティングするため、クライアントが実際にダイヤルしたエンドポイントにインターフェースを書き換えない限り、クロスクラウド呼び出しは失敗します。

初回実行で ADK について文書化済みですが、これは ADK 特有のクセではありません。

ボーナス:A2A エンドポイントはベース URL のように見える URL ではない

agentcore status はパスにランタイム ARN をパーセントエンコードし、/invocations サフィックスを付与した URL を出力します。その完全な URL(サフィックスを含む)が A2A のベースです:

https://bedrock-agentcore.us-east-1.amazonaws.com/runtimes/arn%3Aaws%3A...%2F<id>/invocations
                                                                       └── card at <base>/.well-known/agent-card.json

Enter fullscreen mode Exit fullscreen mode

/invocations を省略した(より整然とした)ベースは UnknownOperationException を返します。エンコードされた ARN ではなく裸のランタイム ID を使用した場合も同様です。パーセントエンコーディングは、スコープ付き IAM ポリシーのランタイム ID を導出する際の素朴な ${VAR##*/runtimes/} シェル解析も破壊します。


測定対象

2026-07-31 にデプロイ済みの Cloud Run コーディネータ経由で、us-central1us-east-1 方向に各モード5回のウォーム呼び出しを実行:

モード 中央値 最小 最大
mcp_only 2.96 s 2.37 s 3.84 s
a2a_only 18.92 s 16.30 s 19.68 s
verified 16.19 s 15.64 s 22.86 s

2点が際立ちます。

リモートエージェントホップのコストはツールベースラインの約6倍です。 これはネットワーク遅延ではありません — us-central1 から us-east-1 は数十ミリ秒です。これは2回目のモデルターンです:ワーカーはエージェントであるため、委譲は Nova Micro がツール呼び出しを計画し、呼び出し、構造化された応答を書き込むことを意味し、その上に Gemini が同様の処理を行います。A2A 委譲は独立性を提供しますが、独立性には推論コストがかかります。

verifieda2a_only より遅くない。 verified モードは MCP 呼び出しA2A 呼び出しの両方を実行して比較しますが、中央値はむしろ低くなっています。コーディネータは両方を並行して発行します:

mcp_task = self._call("mcp", self._mcp, request, failures)a2a_task = self._call("a2a", self._a2a, request, failures)mcp_quotes, a2a_quotes = await asyncio.gather(mcp_task, a2a_task)

Enter fullscreen mode Exit fullscreen mode

したがって verified ≈ max(mcp, a2a) であり、和ではありません。一度リモートエージェントのコストを支払っている場合、独立検証はほぼ無料です。2つの中央値の差は実行間のばらつきより小さく、verified が実際に高速であるとは解釈せず、等価と見なしてください。

verified モードでの正しさ(100 USD → EUR および CHF):

EUR  rate 0.87138  ->  87.138    CHF  rate 0.81248  ->  81.248
primary:  mcp-stdio:frankfurter-live
verifier: aws-agentcore-a2a-worker

Enter fullscreen mode Exit fullscreen mode

両ターゲットが一致しました。両方とも rate is stale; check the observation timestamp を伴っていました — MCP 側は Frankfurter の日次参照レート(2026-07-30T00:00:00Z タイムスタンプ)を31日 04:09Z の実行に対して返し、24時間しきい値を超えていました。フィクスチャではなく実際の公開レートで陳腐化ルールが発火したことは、ここではより有用なシグナルです。


本実行が確立しないこと

  • IAM ポリシーはまだ最小権限ではありません。 bedrock-agentcore:InvokeAgentRuntimeruntime/<id> および runtime/<id>/* にスコープすると 403 で拒否され、Resource: "*" のみ機能しました。AgentCore は特定できていない ARN 形状で認可しており、データプレーンイベントはデフォルトで CloudTrail に含まれません。上記の測定は広範なポリシーで行いました。
  • n は5、一地域ペア、1日、1モデルペアです。 p95 なし、コスト会計なし、トークン数なし、コールド/ウォーム分布なし。38ケース × 3モードの行列はこの方向では実行されていません。
  • どちらのクラウドが高速であるかの主張ではありません。 Gemini 2.5 Flash と Nova Micro は異なる価格帯の異なるモデルです。6× は エージェント委譲 のコストであり、AWS のコストではありません。

保持する価値がある部分

ドメインコアは変更されませんでした。CurrencyCoordinator は2つのダックタイピングされたアダプタを受け取るため、どちらのクラウドがマスターをホストするかはデプロイの決定事項です。これは成立しました:比較ロジック、陳腐化ルール、失敗分類、評価ハーネスはすべて逆転後も無傷で存続しました。

破綻したすべては継ぎ目 — プロトコルバージョン、依存関係エクストラ、ID フェデレーション、ヘッダー転送、URL 形状 — にありました。6つの欠陥のうち5つは、何かが実際にデプロイされるまで不可視でした。得られる教訓は1つです:ノートPC を離れたことのない相互運用性テストは、モックをテストしているに過ぎません。