用 AWS 搭一个无服务器 A2A 网关:一个域名管理多个 Agent

2026-07-02 30 预计阅读时间: 1 分钟
来源: aws.amazon.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

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_idscope 或 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 数量从两个增长到几十个时,这个边界会变得非常实际。


相关推荐