A2A 的一个现实问题是:当团队开始部署多个 Agent,客户端到底该连谁、怎么发现、谁有权限访问,很快会从“协议问题”变成“入口治理问题”。这篇内容的核心做法是,在 AWS 上构建一个无服务器 A2A gateway,用单个域名承载多个 Agent,并通过 /agents/{agentId} 这种路径路由到不同后端。标准 A2A 客户端不需要修改,这是它最有价值的地方。
为什么要把多个 Agent 放到一个网关后面
如果每个 Agent 都暴露独立域名,短期看简单,长期会带来几个麻烦:客户端配置分散、证书和域名管理变多、访问控制策略难统一、Agent 上下线也难做一致的发现入口。
A2A gateway 的思路是把这些事情收束到一个边界上:
- 一个公共域名,例如
https://a2a.example.com - 每个 Agent 一个路径前缀,例如
/agents/weather、/agents/support - 网关负责发现、路由和访问控制
- 后端 Agent 可以继续保持自己的实现方式
路径路由的关键点在于:标准 A2A 客户端仍然访问符合预期的 Agent URL,只是这个 URL 被统一挂到了 gateway 域名下面。客户端不用知道后端到底是 Lambda、容器服务,还是另一个 HTTP 服务。
典型架构:API Gateway + Lambda 路由层
在 AWS 上可以这样实践一个最小可运行版本:
- Amazon API Gateway 提供 HTTPS 入口和路径匹配
- Lambda 作为轻量路由器,解析
/agents/{agentId} - DynamoDB 或配置文件保存
agentId -> upstream映射 - IAM、JWT authorizer 或 Lambda authorizer 负责访问控制
- 后端 Agent 暴露 HTTP 接口,网关把请求转发过去
来源摘要强调的是“serverless A2A gateway”和“path-based routing”。因此下面示例用 Lambda 代理请求来说明实现方式;生产环境可以把 Agent 注册表、鉴权和观测拆得更细。
可以这样实践:一个最小 Lambda 路由器
下面的 Node.js Lambda 示例会从路径中取出 agentId,根据静态注册表选择上游 Agent,并把请求转发过去。你需要把 AGENT_REGISTRY 改成自己的 Agent 地址。
// index.mjs
const AGENT_REGISTRY = {
weather: "https://weather-agent.example.com",
support: "https://support-agent.example.com"
};
export const handler = async (event) => {
const path = event.rawPath || event.path || "/";
const match = path.match(/^\/agents\/([^/]+)(\/.*)?$/);
if (!match) {
return json(404, { error: "Route must match /agents/{agentId}" });
}
const agentId = decodeURIComponent(match[1]);
const restPath = match[2] || "/";
const upstream = AGENT_REGISTRY[agentId];
if (!upstream) {
return json(404, { error: `Unknown agent: ${agentId}` });
}
// Minimal access-control example. Replace with JWT/IAM/authorizer in production.
const apiKey = event.headers?.["x-api-key"] || event.headers?.["X-Api-Key"];
if (apiKey !== process.env.GATEWAY_API_KEY) {
return json(403, { error: "Access denied" });
}
const query = event.rawQueryString ? `?${event.rawQueryString}` : "";
const targetUrl = `${upstream}${restPath}${query}`;
const response = await fetch(targetUrl, {
method: event.requestContext?.http?.method || event.httpMethod || "GET",
headers: sanitizeHeaders(event.headers || {}),
body: event.body
? event.isBase64Encoded
? Buffer.from(event.body, "base64")
: event.body
: undefined
});
const body = await response.text();
return {
statusCode: response.status,
headers: {
"content-type": response.headers.get("content-type") || "application/json"
},
body
};
};
function sanitizeHeaders(headers) {
const copy = { ...headers };
delete copy.host;
delete copy.Host;
return copy;
}
function json(statusCode, payload) {
return {
statusCode,
headers: { "content-type": "application/json" },
body: JSON.stringify(payload)
};
}
配套的 AWS SAM 模板可以这样写。运行前需要把 GATEWAY_API_KEY 换成你自己的测试密钥。
# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Minimal serverless A2A gateway with path-based routing
Resources:
A2AGatewayFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: nodejs20.x
Handler: index.handler
CodeUri: .
Timeout: 20
MemorySize: 256
Environment:
Variables:
GATEWAY_API_KEY: change-me-in-real-deployments
Events:
AgentProxy:
Type: HttpApi
Properties:
Path: /agents/{proxy+}
Method: ANY
本地和部署命令:
sam build
sam local start-api
# 另开一个终端测试。把 URL 和 key 改成你的本地或部署环境。
curl -i \
-H 'x-api-key: change-me-in-real-deployments' \
'http://127.0.0.1:3000/agents/weather/.well-known/agent-card.json'
sam deploy --guided
这个例子故意保持简单:Agent 注册表写死在代码里,访问控制也只是 API key 检查。它适合用来验证路径路由模型,不适合作为生产安全边界直接上线。
访问控制不该散落在每个 Agent 里
A2A gateway 很适合承担统一的访问控制。原因很直接:Agent 数量越多,越不应该让每个 Agent 各自实现一套鉴权策略。
可以按 Agent 粒度做策略,例如:
{
"tenant-a": ["weather", "support"],
"tenant-b": ["weather"]
}
在 Lambda authorizer 或路由 Lambda 中,根据 JWT 的 tenant_id、scope 或 IAM principal 判断是否允许访问目标 agentId。这样做的好处是策略集中,审计日志也集中;代价是 gateway 会变成关键路径,延迟、限流、错误处理都要认真设计。
Agent 发现:让客户端只记住一个入口
来源摘要提到 agent discovery。路径式网关天然可以提供一个发现端点,比如:
GET /agents
Host: a2a.example.com
Authorization: Bearer <token>
返回当前调用方可见的 Agent 列表:
{
"agents": [
{
"id": "weather",
"url": "https://a2a.example.com/agents/weather",
"description": "Weather information agent"
},
{
"id": "support",
"url": "https://a2a.example.com/agents/support",
"description": "Customer support agent"
}
]
}
注意这里的 url 指向网关路径,而不是后端真实地址。这样客户端拿到的始终是稳定入口,后端迁移、扩容、替换实现时,不需要推动客户端重新配置。
上线前的取舍清单
采用 serverless A2A gateway 时,我会重点检查这些点:
- 路由规则是否稳定:
/agents/{agentId}后面的子路径要原样传递,避免破坏标准 A2A 客户端行为。 - 鉴权是否集中:不要让 gateway 只做转发,却把权限判断留给每个 Agent 各自发挥。
- 注册表是否可运维:生产环境建议用 DynamoDB、Parameter Store 或内部控制面,而不是硬编码。
- 超时和流式响应是否匹配:如果 Agent 响应时间长,要确认 API Gateway、Lambda 和客户端超时设置一致。
- 日志是否带上
agentId、调用方和请求 ID:否则排查跨 Agent 问题会很痛苦。
这类网关的价值不在于“多加一层代理”,而在于给 Agent 生态建立一个稳定入口:客户端不改,Agent 可以变,访问策略有地方落地。等 Agent 数量从两个增长到几十个时,这个边界会变得非常实际。