Deploy an Agent2Agent (A2A) protocol server in Knative

更新时间:
复制 MD 格式

Agent2Agent (A2A) is an open standard designed to enable seamless communication and collaboration between AI agents. Deploying an A2A server in Knative leverages features such as automatic scaling—including scale-in to zero—to enable on-demand resource usage and rapid version iteration.

How it works

AI agents possess inference, planning, and memory capabilities, allowing them to learn autonomously and complete tasks on behalf of users. Similar to how MCP provides a data access standard for LLMs, the Agent2Agent (A2A) protocol defines a standardized framework for interoperability between agents.

Deploying an A2A server in Knative involves the following core interactions:

  • Discovery: After deployment, the service exposes its interface through a standard agent card, allowing other agents to query its skills (AgentSkill) and capabilities (AgentCapabilities).

  • Communication: Use Knative’s gateway and service routing capabilities to handle standard message exchanges over HTTP or gRPC.

  • Collaboration: Agents delegate tasks and coordinate actions through APIs.

See the A2A specification for the protocol architecture and key concepts of agent communication.

Preparations

  • Deploy Knative in your cluster. For more information, see Deploy and manage Knative components.

  • You have obtained the gateway access address.

    You can find it on the Add-ons or Services page. The following example shows how to locate it.

    At the bottom of the Service Management page, view the Access Gateway address. It follows this format: alb-<instance ID>.cn-hangzhou.alb.aliyuncsslb.com.

Step 1: Deploy the A2A server

This example deploys a basic agent service named helloworld-agent-server.

  1. Create a2a-service.yaml.

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: helloworld-agent-server
      # Update the namespace as needed
      namespace: default 
      annotations:
        # Use a wildcard domain for quick validation
        knative.aliyun.com/serving-ingress: / 
    spec:
      template:
        spec:
          containers:
          # Replace {region} with your actual region, such as cn-hangzhou
          - image: registry-{region}-vpc.ack.aliyuncs.com/acs/knative-samples-a2a:v1.0-952c112
            name: user-container
            env:
            # INVOKE defines the invoke URL returned in the Agent Card. Replace it with your gateway address.
            - name: INVOKE
              value: http://<YOUR_GATEWAY_ADDRESS>/invoke
            ports:
            - containerPort: 9001
              name: http1
              protocol: TCP
  2. Deploy the service.

    kubectl apply -f a2a-service.yaml
  3. Check the Knative Service status.

    kubectl get ksvc helloworld-agent-server

    Expected output:

    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    

Step 2: Verify the service and retrieve the agent card

After deployment, verify that the service correctly returns an agent card compliant with the A2A protocol.

  1. Get the gateway address and the service’s default domain name.

    1. On the ACK Clusters page, click the name of your cluster. In the left navigation pane, click Applications > Knative.

    2. On the Services page, get the Default Domain of the service.

      The following shows the Gateway and the Default Domain.

      The gateway address follows this format: alb-<instance ID>.cn-hangzhou.alb.aliyuncsslb.com. The service helloworld-agent-server is in the successful state, and its default domain name is helloworld-agent-server.default.example.com.

  2. Access the service’s metadata endpoint.

    # Replace <GATEWAY_ADDRESS> with your actual gateway address
    curl http://<GATEWAY_ADDRESS>/.well-known/agent-card.json | jq .

    Review the expected output.
    The output must include the agent’s capabilities (capabilities), description (description), and list of skills (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": ""
    }

Step 3: Call the service using an A2A client

Write client code in Golang to simulate another agent communicating with the deployed A2A server.

  1. Set up your development environment and install Go.

  2. Create a file named main.go and add the following code.

    package main
    import (
    	"context"
    	"flag"
    	"log"
    	// Import core libraries for the A2A protocol
    	"github.com/a2aproject/a2a-go/a2a"
    	"github.com/a2aproject/a2a-go/a2aclient"
    	"github.com/a2aproject/a2a-go/a2aclient/agentcard"
    	// Import gRPC-related libraries
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/credentials/insecure"
    )
    // Replace <GATEWAY_ADDRESS> with your actual gateway address
    var cardURL = flag.String("card-url", "http://<GATEWAY_ADDRESS>", "Base URL of AgentCard client.")
    func main() {
    	flag.Parse()
    	ctx := context.Background()
    	// Service discovery
    	card, err := agentcard.DefaultResolver.Resolve(ctx, *cardURL)
    	if err != nil {
    		log.Fatalf("Failed to resolve an AgentCard: %v", err)
    	}
    	// Configure the transport layer
    	withInsecureGRPC := a2aclient.WithGRPCTransport(grpc.WithTransportCredentials(insecure.NewCredentials()))
    	// Create the client
    	client, err := a2aclient.NewFromCard(ctx, card, withInsecureGRPC)
    	if err != nil {
    		log.Fatalf("Failed to create a client: %v", err)
    	}
    	// Build the message
    	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. Run the code to test.

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

    The following output indicates that the client successfully connected to the Knative service and received a response from the server.

    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:}

Production environment recommendations

  • Custom domain and HTTPS: Do not use test domains in production. Configure a custom domain name and enable an HTTPS certificate to secure agent-to-agent communication.

  • Cold start optimization: If agent calls are infrequent, Knative scales instances down to zero. To avoid cold-start latency on the first request, configure a minimum instance count (MinScale) or configure reserved instances.

Billing

Knative itself does not incur additional charges. However, you are billed for the cloud services that you use, such as computing and network resources. For more information, see Cloud service costs and .