在 Knative 中部署 Agent2Agent (A2A) 协议服务器

Agent2Agent (A2A) 协议是一种开放标准,旨在实现 AI 智能体(Agent)之间的无缝通信与协作。通过在 Knative 中部署 A2A 服务器,能够利用其自动扩缩容(包括缩容至0)等特性,实现资源按需使用和版本的快速迭代。

工作原理

AI 智能体拥有推理、规划和记忆能力,能够自主学习并代表用户完成任务。类比于 MCP 为 LLM 提供数据访问标准,Agent2Agent (A2A) 协议为智能体之间的互操作性提供了标准化定义。

在 Knative 中部署 A2A 服务器,主要涉及以下核心交互:

  • 发现(Discovery):服务部署后,通过标准的智能体卡片暴露接口,允许其他智能体查询其技能 (AgentSkill)和功能(AgentCapabilities)。

  • 通信(Communication):利用 Knative 的网关和服务路由能力,处理基于 HTTP/gRPC 的标准消息交换。

  • 协作(Collaboration):智能体通过 API 委派任务并协调行动。

可参见 A2A 规范了解 Agent 通信的协议架构和核心概念

准备工作

  • 已在集群中部署Knative,请参见部署与管理Knative组件

  • 已获取访问网关地址。

    可在Knative组件管理服务管理页面获取。示例如下。

    image

步骤一:部署 A2A 服务器

本示例部署一个名为 helloworld-agent-server 的基础智能体服务。

  1. 创建a2a-service.yaml

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: helloworld-agent-server
      # 根据实际情况修改命名空间
      namespace: default 
      annotations:
        # 使用通配域名便于快速验证
        knative.aliyun.com/serving-ingress: / 
    spec:
      template:
        spec:
          containers:
          # 将 {region} 替换为实际使用的地域,如 cn-hangzhou
          - image: registry-{region}-vpc.ack.aliyuncs.com/acs/knative-samples-a2a:v1.0-952c112
            name: user-container
            env:
            # INVOKE 定义了 Agent Card 中返回的调用 URL,请替换为服务的访问网关地址
            - name: INVOKE
              value: http://<YOUR_GATEWAY_ADDRESS>/invoke
            ports:
            - containerPort: 9001
              name: http1
              protocol: TCP
  2. 部署服务。

    kubectl apply -f a2a-service.yaml
  3. 查看 Knative Service 状态。

    kubectl get ksvc helloworld-agent-server

    预期输出:

    NAME                      URL                                                  LATESTCREATED                   LATESTREADY                     READY   REASON
    helloworld-agent-server   http://helloworld-agent-server.default.example.com   helloworld-agent-server-00001   helloworld-agent-server-00001   True    

步骤二:验证服务与获取 Agent Card

部署完成后,需要验证服务是否能够正确返回符合 A2A 协议的智能体卡片(Agent Card)。

  1. 获取访问网关与服务默认域名。

    1. ACK集群列表页面,单击目标集群名称,在集群详情页左侧导航栏,选择应用 > Knative

    2. 服务管理页面,获取服务默认域名

      下图展示了访问网关默认域名

      image

  2. 访问服务的元数据端点。

    # 将 <GATEWAY_ADDRESS> 替换为实际获取的网关地址
    curl http://<GATEWAY_ADDRESS>/.well-known/agent-card.json | jq .

    查看预期输出,
    输出应包含智能体的能力(capabilities)、描述(description)和技能列表(skills)。

    {
      "capabilities": {
        "streaming": true
      },
      "defaultInputModes": [
        "text"
      ],
      "defaultOutputModes": [
        "text"
      ],
      "description": "Just a hello world agent",
      "name": "Hello World Agent",
      "preferredTransport": "JSONRPC",
      "protocolVersion": "",
      "skills": [
        {
          "description": "Returns a 'Hello, world!'",
          "examples": [
            "hi",
            "hello"
          ],
          "id": "hello_world",
          "name": "Hello, world!",
          "tags": [
            "hello world"
          ]
        }
      ],
      "url": "http://XXX/invoke",
      "version": ""
    }

步骤三:通过 A2A Client 调用服务

使用 Golang 编写客户端代码,模拟另一个智能体与部署的 A2A 服务器进行通信。

  1. 准备开发环境,安装 Go 语言环境

  2. 创建 main.go 文件,写入以下代码。

    package main
    
    import (
    	"context"
    	"flag"
    	"log"
    
    	// 引入 A2A 协议相关的核心库
    	"github.com/a2aproject/a2a-go/a2a"
    	"github.com/a2aproject/a2a-go/a2aclient"
    	"github.com/a2aproject/a2a-go/a2aclient/agentcard"
    
    	// 引入 gRPC 相关的库
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/credentials/insecure"
    )
    
    // 将 <GATEWAY_ADDRESS> 替换为实际获取的网关地址
    var cardURL = flag.String("card-url", "http://<GATEWAY_ADDRESS>", "Base URL of AgentCard client.")
    
    func main() {
    	flag.Parse()
    	ctx := context.Background()
    
    	// 服务发现
    	card, err := agentcard.DefaultResolver.Resolve(ctx, *cardURL)
    	if err != nil {
    		log.Fatalf("Failed to resolve an AgentCard: %v", err)
    	}
    
    	// 配置传输层
    	withInsecureGRPC := a2aclient.WithGRPCTransport(grpc.WithTransportCredentials(insecure.NewCredentials()))
    
    	// 创建客户端
    	client, err := a2aclient.NewFromCard(ctx, card, withInsecureGRPC)
    	if err != nil {
    		log.Fatalf("Failed to create a client: %v", err)
    	}
    
    	// 构建消息
    	msg := a2a.NewMessage(a2a.MessageRoleUser, a2a.TextPart{Text: "Hello, world"})
    	resp, err := client.SendMessage(ctx, &a2a.MessageSendParams{Message: msg})
    	if err != nil {
    		log.Fatalf("Failed to send a message: %v", err)
    	}
    
    	log.Printf("Server responded with: %+v", resp)
    }
  3. 运行代码进行测试。

    go mod init a2a-demo
    go mod tidy
    go run main.go

    预期输出如下,表明客户端已成功连接到Knative 服务,且服务端已经处理完毕并返回了响应。

    2025/11/27 17:24:21 Server responded with: &{ID:019ac4a0-c386-7cdc-9aad-d40fb8f98ae2 ContextID: Extensions:[] Metadata:map[] Parts:[{Text:Hello, world! Metadata:map[]}] ReferenceTasks:[] Role:agent TaskID:}

生产环境使用建议

  • 自定义域名与 HTTPS:生产环境中请勿直接使用测试域名。建议配置自定义域名并开启 HTTPS 证书,以确保 Agent 间通信的安全性。

  • 冷启动优化:如果智能体调用频率较低,Knative 会将实例缩容至0。为避免首个请求的冷启动延迟影响交互体验,可配置最小实例数(MinScale)或配置保留实例

计费说明

Knative组件本身不产生额外费用。但在使用过程中产生的计算资源(如ECS)、网络资源(如ALB)等费用,由各云产品收取。请参见云产品资源费用