本シリーズの過去6記事で、同じ通貨ベンチマークを
6回構築し、その都度オーケストレーションを行うクラウドと
それに応答するクラウドを入れ替えました。ドメインコアは同じ、38の評価ケースも同じ、Decimal比較器も同じ、
全ホップで公開レートソースは両サイドで同一です。変更したのはオーケストレーションの方向のみです。

これで3クラウド間の全方向エッジが構築・測定済みとなりました。本記事はその総括:6つを並べたときの数値が示すこと、複数回にわたり発生した障害、そして依然として測定されていない点です。

要約:A2A v1.0は展開した全方向で相互運用できました。
プロトコルは高コストでもなく、障害の原因でもなく、最適化対象でもありませんでした。問題だったのはIdentity、Packaging、Timeoutです。

カバレッジマトリクス

3クラウド、3フレームワーク、6つの可能な方向性:

Master(オーケストレーション) Remote(応答) Status
Microsoft Foundry, Azure Google ADK, Cloud Run Deployed — full 38-case matrix + hosted smoke
Bedrock AgentCore, AWS Google ADK, Cloud Run Deployed — 38-case matrix (local harness) + hosted smoke
Bedrock AgentCore, AWS Microsoft Foundry, Azure Deployed — hosted smoke
Microsoft Foundry, Azure Bedrock AgentCore, AWS Deployed — hosted smoke
Google ADK, Cloud Run Microsoft Foundry, Azure Deployed — live measured, 5–6 run medians
Google ADK, Cloud Run Bedrock AgentCore, AWS Deployed — live measured, 5-run medians

全6方向が展開・測定済み。最後のケースが最も示唆に富みました:
ローカルスイートはグリーンで1日間コード完了していましたが、
デプロイすると6つの欠陥が表面化し、そのうち5つはローカルテストでは検出不可能でした。この比率が本シリーズで最も信頼できる発見です。

もう1つのリポジトリgcp-adk-a2a-currencyは、Cloud Run上のADKコーディネータがADKリーフエージェントを呼び出しますが、デフォルトのA2AホップはGCP内部に留まるため、クラウド境界を越えず、上記6つと比較対象外です。

各実行は同一の3モードを使用:

Mode Path
mcp_only Master calls its own MCP exchange-rate tool
a2a_only Master delegates to the remote agent over A2A v1.0
verified Both run concurrently; deterministic code compares the results

どの実行でも、モデルがツールを選択し、回答を説明しました。算術処理や合意判定は行いません。常に以下のコードで判定:

difference = abs(primary.converted_amount - verifier.converted_amount)
relative_difference = difference / abs(primary.converted_amount)
agreed = relative_difference <= Decimal("0.005")

Enter fullscreen mode Exit fullscreen mode

数値の並列表記

latency列を読む前にprovenance列を確認してください。2行は114レコードの評価マトリクス、2行は単一のhosted smokeケース、2行は繰り返しライブ実行の小規模中央値です。これらは互換性のある証拠ではありません。

Master → Remote Remote model / hosting mcp_only a2a_only verified Provenance
Foundry → ADK gemini-2.5-flash, Cloud Run 297 ms 1.69 s 1.71 s 114-record matrix, medians
AgentCore → ADK gemini-2.5-flash, Cloud Run 286 ms 2.09 s 1.87 s 114-record matrix, medians
AgentCore → Foundry gpt-5-mini, Foundry hosted ~4.2 s ~18.8 s ~18.1 s single hosted smoke
Foundry → AgentCore Nova Micro, AgentCore 359 ms 25.1 s 28.2 s single hosted smoke
ADK → Foundry gpt-5-mini, Foundry hosted n/a 23.4 s n/a median of 5 live runs
ADK → AgentCore Nova Micro, AgentCore 2.96 s 18.92 s 16.19 s median of 5 live runs

2つのマトリクス行のp95:Foundry → ADK a2a_only 4.82 s, verified 4.15 s;
AgentCore → ADK a2a_only 6.10 s, verified 4.33 s。ADK →
AgentCore行の範囲:mcp_only 2.37–3.84 s, a2a_only 16.30–19.68 s, verified
15.64–22.86 s。

Finding 1: ばらつきはremoteのruntimeによるもので、プロトコルではない

表中のA2Aレッグ時間は1.69 sから25.1 sまで——同一プロトコル、同一SDK、大陸間ホップで15倍の差が生じています。変動要因はプロトコルではありません。

6番目のエッジが存在する前は、より簡潔な説明が可能でした:remoteの役割が決定要因——1問に回答するleafは約2秒、多ツール推論ループを実行するmasterは約20秒かかる。しかしADK → AgentCoreの実行でこの理論は否定されました。AgentCoreワーカーは定義上leafであり、1ツールコールで1問に回答するにもかかわらず、18.92 sを要しました。

実際の6行が示すのはremoteのruntimeとmodelであり、役割ではありません:

  • remoteがgemini-2.5-flash on Cloud Runのもの:1.69–2.09 s
  • remoteがgpt-5-mini on Foundry hostingのもの:18.8–23.4 s
  • remoteがNova Micro on AgentCore Runtimeのもの:18.9–25.1 s

このグループ化は双方向にわたり、オーケストレーションするクラウドに関係なく成立します。正直な解釈は、委譲コストは1回の推論に加えremoteプラットフォームのリクエストオーバーヘッドであり、2つのhosted agent runtimeはCloud Run上のコンテナより1桁高コストである、ということです。モデル速度とプラットフォームオーバーヘッドのどちらが要因か分離していません——同一モデルを両runtimeで実行する必要があります。

プロトコルが要因ではないことは、ADK → Foundryパスで直接測定済みです:認証済みagent-card取得は0.35 s warm(0.51 s cold)で、トークンはコール間でキャッシュされます。第2クラウドをパスに追加——直接ワークステーションコール21.2 sに対しCloud Run ADKプロキシ経由23.4 s——のコストは約2秒であり、実行間ばらつき18.9–29.5 sに対して小さいものです。master単独の分散の方が追加クラウド全体のコストより大きい。

クロスクラウドエージェントアーキテクチャを高速化したい場合、プロトコルやプロキシは時間のかかる場所ではありません。

Finding 2: verifiedモードは2回のA2Aコールではなく1回で済む

全実行で、verifiedは両レッグの合計ではなく遅いレッグと同等のコストで完了しました。MCPとA2Aが並列実行されるためです:

  • Foundry → ADK: a2a_only 1.69 s, verified 1.71 s
  • AgentCore → ADK: a2a_only 2.09 s, verified 1.87 s(verifiedのマトリクス中央値がa2a_onlyを下回った——同一分布、異なるケース構成)
  • AgentCore → Foundry: A2Aレッグ ~18.1 s inside a verified run whose MCP leg was ~3.1 s
  • Foundry → AgentCore: a2a_only 25.1 s, verified 28.2 s
  • ADK → AgentCore: a2a_only 18.92 s, verified 16.19 s——verifiedがa2a_onlyを下回ったが、実行間ばらつき以下。これら2つは等価とみなす。

並列性は明示的であり、偶発的ではありません:

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

したがって、独立したクロスクラウド検証のコストは「1回のremoteコール」であって、「1回のremoteコール+ベースライン」ではありません。

Finding 3: happy pathでは精度差はゼロ

展開した全クロスクラウドverified実行で、完全一致が報告されました——
relative_difference: "0", agreed: true, 警告なし:

  • AgentCore → ADK, hosted: EUR and CHF, zero difference
  • AgentCore → Foundry, hosted: rate 0.87873, amount 87.87300, both sides
  • Foundry → AgentCore, hosted: rate 0.87138, both sides
  • Foundry → ADK, hosted: agreed: true, relative_difference: 0
  • ADK → Foundry, live: identical quotes on every one of five runs
  • ADK → AgentCore, live: EUR 0.8713887.138 and CHF 0.8124881.248,
    both agreed, primary mcp-stdio:frankfurter-live, verifier aws-agentcore-a2a-worker

これは意図的な設計です。両サイドが同じFrankfurter日次参照レートを読み取るため、不一致はプロトコル、モデル、コードに起因し得ますが、データソースのずれによるものではありません。

最後の実行から得られた1つの詳細は、合意自体より価値があります:両ターゲットがrate is stale; check the observation timestampを伴っていました。Frankfurterの日次参照レートは2026-07-30T00:00:00Zとタイムスタンプされ、31日の04:09Zに実行——24時間閾値を超過していました。注入フィクスチャではなく実際の公開レートでstaleルールが発火し、モデルが安心できる文章に丸めるのではなくコーディネータが表面化した点が、より有用なシグナルです。

114レコードマトリクスの96.77%合意率は矛盾ではありません。38ケースには意図的に注入された障害(タイムアウト、staleレート、強制不一致、悪意あるツールテキスト)を含み、障害ケースが100%を下回らせています。全60のlive-adapterレコードは許容範囲内で合意しました。実行全体で不一致となったのは注入したもののみで、verifierはすべてをフラグ付けしました。

「検証に価値はあるか」という問いへの正直な回答:happy pathでは精度面で何も得られません。余分な1秒——あるいは20秒——で得られるのは独立した障害検知です。第2の実装が第2のクラウド上で、異なるベンダーのモデルで動作し、ツールが侵害・stale・誤りである場合に明示的に不一致を示します。

複数回にわたり表面化した障害

6回の構築、6組のバグ、そして多くの重複。これらは繰り返し発生したものです。

A2A v0.3.0 → v1.0 wire break

6回の構築のうち5回で発生し、通常同じ様相を呈しました:

a2a.utils.errors.MethodNotFoundError: Method not found

Enter fullscreen mode Exit fullscreen mode

または生JSON-RPCで:

{"error": {"code": -32601, "message": "Method not found",
           "data": [{"reason": "METHOD_NOT_FOUND",
                     "metadata": {"detail": "'method' field is not a valid A2A method."}}]}}

Enter fullscreen mode Exit fullscreen mode

v0.3.0はmessage/sendをルーティングします。v1.xはproto-JSONパラメータ付きSendMessageをルーティングします。フォールバック交渉はありません:クライアントはprotocolVersion: 0.3.0を明示するagent cardを取得しつつ、v1.0メソッドを呼び出します。

完全に認可されたエンドポイントでMethodNotFoundErrorが発生した場合、他のデバッグに先立ち両サイドのa2a-sdkメジャーバージョンを確認してください——またクライアントのメソッド表記も確認してください。Foundry cardは1つのURLで3つのインターフェース(JSONRPC 1.0, JSONRPC 0.3, HTTP+JSON 0.3)を広告し、誤ったものを選択させる可能性があります。

6回目の構築では、同じずれを生む第3の方法を発見しました。これは両端が正しいため、より厄介です。v1.0クライアントが"protocolVersion": "1.0"を広告するv1.0ワーカーを呼び出し、以下の応答を得ました:

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はA2A-Version: 1.0を送信します。AgentCore Runtimeは許可リスト登録済みリクエストヘッダのみを転送するため、ヘッダは到着せず、サーバー側バリデータは欠落ヘッダをv0.3として扱います:

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

Enter fullscreen mode Exit fullscreen mode

欠落ヘッダと旧クライアントはサーバーにとって区別できません。1行のruntime設定で修正可能です:

"requestHeaderAllowlist": ["A2A-Version"]

Enter fullscreen mode Exit fullscreen mode

3回の構築、3つのメカニズム——推移的ピン、フレームワークextra、そしてプロキシによるヘッダのサイレント除去——すべてが同一のバージョンずれ症状に至りました。2つのA2Aエンドポイント間の任意の中間ノードはプロトコルバージョンを欠落させる場所となり、欠如をバージョンにデフォルトすることは、トランスポートバグをクライアントバグに見せかけます。

依存関係ピンはプロトコル制約である——そうでなくなるまで

エコシステムピンはこれらの構築の各時点で相互排他的でした:

Package a2a-sdk requirement
strands-agents 1.50.2 >=1.0.0,<2
agent-framework-a2a 1.0.0b260721 >=1.0.0,<2
google-adk 2.1.0 – 2.4.0 >=0.3.4,<0.4
google-adk 2.5.0 >=0.3.4,<2
a2ui-agent-sdk (through 0.4.0) <0.4.0
strands-agents[a2a] (latest) <0.4

google-adk 2.5.0はGCP側をアンブロックしたリリースです。A2UIは完全に削除されました。プロトコル隣接のextensionがエージェント全体をv0.3.0に固定したためです——コミット前にextraを監査してください。

Foundry → AgentCore構築で有用な教訓が得られました。AgentCoreのA2Aサーバーextraは0.3系統を要求し、Foundry側クライアントは1.xを使用していました。両方を1つのバンドルにインストールすることは不可能でした。修正はバージョンアップではなくアーキテクチャ変更です:AgentCoreアプリケーションバンドルを互換SDKに固定し、クライアントを分離し、2つに通信させます。Pythonパッケージがバージョンを共有しなくとも、ネットワークプロトコルは相互運用できました。パッケージバージョンの整合性は同一プロセス要件であり、ワイヤ要件ではありません。

6回目の構築では別の利用可能な回答を採用し、これも機能しました:エージェントループにはエージェントフレームワークを保持し、その[a2a]extraを削除し、A2A v1.0サーバー面をa2a-sdkから直接構築します。

# 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

したがって、誤ったワイヤバージョンを固定するextraからの2つの検証済み脱出策があります:バンドルを分離する、またはextraをバイパスする。どちらもデプロイ済み実行で裏付けられています。各側のlockfileテストにより、依存関係の更新が不一致を静かに再導入することを防げます。

すべてのデフォルトタイムアウトは単一クラウドを前提とする

コーディネータの当初のadapterあたり10秒タイムアウトは、ローカルでは寛大でしたが、6回の全クロスクラウド実行で致命的でした。クリーンに失敗した点のみが唯一の利点でした:

{"failures": {"a2a": "timeout: adapter timed out"}, "elapsed_ms": 10010}

Enter fullscreen mode Exit fullscreen mode

この失敗は、直接コールで既に25秒かかることが判明しているパスから発生しました。アダプタは正常;ポリシーが誤っていました。最終値:AgentCoreおよびFoundry-master hostedパスには60 s、ADKクライアントおよびFoundryコーディネータには120 s。

コールドスタートは単にレイテンシを追加するだけではありません。ADKワーカーからのコールドスタート応答は、要求されたターゲットを完全に省略しました。パースが厳密であるため、サイレントな短い回答ではなく、型付きのprotocol障害として表面化しました。部分応答を考慮して設計してください。

Identityがすべてだった

6回の全構築を通じて最も強いパターン:A2A相互運用性はプロトコルの衣をまとった認証問題です。 JSON-RPC部分は全方向で容易でした。

AWSまたはGCPからFoundry呼び出す場合:

  • 受信A2Aはopt-inかつpreview——hosted FoundryエージェントはA2AをPATCHで有効化するまでResponsesを話します。
  • PATCHのprotocol_configuration置換であり、マージではありません。仮定せず検証しました:{"a2a": {}}のみのパッチでResponsesが削除され、A2A agent cardも同時に削除されました(400)。そのリクエスト自体は200を返しました。
  • 匿名/.well-known/agent-card.jsonはありません。カードはEntra背後の<agent>/endpoint/protocols/a2a/agentCard/v1.0にあります。発見自体が特権コールであるため、カード上の404や401はパス誤りではなくrole assignment欠如の可能性が高いです。
  • 呼び出し元にはプロジェクトに対するFoundry Agent Consumerデータプレーンロールが必要であり、デプロイヤにはFoundry Project Managerが必要です。Azure Ownerはどちらも含みません。
  • まずトークンオーディエンスを正しく:https://ai.azure.com/.default、ARMオーディエンスではありません。誤ったスコープの完全に有効なトークンは、scope問題とは異なる様相の401を生成します。

AzureからAgentCore呼び出す場合:

  • AgentCoreのデフォルトruntime認可はIAM/SigV4であり、AWS呼び出し元には正しいですが、AWSリクエストに署名できないFoundryコンテナには無用です。直接AWS CLI呼出は成功しましたが、クロスクラウド呼出は不可能でした。
  • 修正はカスタムJWTオーソライザとCognito machine-to-machineクライアントの追加です。A2AアダプタがOAuth client-credentials交換を行い、トークンをキャッシュし、オーソライザがissuer、client、および必須のcurrencybench/invokeスコープを検証します。

GCPからAgentCore呼び出す場合——同じ壁、異なる回答:

6回目の構築は同じCUSTOM_JWTアプローチから開始し、AgentCoreのデフォルトAWS_IAMオーソライザへのkeylessフェデレーションに置き換えました:Cloud Runのmetadata serverがGoogle OIDCトークンを発行し、STS AssumeRoleWithWebIdentityが一時クレデンシャルと交換し、リクエストはSigV4署名されます。コンテナ内に長期AWSクレデンシャルは存在しません。

到達までに2つのIAM欠陥——いずれも正しい設定に見える——を要しました。

条件キーは名前の意味するところではありません。 以下のtrust policyは構文的に有効で、正しく読めますが、決して一致しません:

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

Enter fullscreen mode Exit fullscreen mode

Condition key Actual Google claim
accounts.google.com:oaud the token's aud — your audience string
accounts.google.com:aud the token's azp — the SA's numeric client id
accounts.google.com:sub the token's sub

audience文字列数値と比較されるため、STSは永遠に「Incorrect token audience」と回答します。audienceはoaudで固定してください。

またsubjectも固定してください。audienceのみでは認可になりません。
これはキーが正しくなった後も重要です。任意のGoogleプリンシパルは任意のaudience文字列に対するIDトークンを要求できます——audienceは呼び出し元が選択する値であり、プラットフォームが付与する権限ではありません。audienceのみを条件とするtrust policyは、「あるGoogle identityがこれを生成した」以上の何も証明せず、共有組織ではほとんど何も証明しません。

したがって、policyはsubも条件とし——メールアドレスではなくservice accountの不変な数値IDで条件付けます。メールアドレスはアカウント削除・再作成で解放・再バインド可能な名前だからです:

gcloud iam service-accounts describe "$SA_EMAIL" --format='value(uniqueId)'

Enter fullscreen mode Exit fullscreen mode

これが、bearerトークンを運ぶのではなく、呼び出し先のネイティブオーソライザにフェデレーションする理由でもあります:trust決定はIAM policyに委ねられ、CloudTrailに本物のプリンシパル帰属があり、policy編集で失効可能であり、アプリケーション設定に存在するclaim-string matcherではありません。

GoogleをOIDCプロバイダとして登録するとGoogleフェデレーションが破綻します。 明白な次の手——accounts.google.comに対するaws iam create-open-id-connect-provider——は誤りです。AWSはGoogleとネイティブにフェデレーションします。明示的プロバイダを作成すると、STSはそのプロバイダのthumbprintに対する検証に切り替わり、すべての交換が失敗します:

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

Enter fullscreen mode Exit fullscreen mode

プロバイダを削除すると修正されます。プリンシパルは裸のドメインです:
"Federated": "accounts.google.com"

ここでの有用な診断は、これら2つのバグが異なるエラーを生成することです——InvalidIdentityTokenはトークンが一切検証できなかったことを意味し、trust-policy不一致はAccessDeniedです。この区別が、「プロバイダ設定が誤っている」と「条件が誤っている」を最速で判別する方法です。

したがって、同一AWS runtimeへの2つのクロスクラウドコールに、2つの異なる回答が存在します:Azureからのbearerトークン、GCPからのkeylessフェデレーション。どちらも展開・測定済みです。

そしてクレデンシャルは常に呼び出し側に存在します。GoogleがAzureを呼び出す場合、Azure client secretはGoogle Secret Managerに存在します。AWSがAzureを呼び出す場合、Entra service-principalクレデンシャルはAWS Secrets Managerに存在し、単一のsecret ARNにスコープされます。secretが存在することを回避する方法はありません;ソースツリー、マニフェスト、シェル履歴に存在することを回避する方法はあります。

予算化すべき1つのタイミングトラップ:data-plane RBACの伝播に105秒を要しましたが、az role assignment listは既に割り当て済みと表示していました。エージェント書き込みは全時間403を返し続けました。これは伝播であり、設定誤りではありません。ロールを再割り当てする前に待機してください。soft-deleted Foundryアカウントも、パージされるまで再作成をブロックしました——Azureリソース状態にも記憶があります。

テストはソースツリーをインポートする;ユーザーはイメージを実行する

このクラスのバグは3回の構築で出現し、毎回グリーンテストスイートには不可視でした。

aiohttpが独立に2つを破壊しました。azure.identity.aioは非同期トランスポートをaiohttpで構築しますが、これはazure-identityのハード依存関係ではありません——通常のワークステーションには存在するため、ローカル実行はパスします:

File "azure/core/pipeline/transport/__init__.py", line 94, in __getattr__
    raise ImportError("aiohttp package is not installed")

Enter fullscreen mode Exit fullscreen mode

AWSでは、リポジトリルートrequirementsへの追加では不十分でした;CodeZipはアプリケーションバンドルを独立に解決するため、app/CurrencyCoordinator/pyproject.tomlに固定する必要がありました。GCPではuv sync --frozenでコンテナ内に同様の欠如が生じました。

このファミリの残り2つは、いずれもADK-calls-Foundryクライアントイメージ由来です:COPY pyproject.toml uv.lock agent.py ./行がentra_auth.pyに言及しておらず、モジュールがイメージに一切存在しなかったこと;およびfrom entra_auth import EntraAuthというフラットインポートが解決できないこと——ADKのsingle-agentモードはファイルをapp.agentとしてインポートするため、サブモジュールとして扱われ、/sys.pathにあり/appではないためです。相対インポートは両コンテキストで機能します。

Foundry-master側でも同様の形状:-m mcp_server.serverはデプロイバンドル外のmonorepo siblingをインポートできないため、MCP stdioツールが実サブプロセスを実インポートパスで起動します。修正はazdに渡す前にバンドルへrsyncすることでした。洗練されていませんが、hosted runtimeはリポジトリレイアウトに関心がありません。

6回目の構築は、このパターンの最も明快な標本を生成しました。コンテナはビルドされ、全ローカルテストはパスしましたが、起動時に死にました:

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]extraのみに同梱されます。google-adk[a2a]も裸のa2a-sdkもこれを引き込みません。aiohttpと同一の形状——ワークステーションに偶然存在する任意の推移的依存関係——ですが、初回コールではなく起動時に失敗したため、Cloud Runはhealth-checkタイムアウトとして報告し、gcloudは誤解を招くResource 'currency-adk-coordinator' already exists競合を表示しました。実際のエラーはrevisionログにのみ存在しました。

このクラスのバグを発見するのは、コンテナの実ディレクトリレイアウトをローカルでロードするか、デプロイして呼び出すかの2つだけです。

from google.adk.cli.utils.agent_loader import AgentLoader
AgentLoader("/app").load_agent("app")   # ModuleNotFoundError before the fix

Enter fullscreen mode Exit fullscreen mode

系論:モデルを通過するエラーはobservableではない

上記のSTS失敗のデバッグに、愚かな理由で2回のデプロイサイクルを要しました。アダプタは正確で実行可能なメッセージをraiseしましたが、返ってきたのは:

The remote agent reported an issue with the web identity token.

失敗テキストはtool resultとして返されるため、masterモデルを通過し言い換えられます。デバッグに使用するものは、単にraiseするのではなく、失敗時点でサーバーサイドにログ記録する必要があります。上流の詳細にも同様のことが言えます:アダプタはSTSレスポンスボディを破棄し、HTTPステータスのみを報告していました。これはaudience不一致と検証不能トークンを区別できない半分そのものです。

agent cardの移植性は内部URLに依存する

ADKのto_a2a()はホスト、ポート、スキームを公開カードに焼き込みます。ローカルデフォルトのままにすると、Cloud Runエージェントはhttp://127.0.0.1:10001を広告するカードを配布し、リモートクライアントは自分自身を呼び出そうとします。コンテナはA2A_PUBLIC_URLを読み取る必要があります——Cloud Runは初回デプロイ完了までサービスURLを公開しないため、デプロイスクリプトは第2パスでのみ設定可能です。

初回構築後にADKの癖として書き上げましたが、そうではありません。AgentCoreワーカーのカードもhttp://127.0.0.1:9000——自コンテナのbindアドレス——を全く同じ理由で広告します。a2a-sdkクライアントはカードURLでルーティングするため、本シリーズの全クロスクラウドクライアントは、カードのインターフェースを実際にダイヤルしたエンドポイントに書き換えることになります:

for interface in card.supported_interfaces:
    interface.url = endpoint

Enter fullscreen mode Exit fullscreen mode

クラウド境界を越える場合、双方向に「カードはメッセージの送信先を教える」と助言として扱ってください。

A2AベースURLはベースURLらしく見えるものではない

agentcore statusはruntime ARNをpercent-encodedでパスに含め、/invocationsサフィックスを付与して出力します。この完全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の代わりに裸のruntime idを代入しても同様です。percent-encodingには第2の咬みつきがあります:スコープ付きIAM policyのためのruntime idを素朴な${VAR##*/runtimes/}で導出すると、 malformed ARNが生じ、デプロイは成功しますが、コール時にのみ失敗します。分割前にデコードしてください。

逆方向から来ると、Foundryはカードを手書きさせることになります。MCPツールが自動的にskillsとして表示されるADKの後では後退のように感じられます。また正直さへの一歩でもあります:カードは契約であり、その側ではそれを明示させられます。

モデルレイヤの失敗はプロトコル失敗ではない

interopバグのように読めるため、分離する価値があります:

  • Nova Microは「100 USDをEURに変換せよ」と読み、ターゲット通貨が欠けていると主張し、ユーザーに確認を求めました。ツール可用性が問題ではなく、自然言語引数抽出が問題でした。master promptは既に存在する情報に対する確認要求を禁止し、リグレッションテストでこれを保持します。
  • Gemini 2.5 Flashは genuinely liveなhosted-adk-a2aクォートをnon-liveと報告しました。ソース名がフィクスチャソースhosted-local-a2ahosted-プレフィックスを共有し、共有命令が部分文字列で区別するためです。データは正しかった;文章のみが誤っていました。同一命令はNova Microおよびgpt-5-miniでは正しくラベル付けされます——これがモデルバグではなく共有命令バグである理由です。
  • gpt-5-miniおよびgemini-2.5-flashは全実行を通じて「ツールを呼び出せ、計算するな」を遵守しました。算術をDecimalに保持することで、合意チェックは数値を比較し、モデルの文章を比較しません。

ベンチマークをfail closedにする

最も重要な非技術的教訓。hostedベンチマークツールがエンドポイント設定欠如時にサイレントに決定論的フィクスチャへフォールバックすると、何もないところから綺麗に見える数値を渡され、それらを公開することになります。両hostedコーディネータは現在拒否します:

{ "name": "CURRENCY_REQUIRE_GCP_ADK", "value": "1" }

Enter fullscreen mode Exit fullscreen mode

これを設定すると、a2a_onlyおよびverifiedはplausibleなフィクスチャ回答ではなくgcp_adk_not_configuredを返します。ローカルフィクスチャ実行は依然として機能しますが、名前で要求する必要があります(CURRENCY_ALLOW_LOCAL_FIXTURES=true)。

6回反転させても生き残ったもの

ドメインコアです。2つのインターフェース——MCP-backed ExchangeRateToolとA2A-backed RemoteCurrencyAgent——で、Microsoft Agent Framework、Strands、ADKはいずれもその外側にあり、全hosting runtimeもその外側にあります。

どのクラウドがオーケストレーションするかを反転させても、ドメインサービスを書き換える必要はありません。フレームワーク非依存のPythonが、6回の全構築で算術、並列性、タイムアウトポリシー、障害分類を所有し、同一の38ケースと同一の比較器がすべてに対して実行されました。この移植性が本プロジェクトの実際の結果です——個々のレイテンシ数値よりも、そして2つのSDKを1つのプロセスに入れることより、はるかに重要です。

全6回を通じて破綻したものはすべてseamで破綻しました:プロトコルバージョン、依存関係extra、identityフェデレーション、ヘッダ転送、URL形状。ドメインロジックで破綻したものはありません。これがプロジェクト全体の結果の形状です。

Typed failureは6つを比較可能にする上で大きな役割を果たしました。各アダプタ例外は、1つの境界でvalidationproviderauthenticationtransporttimeoutprotocolのいずれか1つに正規化されます。「どのレイヤが破綻したか」はリサーチ上の問いであり、ログをgrepする演習ではありません。

本結果が確立しないもの

グリーンスモークテストから過大に主張したくなる誘惑こそ、本シリーズが抵抗するために構築されたものであるため、率直に述べます:

  • 6行のうち2行は単一観測です。 1回のhostedリクエストはレイテンシ分布ではありません。Foundry → AgentCore実行は測定済みアダプタ作業28.2 sに対し、エンドツーエンドで約45 sを要しました;その差はモデルおよびhosted-responseオーバーヘッドであり、1サンプルではいずれの数値もベンチマークと呼ぶのは無責任です。
  • 完全な38ケースマトリクスを持つのはGCP-remoteパス2つのみです。 AgentCore ↔ Foundryペアおよび両ADK-as-masterパスは未実行です。
  • トークン使用量とクラウドコストは全箇所で未測定です。 throttlingおよびtoken expiry下の挙動、繰り返しのwarm/cold分布も同様です。
  • AgentCore → ADKマトリクスはlocal harness上で実行されました。AgentCore hostingレイヤ経由ではありません。hostingを試したのはsmoke testのみです。このラベルを明示することで、ハーネスレイテンシをAgentCoreに帰属させることを避けます。
  • 本結果は、いずれのフレームワークまたはクラウドが他より優れていることを示しません。 レイテンシ差はremoteが使用したモデルとruntimeを追跡するものであり、プラットフォーム品質ではありません——またモデル速度とプラットフォームリクエストオーバーヘッドを分離しておらず、分離には両runtimeで同一モデルが必要です。
  • ADK → AgentCore数値は広いIAM policyで取得されました。 bedrock-agentcore:InvokeAgentRuntimeruntime/<id>およびruntime/<id>/*にスコープすると、agent-card取得時に403で拒否されました;Resource: "*"のみが機能しました。AgentCoreは未特定ARN形状に対して認可しており、データプレーンイベントはデフォルトでCloudTrailに表示されないため、denialを読むことができませんでした。したがってこの行はleast-privilege設定ではなく、そのように出荷するつもりはありません。
  • 2行はn=5、1日、1リージョンペア、1モデルペアです。 p95、トークン数、コスト会計、cold/warm分離はありません。

6行での総括

  1. A2A v1.0は全6方向で相互運用できました。 スキーマ変換不要、parseable JSONを要求する以上のフレームワーク固有グルー不要。
  2. プロトコルは決して遅い部分ではありません。 A2Aレッグはremoteのmodelとruntimeを追跡しました——Cloud Runコンテナへ1.7–2.1 s、どちらのhosted agent runtimeへも18.8–25.1 s。認証済みカード取得コストは0.35 s;パスへの第2クラウド追加コストは約2 s。
  3. クロスクラウドA2Aはidentityプロジェクトです。 service principal、data-plane role、token audience、callee上の非ネイティブauth、不可視のRBAC伝播に予算を割いてください。呼び出し元がworkload OIDCトークンを発行できる場合、calleeのネイティブオーソライザへのフェデレーションはbearerトークンを運ぶより優れています——audienceだけでなくsubjectも固定してください。
  4. バージョンずれはloudに、しかしlateに失敗します。3つの別個のメカニズムを通じて:推移的ピン、フレームワークextra、バージョンヘッダを除去 and するプロキシ。パッケージピンはプロセスを制約し、ワイヤを制約しません。バンドルを分離するかextraをバイパスする;どちらも検証済み脱出策です。
  5. イメージはソースツリーではありません。 グリーンテストスイートを生き延びたすべてのバグはpackagingまたはseamバグでした——そして最後の構築では、6つの欠陥のうち5つが実際にデプロイされるまで不可視でした。
  6. 検証が買うのは独立した障害検知であり、精度ではありません。 happy pathでは両クラウドは毎回正確に合意しました。注入された障害——およびlive Frankfurterクォートで発火した実際のstale-rate警告——が、第2のクラウドが第2の価値を発揮する場所です。

Source

各リポジトリ——デプロイスクリプト、テスト、評価ケース、生結果を含む:

Upstream agent from
Getting Started with MCP, ADK and A2A.

クラウド境界を越えてA2Aを実行したことのある方——特にAgentCoreが実際に認可するARN形状を解明した方、またはこれら6つがいずれも遭遇しなかったcard/version不一致に遭遇した方——ぜひ知見を共有いただければと思います。