微信扫码
添加专属顾问
我要投稿
深入解析A2A协议核心组件,提升智能体协作理解。 核心内容: 1. A2A协议核心组件概览 2. 智能体卡(Agent Card)的构成与作用 3. 任务、人工制品、消息等组件的详细解析
每一种协议都是一种规范,都有自己特定的一些组件,只要大家都按照这个组件规范来,才可以实现互联互通,我们通过《一文讲清楚关于智能体之间的协作方式-A2A协议》了解了A2A协议,那这篇文章就深入刨析一下A2A协议的核心组件,加深一下对于A2A协议的理解。
A2A协议中的核心组件主要包括有:代理卡或智能体卡(Agent Card)、任务(Task)、人工制品(Artifact)、消息(Message)、部件或工件(Part)、推送通知(PushNotification),下面就一一介绍下
智能体卡指的是一个智能体的“简历”,支持A2A的的远程智能体需要以JSON格式发布智能体卡,描述智能体的功能/技能和身份验证机制,客户端使用智能体卡信息来确定可以执行任务的最佳智能体,并利用A2A与该远程智能体进行通信。
智能体卡信息
{ // 代理的人类可读名称(例如:"菜谱助手") name: string; // 代理的描述信息,用于帮助用户和其他代理理解其功能 //(例如:"帮助用户提供菜谱和烹饪指导的代理") description: string; // 代理托管地址的URL url: string; // 代理服务提供商(可选) provider?: { organization: string; // 组织名称 url: string; // 提供商URL }; // 代理版本号(格式由提供商定义,例如:"1.0.0") version: string; // 代理文档的URL(可选) documentationUrl?: string; // 代理支持的扩展能力(可选) capabilities: { streaming?: boolean; // 是否支持SSE流式传输 pushNotifications?: boolean; // 是否支持向客户端推送通知 stateTransitionHistory?: boolean; // 是否暴露任务状态变更历史 }; // 代理的认证要求(遵循OpenAPI认证结构) authentication: { schemes: string[]; // 认证方案(例如:Basic, Bearer) credentials?: string; // 私有卡使用的客户端凭证 }; // 代理默认支持的交互模式(所有技能通用) defaultInputModes: string[]; // 输入支持的MIME类型 defaultOutputModes: string[]; // 输出支持的MIME类型 // 代理具备的技能单元 skills: { id: string; // 技能的惟一标识符 name: string; // 技能的人类可读名称 description: string; // 技能描述(客户端/人类理解功能的提示) tags: string[]; // 功能类别标签(例如:"烹饪", "客服", "计费") examples?: string[]; // 使用场景示例(例如:"我需要面包食谱") inputModes?: string[]; // 输入MIME类型(覆盖默认配置时使用) outputModes?: string[]; // 输出MIME类型(覆盖默认配置时使用) }[];}
任务(Task)是一个有状态实体,它允许客户端(Client)和远程代理(Remote Agent)实现特定功能并生成结果,客户端和远程代理在任务(Task)中交换消息。远程代理将结果生成为工件。
任务始终由客户端创建,状态始终由远程代理(Remote Agent)来确定,如果客户端需要,多个任务可以是公共会话(有可选的sessionId表示)的一部分。为此,Client在创建Task时设置了一个可选的sessionId。
代理可以做以下几件事情
立即完成请求
将工作安排到以后
拒绝请求
协商不同的模式
向客户询问更多信息
委托给其他代理和系统
即使在代理完成目标之后,客户端也可以进行多轮对话来请求更多信息或更改统一任务的上下文。比如:
客户端:“画一张兔子的图片”
智能体:<image>兔子</image>
客户端:“将兔子变成红色的”
任务的主要作用:
任务用于传输工件(结果)和消息(想法、说明、任何其他内容)
任务维护状态以及消息的可选历史记录
任务描述实体
{ id: string; // 任务的唯一标识符 sessionId: string; // 客户端生成的会话ID(用于关联任务所属会话) status: TaskStatus; // 任务的当前状态 history?: Message[]; // 消息历史记录(可选) artifacts?: Artifact[]; // 代理创建的产物集合(可选) metadata?: Record<string, any>; // 扩展用元数据(可选)}
任务状态实体(任务状态及其附加信息)
{ state: TaskState; // 任务状态类型 message?: Message; // 给客户端的附加状态更新信息(可选) timestamp?: string; // ISO格式的日期时间值(可选)}
任务状态更新事件(在订阅请求期间由服务器发送)
{ id: string; // 唯一标识符 status: TaskStatus; // 任务状态 final: boolean; // 标识事件流是否结束的布尔值 metadata?: Record<string, any>; // 扩展元数据(可选)}
任务工件更新事件
{ id: string; // 唯一标识符 artifact: Artifact; // 代理生成的产物对象 metadata?: Record<string, any>; // 扩展元数据(可选)}
任务发送参数
{ id: string; // 唯一标识符 sessionId?: string; // 未设置时服务器会为新建任务生成会话ID(可选) message: Message; // 消息对象 historyLength?: number; // 要获取的最近消息数量(可选) pushNotification?: PushNotificationConfig; // 连接断开时服务器发送通知的目标配置(可选) metadata?: Record<string, any>; // 扩展元数据(可选)}
任务状态枚举(TaskState)
| "submitted" // 已提交(任务已创建待处理)| "working" // 处理中(任务正在执行)| "input-required" // 需交互(等待用户输入或确认)| "completed" // 已完成(任务成功结束)| "canceled" // 已取消(任务被主动终止)| "failed" // 已失败(任务执行异常)| "unknown" // 未知状态(系统无法识别)
工件作为任务的最终结果。工件是不可变的,可以命名,并且可以多个工件。流式处理响应可以将部件附加在现有的工件上。
单个任务可以生成多个工件。例如,“create a webpage”可以创建单独的HTML和图像工件。
{ name?: string; // 名称(可选) description?: string; // 描述(可选) parts: Part[]; // 工件列表(Part对象数组) metadata?: Record<string, any>; // 扩展元数据(可选) index: number; // 分块序号 append?: boolean; // 追加模式标识(可选,是否追加到现有数据末尾) lastChunk?: boolean; // 结束分块标识(可选,是否为最后一个数据分块)}
消息包含不是工件的任何内容,这可能包含代理想法、用户上下文、说明、错误、状态或元数据等内容。
来自客户端的所有内容都以消息的形式出现。代理发送消息以传达状态或提供说明(而生成的结果以工件发送)
消息可以有多个部分来表示不同的内容,例如,用户请求可以包括来自用户的文本描述,然后包含用作客户端上下文的多个文件
{ role: "user" | "agent"; // 角色类型("user"表示用户,"agent"表示代理) parts: Part[]; // 内容部分数组(Part对象列表) metadata?: Record<string, any>; // 扩展元数据(可选)}
客户端与远程代理之间交换的完整格式内容,作为Message或Artifact的一部分,每个部件都有自己的内容类型和元数据
文本部件(TextPart)
{ type: "text"; // 类型标识(固定为'text') text: string; // 文本内容字符串}
文件部件(FilePart)
{ type: "file"; // 文件类型标识 file: { // 文件信息对象 name?: string; // 文件名(可选) mimeType?: string; // MIME类型(可选) // 二选一配置项: bytes?: string; // Base64编码内容 uri?: string; // 资源定位地址 };}
数据部件(DataPart)
{ type: "data"; // 类型标识(固定为'data') data: Record<string, any>; // 通用数据对象(键值对结构)}
A2A 支持一种安全通知机制,代理可以通过 PushNotificationService 将更新通知客户端连接会话之外。在企业内部和企业之间,代理验证通知服务的身份,使用服务验证自身身份,并提供将通知与正在执行的任务相关联的标识符,这一点至关重要。
PushNotificationService 的目标服务器应被视为单独的服务,并且不能保证(甚至期望)直接成为客户端。此 PushNotificationService 负责对代理进行身份验证和授权,并将已验证的通知代理到相应的终端节点(可以是从发布/订阅队列到电子邮件收件箱或其他服务等的任何内容)。
对于具有隔离的客户端-代理对(例如,包含的 VPC 中的本地服务网格等)的人为场景或没有企业安全问题的隔离环境,客户端可以选择简单地打开一个端口并充当自己的 PushNotificationService。任何企业实施都可能具有一个集中式服务,该服务使用受信任的通知凭证对远程代理进行身份验证,并且可以处理联机/脱机方案。(这应该类似于移动推送通知服务)。
推送通知配置信息(PushNotificationConfig )
{ url: string; // 任务地址URL token?: string; // 此任务/会话独有的身份令牌(可选) authentication?: { // 认证配置(可选) schemes: string[]; // 支持的认证方案(如:Basic, Bearer) credentials?: string; // 客户端使用的认证凭证(可选) };}
任务推送通知配置信息(TaskPushNotificationConfig )
{ id: string; // 任务ID(关联的任务唯一标识) pushNotificationConfig: PushNotificationConfig; // 推送通知配置(定义通知策略的对象)}
{ "name": "Google Maps Agent", "description": "Plan routes, remember places, and generate directions", "url": "https://maps-agent.google.com", "provider": { "organization": "Google", "url": "https://google.com" }, "version": "1.0.0", "authentication": { "schemes": "OAuth2" }, "defaultInputModes": ["text/plain"], "defaultOutputModes": ["text/plain", "application/html"], "capabilities": { "streaming": true, "pushNotifications": false }, "skills": [ { "id": "route-planner", "name": "Route planning", "description": "Helps plan routing between two locations", "tags": ["maps", "routing", "navigation"], "examples": [ "plan my route from Sunnyvale to Mountain View", "what's the commute time from Sunnyvale to San Francisco at 9AM", "create turn by turn directions from Sunnyvale to Mountain View" ], // can return a video of the route "outputModes": ["application/html", "video/mp4"] }, { "id": "custom-map", "name": "My Map", "description": "Manage a custom map with your own saved places", "tags": ["custom-map", "saved-places"], "examples": [ "show me my favorite restaurants on the map", "create a visual of all places I've visited in the past year" ], "outputModes": ["application/html"] } ]}
允许客户端将内容发送到远程代理已启动新Task、回复中断的Task或重新打开已完成的Task。任务中断可能是由于代理需要额外的用户输入或运行时错误而引起的。
请求示例
{ "jsonrpc": "2.0", "id": 1, "method":"tasks/send", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "message": { "role":"user", "parts": [{ "type":"text", "text": "tell me a joke" }] }, "metadata": {} }}
响应示例
{ "jsonrpc": "2.0", "id": 1, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "completed", }, "artifacts": [{ "name":"joke", "parts": [{ "type":"text", "text":"Why did the chicken cross the road? To get to the other side!" }] }], "metadata": {} }}
客户端可以使用此方法检索为 Task 生成的 Artifacts。代理确定之前提交给它的任务的保留时段。对于超过代理保留时段的任务,或者对于生存期较短且未由代理保留的任务,代理可能会返回错误代码。
客户端还可以请求 Task 历史记录的最后 N 项,其中包括客户端和服务器按顺序发送的所有消息。默认情况下,此值为 0 (无历史记录)。
请求示例
{ "jsonrpc": "2.0", "id": 1, "method":"tasks/get", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "historyLength": 10, "metadata": {} }}
响应示例
{ "jsonrpc": "2.0", "id": 1, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "completed" }, "artifacts": [{ "parts": [{ "type":"text", "text":"Why did the chicken cross the road? To get to the other side!" }] }], "history":[ { "role": "user", "parts": [ { "type": "text", "text": "tell me a joke" } ] } ], "metadata": {} }}
客户可以选择取消之前提交的任务,如下所示
请求示例
{ "jsonrpc": "2.0", "id": 1, "method":"tasks/cancel", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "metadata": {} }}
响应示例
{ "jsonrpc": "2.0", "id": 1, "result": { "id": 1, "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "canceled" }, "metadata": {} }}
客户端可以配置推送通知URL,以接收有关任务状态更改的更新。
请求示例
{ "jsonrpc": "2.0", "id": 1, "method":"tasks/pushNotification/set", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { "schemes": ["jwt"] } } }}
响应示例
{ "jsonrpc": "2.0", "id": 1, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { "schemes": ["jwt"] } } }}
客户端可以使用此方法检索Task的当前配置的推送通知配置
请求示例
{ "jsonrpc": "2.0", "id": 1, "method":"tasks/pushNotification/get", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64" }}
响应示例
{ "jsonrpc": "2.0", "id": 1, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "pushNotificationConfig": { "url": "https://example.com/callback", "authentication": { "schemes": ["jwt"] } } }}
如果Task需要额外的用户输入,则可以暂停在远程代理执行的任务。当Task处于 input-required 状态时,客户端需要为Task提供额外的输入才能在远程代理上恢复处理。
input-required 状态中包含的Message必须包含指示客户端必须执行的动作的详细信息。例如“填写表格”或“请先登录”。如果这包括结构化数据,则应将指令作为第一个Part,将结构化数据作为第二个Part进行发送。
第一次请求示例
{ "jsonrpc": "2.0", "id": 1, "method":"tasks/send", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "message": { "role":"user", "parts": [{ "type":"text", "text": "request a new phone for me" }] }, "metadata": {} }}
第一次响应示例
{ "jsonrpc": "2.0", "id": 1, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "input-required", "message": { "parts": [{ "type":"text", "text":"Select a phone type (iPhone/Android)" }] } }, "metadata": {} }}
追问请求示例
{ "jsonrpc": "2.0", "id": 2, "method":"tasks/send", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "message": { "role":"user", "parts": [{ "type":"text", "text": "Android" }] }, "metadata": {} }}
追问响应示例
{ "jsonrpc": "2.0", "id": 2, "result": { "id": 1, "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "completed" }, "artifacts": [{ "name": "order-confirmation", "parts": [{ "type":"text", "text":"I have ordered a new Android device for you. Your request number is R12443" }], "metadata": {} }], "metadata": {} }}
对于能够通过 HTTP 与 SSE 通信的客户端和远程代理,客户端可以在创建新任务时使用 method 发送 RPC 请求。远程代理可以使用 TaskStatusUpdateEvents(用于传达状态更改或说明/请求)和 TaskArtifactUpdateEvents(用于流式传输生成的结果)流进行响应。 请注意,TaskArtifactUpdateEvents 可以将新部分附加到现有构件。客户 可用于在流式处理之外检索整个 Artifact。 代理必须在流末尾设置 final: true 属性,或者如果代理中断并需要额外的用户输入。
请求示例
{ "method":"tasks/sendSubscribe", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "message": { "role":"user", "parts": [{ "type":"text", "text": "write a long paper describing the attached pictures" },{ "type":"file", "file": { "mimeType": "image/png", "data":"<base64-encoded-content>" } }] }, "metadata": {} }}
响应示例
data: { "jsonrpc": "2.0", "id": 1, "result": { "id": 1, "status": { "state": "working", "timestamp":"2025-04-02T16:59:25.331844" }, "final": false }}data: { "jsonrpc": "2.0", "id": 1, "result": { "id": 1, "artifact": [ "parts": [ {"type":"text", "text": "<section 1...>"} ], "index": 0, "append": false, "lastChunk": false ] }}data: { "jsonrpc": "2.0", "id": 1, "result": { "id": 1, "artifact": [ "parts": [ {"type":"text", "text": "<section 2...>"} ], "index": 0, "append": true, "lastChunk": false ] }}data: { "jsonrpc": "2.0", "id": 1, "result": { "id": 1, "artifact": [ "parts": [ {"type":"text", "text": "<section 3...>"} ], "index": 0, "append": true, "lastChunk": true ] }}data: { "jsonrpc": "2.0", "id": 1, "result": { "id": 1, "status": { "state": "completed", "timestamp":"2025-04-02T16:59:35.331844" }, "final": true }}
以下是客户端与代理之间使用非文本数据的示例交互
发送任务请求
{ "jsonrpc": "2.0", "id": 9, "method":"tasks/send", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "message": { "role":"user", "parts": [{ "type":"text", "text": "Analyze the attached report and generate high level overview" },{ "type":"file", "file": { "mimeType": "application/pdf", "data":"<base64-encoded-content>" } }] }, "metadata": {} }}
发送任务响应
{ "jsonrpc": "2.0", "id": 9, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "working", "message": { "role": "agent", "parts": [{ "type":"text", "text":"analysis in progress, please wait" }], "metadata": {} } }, "metadata": {} }}
获取任务状态请求
{ "jsonrpc": "2.0", "id": 10, "method":"tasks/get", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "metadata": {} }}
获取任务状态响应
{ "jsonrpc": "2.0", "id": 9, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "completed" }, "artifacts": [{ "parts": [{ "type":"text", "text":"<generated analysis content>" }], "metadata": {} }], "metadata": {} }}
客户端或代理都可以从另一方请求结构化输出
请求示例
{ "jsonrpc": "2.0", "id": 9, "method":"tasks/send", "params": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "message": { "role":"user", "parts": [{ "type":"text", "text": "Show me a list of my open IT tickets", "metadata": { "mimeType": "application/json", "schema": { "type": "array", "items": { "type": "object", "properties": { "ticketNumber": { "type": "string" }, "description": { "type": "string" } } } } } }] }, "metadata": {} }}
响应示例
{ "jsonrpc": "2.0", "id": 9, "result": { "id": "de38c76d-d54c-436c-8b9f-4c2703648d64", "sessionId": "c295ea44-7543-4f78-b524-7a38915ad6e4", "status": { "state": "working", "message": { "role": "agent", "parts": [{ "type":"text", "text":"[{\"ticketNumber\":\"REQ12312\",\"description\":\"request for VPN access\"},{\"ticketNumber\":\"REQ23422\",\"description\":\"Add to DL - team-gcp-onboarding\"}]" }], "metadata": {} } }, "metadata": {} }}
以下是服务器在处理客户端请求时遇到错误时响应客户端的ErrorMessage格式
{ code: number; // 状态码(表示请求处理结果的数字代码) message: string; // 消息描述(服务器返回的可读信息) data?: any; // 业务数据(可选,包含请求相关的结构化数据)}
以下是服务器针对错误场景可以响应的标准JSON-RPC错误代码
简单理解:
发起一段对话就是发起一条任务,一条任务可能由多个智能体完成,与智能体的交互中包含了发送信息和回复信息,发送信息和回复信息中均可以使用部件来表示多种形态的信息,任务执行过程中会有很多状态,比如我要发起一个数据采集任务,但是执行途中发现源数据库的密码错误,就需要将任务状态置为请求输入或失败,以便告知用户最新的状态。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费场景POC验证,效果验证后签署服务协议。零风险落地应用大模型,已交付160+中大型企业
2024-10-14
2024-10-09
2024-06-20
2024-06-14
2025-02-04
2024-06-16
2024-06-14
2025-02-09
2024-05-31
2024-07-24
2025-05-12
2025-05-09
2025-04-17
2025-04-14
2025-04-10
2025-04-05
2025-03-24
2025-03-11