詳解 A2A-Agent2Agent- 協議

什麼是 A2A 協議

A2A(Agent2Agent)協議 是由 Google Cloud 推出的一個開放協議,旨在促進不同 AI 代理之間的互操作性。其主要目標是允許這些代理在動態的、多代理的生態系統中進行有效的通信和協作,無論它們是由不同的供應商構建的還是使用不同的技術框架。

A2A 的設計原則總結

A2A(Agent2Agent)協議的設計原則旨在提升代理之間的協作能力,確保靈活性、安全性和與現有系統的兼容性。以下是這些原則的綜合總結:

  1. 擁抱代理能力
  1. 基於現有標準構建
  1. 默認安全
  1. 支持長時間運行的任務
  1. 模態無關

整體看下來,協議在開放性、安全性、靈活性上考慮得比較多。這些點都是 MCP 有所不足的。和 MCP 的對比我們放在最後。先說正題——詳解 A2A

A2A 的參與者

A2A 協議有三個參與者:

參考如下的圖

通過上面的圖,我們可以清晰地看到三個參與者的位置,對比之前 MCP 參與者,缺少一個 Host 的參與者。這個是設計思路上的不同,是要開放實現,還是規範一個機制,在 A2A 的實現中,安全等因素,已經通過別的方式實現,但確實 User 如何發現需要的 Agent,是一個遺留的問題。

A2A 核心概念

AgentCard

AgentCard 是一個 JSON 文件,描述了 Agent 提供了什麼樣的功能,官方建議託管在 https:// base url /.well-known/agent.json
這樣就可以直接通過 HTTP GET 獲取 AgentCard,得到有關 Agent 的描述。

一個自然的引申是:需要註冊表,無論是公開的、還是隱私的。這樣方便查找 Agent 。

但另一個方面,註冊表也可以是去中心化的。我們想象這樣一個場景:每一個網站都有一個 https:// base url /.well-known/agent.json,描述了自己可以做什麼,然後在一個 P2P 的網絡中,不斷的廣播自己的 AgentCard ——甚至這些 AgentCard,可以放在 IPFS 、或者以太坊上,這樣 Agent 的協作關係,就構成了一個自組織的 Agent 網絡。

回到 A2A,一個 AgentCard 的定義如下:

// An AgentCard conveys key information:
// - Overall details (version, name, description, uses)
// - Skills: A set of capabilities the agent can perform
// - Default modalities/content types supported by the agent.
// - Authentication requirements
interface AgentCard {
  // Human readable name of the agent.
  // (e.g. "Recipe Agent")
  namestring;
  // A human-readable description of the agent. Used to assist users and
  // other agents in understanding what the agent can do.
  // (e.g. "Agent that helps users with recipes and cooking.")
  descriptionstring;
  // A URL to the address the agent is hosted at.
  urlstring;
  // The service provider of the agent
  provider?: {
    organizationstring;
    urlstring;
  };
  // The version of the agent - format is up to the provider. (e.g. "1.0.0")
  versionstring;
  // A URL to documentation for the agent.
  documentationUrl?string;
  // Optional capabilities supported by the agent.
  capabilities: {
    streaming?boolean; // true if the agent supports SSE
    pushNotifications?boolean; // true if the agent can notify updates to client
    stateTransitionHistory?boolean; //true if the agent exposes status change history for tasks
  };
  // Authentication requirements for the agent.
  // Intended to match OpenAPI authentication structure.
  authentication: {
    schemesstring[]; // e.g. Basic, Bearer
    credentials?string; //credentials a client should use for private cards
  };
  // The set of interaction modes that the agent
  // supports across all skills. This can be overridden per-skill.
  defaultInputModesstring[]; // supported mime types for input
  defaultOutputModesstring[]; // supported mime types for output
  // Skills are a unit of capability that an agent can perform.
  skills: {
    idstring; // unique identifier for the agent's skill
    namestring; //human readable name of the skill
    // description of the skill - will be used by the client or a human
    // as a hint to understand what the skill does.
    descriptionstring;
    // Set of tagwords describing classes of capabilities for this specific
    // skill (e.g. "cooking", "customer support", "billing")
    tagsstring[];
    // The set of example scenarios that the skill can perform.
    // Will be used by the client as a hint to understand how the skill can be
    // used. (e.g. "I need a recipe for bread")
    examples?string[]; // example prompts for tasks
    // The set of interaction modes that the skill supports
    // (if different than the default)
    inputModes?string[]; // supported mime types for input
    outputModes?string[]; // supported mime types for output
  }[];
}

內容很長,但是比較簡單,我們用下圖來表示:

完整的定義可以參考這裏:https://github.com/sing1ee/a2a-agent-coder/blob/main/src/schema.ts

Task(任務)

任務是一個有狀態的實體,允許客戶端與遠程代理協作以達成特定的結果並生成相應的輸出。在任務內,客戶端與遠程代理之間會交換消息,遠程代理則生成工件作爲結果(代理即是 Agent)。

任務始終由客戶端創建,而其狀態則由遠程代理決定。如果客戶端需要,多個任務可以歸屬於同一個會話(通過可選的 sessionId 表示)。在創建任務時,客戶端可以設置這個可選的 sessionId。

代理收到請求之後,可以採取以下幾種行動:

即使在完成目標後,客戶端仍然可以請求更多信息或在同一任務的上下文中進行更改。例如,客戶端可以請求:“畫一隻兔子的圖片”,代理迴應:“< 圖片 >”,隨後客戶端又可以要求:“把它畫成紅色”。

任務不僅用於傳遞工件(結果)和消息(思考、指令等),還維護着任務的狀態及其可選的歷史記錄,包括狀態變化和消息記錄。

這些特性非常重要,尤其是同一個任務的上下文,可以進行多輪的對話,這些狀態,還有歷史記錄,都有保存,這個非常匹配現在以 Chat 形式爲主的 AI 交互。

任務的定義如下:

interface Task {
  idstring; // unique identifier for the task
  sessionIdstring; // client-generated id for the session holding the task.
  statusTaskStatus; // current status of the task
  history?Message[];
  artifacts?Artifact[]; // collection of artifacts created by the agent.
  metadata?Record<string, any>; // extension metadata
}
// TaskState and accompanying message.
interface TaskStatus {
  stateTaskState;
  message?Message; //additional status updates for client
  timestamp?string; // ISO datetime value
}
// sent by server during sendSubscribe or subscribe requests
interface TaskStatusUpdateEvent {
  idstring;
  statusTaskStatus;
  finalboolean; //indicates the end of the event stream
  metadata?Record<string, any>;
}
// sent by server during sendSubscribe or subscribe requests
interface TaskArtifactUpdateEvent {
  idstring;
  artifactArtifact;
  metadata?Record<string, any>;
}
// Sent by the client to the agent to create, continue, or restart a task.
interface TaskSendParams {
  idstring;
  sessionId?string; //server creates a new sessionId for new tasks if not set
  messageMessage;
  historyLength?number; //number of recent messages to be retrieved
  // where the server should send notifications when disconnected.
  pushNotification?PushNotificationConfig;
  metadata?Record<string, any>; // extension metadata
}
type TaskState =
  | "submitted"
  | "working"
  | "input-required"
  | "completed"
  | "canceled"
  | "failed"
  | "unknown";

Artifact(工件)

工件是代理作爲任務最終結果生成的輸出。工件具有不可變性,可以被命名,並且可以包含多個部分。通過流式響應,可以將新部分附加到現有的工件中。

一個任務可以生成多個工件。例如,當執行 “創建一個網頁” 時,可能會產生單獨的 HTML 工件和圖像工件。

不得不說 A2A 出現的時機很準確,現在 AI 的一些主要的應用的形式,在協議定義上都包括了。Artifact 就是很火的一個形式。

具體的定義:

interface Artifact {
  name?string;
  description?string;
  partsPart[];
  metadata?Record<string, any>;
  indexnumber;
  append?boolean;
  lastChunk?boolean;
}

Message(消息)

消息是包含任何非工件內容的實體。這些內容可以包括代理的思考、用戶的上下文、指令、錯誤信息、狀態更新或元數據。

所有來自客戶端的內容均以消息的形式發送。代理通過消息來傳達狀態或提供指令,而生成的結果則以工件的形式發送。

消息可以包含多個 Part(片段),以表示不同類型的內容。例如,一個用戶請求可能包括用戶的文本描述以及多個用於上下文的文件。

定義如下:

interface Message {
  role: "user" | "agent";
  partsPart[];
  metadata?Record<string, any>;
}

Part(片段)

Part 是客戶端與遠程代理之間作爲消息或工件一部分交換的完整內容。每個 Part 都有其獨特的內容類型和元數據。

以下是不同類型部分的接口定義:

文本部分(TextPart)

interface TextPart {
type: "text";
textstring;
}

文件部分(FilePart)

interface FilePart {
type: "file";
file: {
  name?: string;
  mimeType?: string;
  // 可能的內容
  // oneof {
  bytes?: string; // base64 編碼的內容
  uri?: string;
  //}
};
}

數據部分(DataPart)

interface DataPart {
type: "data";
dataRecord<string, any>;
}

綜合類型

type Part = (TextPart | FilePart | DataPart) & {
metadataRecord<string, any>;
};

更多的消息的細節,參考鏈接:https://a2aprotocol.ai/blog/a2a-sample-methods-and-json-responses

通信機制與異步支持

A2A 支持以下的通信機制:

![](https://mmbiz.qpic.cn/sz_mmbiz_png/uVhqWvaiaiaPPibzcKOSmFfib3TVvFDZVE4HZ4F8GHyYBIOxEE24SImHWC5NK5NrKNUK9U0ptZdZmTp8hd1Km1qicibg/640?wx_fmt=png&from=appmsg)

在推送通知時,代理需要驗證通知服務的身份,並使用受信任的憑證進行身份驗證,以確保通知的安全性。
基於以上的通信機制,A2A 支持客戶端在處理長時間運行的任務時進行輪詢,代理也可以通過 SSE 向客戶端推送狀態更新。

這裏,最重要的是異步的支持,client 可以通過類似註冊一個 webhook,異步的獲取長時間運行任務的結果——就是 PushNotification 相關的實現。目前大家在使用 LLMs API 的時候,都會遇到一個問題,就是輸出太慢了,而且輸出的過程中,並不能做別的事情。如果有了異步的回調,或者輪詢、重新訂閱,那麼就可以在 client 的開發上,更加靈活,可以給用戶帶來更好的體驗。

以下是推送的定義:

interface PushNotificationConfig {
  urlstring;
  token?string; // token unique to this task/session
  authentication?: {
    schemesstring[];
    credentials?string;
  };
}
interface TaskPushNotificationConfig {
  idstring; //task id
  pushNotificationConfigPushNotificationConfig;
}

錯誤處理(Error Handling)

錯誤消息格式

以下是服務器在處理客戶端請求時遇到錯誤時響應客戶端的 ErrorMessage 格式:

interface ErrorMessage {
codenumber;
messagestring;
data?any;
}

標準 JSON-RPC 錯誤代碼

以下爲服務器在錯誤場景中可以響應的標準 JSON-RPC 錯誤代碼:

3SoijZ

動手實踐

我把官方的 ts 的示例進行了修改,支持了 OpenRouter,主要是改動了兼容 OpenAI 的 API 形式。代碼在這裏:https://github.com/sing1ee/a2a-agent-coder

我是在 Mac 環境下進行的,打開你最愛的終端:

  1. 安裝 Bun
brew install oven-sh/bun/bun # 針對 macOS 和 Linux
  1. 克隆倉庫
git clone git@github.com:sing1ee/a2a-agent-coder.git
  1. 安裝依賴
cd a2a-agent-coder
bun i
  1. 配置環境變量
    參考 .env.example 創建一個.env 文件,內容如下:
OPENAI_API_KEY=sk-or-v1-xxxxxxx
OPENAI_BASE_URL=https://openrouter.ai/api/v1
OPENAI_MODEL=anthropic/claude-3.5-haiku

我用的是 OpenRouter,支付方便,模型衆多。大家嘗試的話,可以註冊一個 OpenRouter,即使沒有充值,可以有每天 50 次免費模型的額度,例如 deepseek/deepseek-chat-v3-0324:free
讓環境變量生效

export $(cat .env | xargs)
  1. 運行 A2A Server
bun run agents:coder
  1. 再打開一個新的終端,運行 A2A Client,這裏不需要配置 env
bun run a2a:cli

以下是我之前運行的結果:

bun run a2a:cli

# result
$ bun x tsx src/cli.ts
A2A Terminal Client
Agent URL: http://localhost:41241
Attempting to fetch agent card from: http://localhost:41241/.well-known/agent.json
✓ Agent Card Found:
  Name:        Coder Agent
  Description: An agent that generates code based on natural language instructions and streams file outputs.
  Version:     0.0.1
Starting Task ID: a1a608b3-3015-4404-a83f-6ccc05083761
Enter messages, or use '/new' to start a new task.
Coder Agent > You: implement binary search
Sending...

Coder Agent [4:28:00 PM]: ⏳ Status: working
  Part 1: 📝 Text: Generating code...

Coder Agent [4:28:02 PM]: ⏳ Status: working
  Part 1: 📄 File: Name: src/algorithms/binary_search.py, Source: """
Implementation of the binary search algorithm in Python.
"""

def binary_search(arr, target):
    """
    Performs a binary search on a sorted array to find the index of a target value.

    Args:
        arr (list): A sorted list of elements.
        target: The value to search for in the array.

    Returns:
        int: The index of the target value if found, otherwise -1.
    """
    low = 0
    high = len(arr) - 1

    while low <= high:
        mid = (low + high) // 2  # Integer division to find the middle index

        if arr[mid] == target:
            return mid  # Target found at index mid
        elif arr[mid] < target:
            low = mid + 1  # Target is in the right half
        else:
            high = mid - 1  # Target is in the left half

    return -1  # Target not found in the array


Coder Agent [4:28:02 PM]: ✅ Status: completed
SSE stream finished for method tasks/sendSubscribe.
--- End of response for this input ---
Coder Agent > You:
Exiting terminal client. Goodbye!

運行過程的流程圖如下:

目前非程序員用戶想體驗,還需要耐心等待,也可以藉助 Cursor 等試一試。

A2A 與 MCP 比較

這個問題,很多人關心,我大概做了一個總結:

pCyAS0

同時,我也在做一些思考,

都在思考中,需要更多的實踐。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/ySDTLuWvJeO9n7uBw2XxmQ