Testing Three Agent Frameworks on A2A — What Broke and What
The setup that made comparison possible
The only way to learn anything is to freeze everything that isn't the variable under test. So I shared:
- The brief and its focus questions (verbatim)
- The system instruction, versioned
- A single search function I wrote myself — not any vendor's built-in
- The scoring rubric, versioned
- Output format: markdown with one stamped header
- Failure taxonomy so errors meant the same thing across runs
The search tool decision matters. Only Google ships a ready search tool. Microsoft's
SupportsWebSearchTool is a protocol a chat client declares, not a tool you hand an agent. Strands bundles none. If I'd used "native search everywhere" I'd be comparing Google's index against Bing against nothing — that's a retrieval product comparison, not a framework comparison. I wanted to see how each framework binds and drives a tool. That's now the only part that varies.Three frameworks, three shapes for the same agent
Google ADK — cleanest constructor, fewest moving parts:
from google.adk.agents import LlmAgent
LlmAgent(
model="gemini-2.5-flash",
name=..., description=...,
instruction=INSTRUCTION,
tools=[web_search],
)Model is a string. Tools are plain callables. to_a2a() serves it. Deployed to Cloud Run in us-central1.
AWS Strands — explicit decoration, model as object:
from strands import Agent, tool
from strands.models import BedrockModel
Agent(
model=BedrockModel(model_id="us.amazon.nova-micro-v1:0"),
system_prompt=INSTRUCTION,
tools=[tool(web_search)],
)You wrap every tool with @tool or tool(). Served via the a2a-sdk reference routes. Hosted on Bedrock AgentCore in us-west-2.
Microsoft Agent Framework — most ceremony, separate executor:
from agent_framework import Agent, A2AExecutor
agent = Agent(
model="gpt-5-mini",
instructions=INSTRUCTION,
tools=[web_search],
)
executor = A2AExecutor(agent)Model runs on Foundry. Served via A2AExecutor. Container Apps in westus2.
What surprised me
Tool binding feels completely different. ADK takes a raw function. Strands demands a decorator. Agent Framework sits somewhere in between but requires the executor wrapper. If you're moving agents between clouds, this is the rewrite surface — not the A2A layer.
Model selection leaks into framework ergonomics. ADK expects a model ID string. Strands wants a BedrockModel object. Agent Framework takes a deployment name that resolves in Foundry. You can't swap models without touching framework code.
Hosting isn't abstracted. Cloud Run, AgentCore, Container Apps — each has its own auth, scaling, and cold-start profile. The framework doesn't hide this.
Credential handling is a mess. Three different auth flows. Three different secret stores. Zero common interface.
Where I'm stuck
The coordinator fans the same brief to all three and scores responses. But scoring is subjective — I'm using an LLM-as-judge with a rubric, which has its own variance. Anyone tried a more deterministic eval harness for this?
Also: Strands on AgentCore cold-starts noticeably slower than Cloud Run for the same workload. Is that the runtime or the model server? Haven't isolated it yet.
Code is at github.com/xbill9/multicloud-a2a-subagent if you want to poke at it. Next week I'm adding a fourth column — self-hosted on vLLM — to see where the platform differences end and the model differences begin.