跳到主要内容
非官方简体中文翻译,内容版权归 Mastra 所有,遵循 Apache-2.0 许可。

A2A (Agent-to-Agent)

Mastra 支持 Agent-to-Agent (A2A) 协议的 0.3.0 版本,用于跨平台多 agent 系统。使用 A2A 可以将 Mastra agents 暴露为远程 agents,或将远程 A2A agents 作为 Mastra subagents 使用。你也可以用 JavaScript 客户端 SDK 调用 A2A 端点。

A2A 是一种开放协议,用于跨网络、框架、供应商和语言边界委派工作。远程 agent 保持其 tools、prompts、memory、workflows 和基础设施的私密性,同时暴露一个协议端点,供其他系统发现和调用。

何时使用 A2A
何时使用 A2A的直接链接

  • 父 agent 应将工作委派给专门的远程 agent。
  • 远程 agent 由另一个服务、团队、供应商或运行时拥有。
  • 后端、浏览器应用或其他 A2A 兼容系统需要以编程方式访问 Mastra agent。
  • 长时间运行的远程工作需要 task ID、状态更新、artifacts、取消、重新订阅或推送通知。

A2A 的工作原理
A2A 的工作原理的直接链接

A2A 使用 agent card 进行发现。该 card 是一个 JSON 文档,由 well-known URL 提供。它描述远程 agent,并包含接受 A2A JSON-RPC 请求的执行 URL。

当使用 Mastra Server 默认的 apiPrefix(/api)时,注册为 weather-agent 的 agent 会暴露:

  • Agent card:/api/.well-known/weather-agent/agent-card.json
  • 执行端点:/api/a2a/weather-agent

Agent card 包含 agent 名称、描述、端点 URL、provider、capabilities、安全元数据和 skills 等字段:

agent-card.json
{
"protocolVersion": "0.3.0",
"name": "Weather Agent",
"description": "Provides weather information.",
"url": "https://agent.example.com/api/a2a/weather-agent",
"version": "1.0",
"provider": {
"organization": "Acme",
"url": "https://acme.example.com"
},
"capabilities": {
"streaming": true,
"pushNotifications": true,
"stateTransitionHistory": false
},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "weather",
"name": "weather",
"description": "Gets weather conditions for a location.",
"tags": ["tool"]
}
]
}

A2A 将工作表示为消息和任务。消息携带文本、文件或结构化数据部分。

任务是有状态的工作单元,带有 ID 和生命周期状态。客户端可以跟踪长时间运行的工作并发送后续轮次。它们还可以取消工作或在断开连接后重新订阅。

协议版本
协议版本的直接链接

Mastra 在相同的 agent card 和执行 URL 上支持 A2A Protocol v0.3 和 v1.0。A2A-Version 请求头选择使用的线上协议:

  • 缺失、为空或 0.3:使用现有的 v0.3 API。
  • 1.0:使用 v1.0 API。
  • 其他任何值:返回 VersionNotSupported 协议错误。

A2AAgent 和 MastraClient.getA2A() 默认使用 v0.3。要在 v1.0 subagent 委派中使用 v1.0,请在 A2AAgent 上设置 protocolVersion: '1.0',或使用 MastraClient.getA2AV1() 进行直接的 v1.0 请求。两个 v1.0 客户端都会自动发送 A2A-Version: 1.0。直接 v1.0 客户端还增加了 tasks/list 操作。

从 @mastra/core/a2a/v1 导入 v1.0 协议类型和 codecs。现有的 @mastra/core/a2a/client 导出仍保留在 v0.3 上。

开始使用
开始使用的直接链接

A2A 在 Mastra 中有两种常见路径:

  • 使用 A2AAgent 将远程 A2A agent 作为 Mastra subagent 使用。
  • 使用 MastraClient.getA2A() 向 Mastra A2A 端点发送请求。

当另一个 Mastra agent 应将工作委派给远程 agent 时,使用 A2AAgent。当应用代码需要直接调用启用了 A2A 的 Mastra 端点时,使用客户端 SDK。

将 A2A agents 作为 subagents 使用
将 A2A agents 作为 subagents 使用的直接链接

使用 A2AAgent 包装远程 A2A agent,然后通过 supervisor agents模式将其添加到父 agent。当远程服务器托管多个 agents 或使用自定义 well-known 路径时,请传入显式的 agent card URL。当远程 agent 要求 A2A v1.0 时,设置 protocolVersion: '1.0'。省略该设置则使用 v0.3。

src/mastra/agents/support-agent.ts
import { Agent } from '@mastra/core/agent'
import { A2AAgent } from '@mastra/core/a2a'

const remoteWeatherAgent = new A2AAgent({
url: 'https://weather.example.com/api/.well-known/weather-agent/agent-card.json',
protocolVersion: '1.0',
headers: {
Authorization: `Bearer ${process.env.WEATHER_AGENT_TOKEN}`,
},
})

export const supportAgent = new Agent({
id: 'support-agent',
name: 'Support Agent',
instructions: 'Answer user questions and delegate weather questions when needed.',
model: 'openai/gpt-5.6-sol',
agents: {
remoteWeatherAgent,
},
})

如果 url 指向一个域名,A2AAgent 会从 /.well-known/agent-card.json 获取 agent card。对于遵循该发现路径的单 agent 服务器,使用域名 URL。对于多 agent 服务器,传入完整的 card URL,例如 https://agent.example.com/api/.well-known/weather-agent/agent-card.json。

在执行期间,A2AAgent 会:

  • 获取并缓存远程 agent card。
  • 从 card 中读取执行 URL 和 capabilities。
  • 在 v0.3 下调用 message/send 或 message/stream,在 v1.0 下调用 SendMessage 或 SendStreamingMessage。
  • 将远程消息、任务、artifacts 和状态更新转换为 Mastra subagent 结果。
  • 当远程任务需要后续输入或重新订阅时,支持 resumeGenerate() 和 resumeStream()。

如果远程 card 未声明支持流式传输,A2AAgent.stream() 会回退到非流式的 generate 路径,并返回缓冲的流结果。

使用客户端 SDK 发送请求
使用客户端 SDK 发送请求的直接链接

当你希望应用代码调用启用了 A2A 的 Mastra agent 时,使用 MastraClient.getA2A()。为服务器源配置 baseUrl,当服务器不使用默认 /api 前缀时配置 apiPrefix。

src/a2a-client.ts
import { MastraClient } from '@mastra/client-js'

const client = new MastraClient({
baseUrl: 'https://agent.example.com',
headers: {
Authorization: `Bearer ${process.env.AGENT_API_TOKEN}`,
},
})

const a2a = client.getA2A('weather-agent')
const card = await a2a.getAgentCard()

console.log(card.name, card.capabilities)

使用 sendMessageStream() 发送消息,并通过 Server-Sent Events (SSE) 接收任务状态和 artifact 更新:

src/a2a-client.ts
const stream = a2a.sendMessageStream({
message: {
kind: 'message',
role: 'user',
messageId: crypto.randomUUID(),
parts: [{ kind: 'text', text: "What's the weather in Prague?" }],
},
})

for await (const event of stream) {
if (event.kind === 'artifact-update') {
console.log(event.artifact.parts)
}
}

如果任务仍在运行时流断开连接,请使用 resubscribeTask() 接收进行中任务的实时更新:

src/a2a-client.ts
const updates = a2a.resubscribeTask({
id: 'task-123',
})

for await (const event of updates) {
console.log(event)
}

使用 v1.0 客户端
使用 v1.0 客户端的直接链接

使用 getA2AV1() 选择加入 A2A v1.0 线上协议。协议包提供 codecs,用于从 JSON 形式的输入创建 v1 请求值:

src/a2a-v1-client.ts
import { ListTasksRequest } from '@mastra/core/a2a/v1'
import { MastraClient } from '@mastra/client-js'

const client = new MastraClient({
baseUrl: 'https://agent.example.com',
})

const a2a = client.getA2AV1('weather-agent')
const response = await a2a.listTasks(
ListTasksRequest.fromJSON({
contextId: 'customer-support',
pageSize: 20,
}),
)

for (const task of response.tasks) {
console.log(task.id, task.status)
}

v1.0 客户端支持 getAgentCard()、sendMessage()、sendMessageStream()、getTask()、listTasks()、cancelTask() 和 resubscribeTask()。

配置 subagent 调用
配置 subagent 调用的直接链接

A2AAgent 接受用于已认证或受限环境的请求选项:

src/mastra/agents/support-agent.ts
import { A2AAgent } from '@mastra/core/a2a'

const remoteWeatherAgent = new A2AAgent({
url: 'https://weather.example.com/api/.well-known/weather-agent/agent-card.json',
headers: {
Authorization: `Bearer ${process.env.WEATHER_AGENT_TOKEN}`,
},
retries: 2,
backoffMs: 250,
maxBackoffMs: 1000,
timeoutMs: 30_000,
})

当运行时需要自定义 fetch 行为或请求取消时,你还可以传入 credentials、fetch 和 abortSignal。

Human-in-the-loop
Human-in-the-loop的直接链接

A2A 使用 input-required 任务状态来建模 human-in-the-loop (HITL) 工作。当任务因等待输入而暂停时,客户端通过发送具有相同 taskId 的后续消息来提供缺失的输入,服务器则继续执行该任务。

Mastra 在两个方向上将其 agent 挂起模型映射到该状态:

  • 作为服务器:当被暴露的 agent 挂起时,任务会转换为 input-required。这包括由 tool 审批或调用 suspend() 的 tool 引起的挂起。任务状态消息包含文本提示,以及带有结构化 suspendPayload 和 resumeSchema 的数据部分。使用相同 taskId 的后续 message/send 或 message/stream 请求会使用提供的输入恢复被挂起的运行。
  • 作为客户端:当远程任务达到 input-required 或 auth-required 时,A2AAgent 会返回一个带有 finishReason: 'suspended' 和 suspendPayload 的挂起结果。调用 resumeGenerate() 或 resumeStream() 会使用原始 taskId 将输入或凭据发送回远程任务。
src/a2a-hitl.ts
import { A2AAgent } from '@mastra/core/a2a'

const agent = new A2AAgent({
url: 'https://agent.example.com/api/.well-known/booking-agent/agent-card.json',
})

const result = await agent.generate('Book a flight to Paris', { runId: 'run-1' })

if (result.finishReason === 'suspended') {
// Inspect result.suspendPayload, collect input from a human,
// then resume the remote task.
const resumed = await agent.resumeGenerate({ approved: true }, { runId: 'run-1' })
console.log(resumed.text)
}

针对 input-required 任务的后续消息可以将恢复数据作为结构化数据部分携带,也可以作为文本部分中的 JSON 或纯文本携带。

当恢复的运行需要额外输入时,任务会回到 input-required,流程重复。恢复被挂起的运行需要在 Mastra 服务器上配置存储,以便跨请求恢复挂起的运行状态。

备注

A2A 任务记录保存在内存存储中,因此暂停的任务只能由挂起它的同一服务器进程恢复。服务器重启,或没有粘性路由的水平扩展部署,会丢失任务记录,后续消息会以 task-not-found 错误失败。

推送通知
推送通知的直接链接

Mastra 为声明了 capabilities.pushNotifications 的远程 agents 支持 A2A 推送通知。当客户端无法保持流打开,或长时间运行的任务应在原始请求结束后更新回调 URL 时,使用推送通知。

客户端获得任务 ID 后,可以为该任务注册回调 URL:

src/a2a-client.ts
await a2a.setTaskPushNotificationConfig({
taskId: 'task-123',
pushNotificationConfig: {
url: 'https://app.example.com/a2a/tasks',
token: process.env.A2A_WEBHOOK_TOKEN,
},
})

当任务达到 completed、failed、canceled、rejected、input-required 或 auth-required 时,Mastra Server 会将当前任务快照发送到已注册的回调。推送通知投递是尽力而为的。请保护回调 URL、验证通知 token,并避免将内部网络目标暴露为推送通知目的地。

推送通知配置存储在内存中,服务器重启后必须重新注册。

签名和验证 agent card
签名和验证 agent card的直接链接

Mastra 支持签名的 A2A agent card,使客户端能够验证发现的 card 来自可信发布者,并且在传输过程中未被更改。在暴露远程 agent 的 Mastra 服务器上配置签名:

src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'

export const mastra = new Mastra({
server: {
a2a: {
agentCardSigning: {
privateKey: process.env.A2A_AGENT_CARD_PRIVATE_KEY!,
protectedHeader: {
alg: 'ES256',
kid: 'agent-card-key',
},
},
},
},
})

配置签名后,Mastra 会在 agent card 上包含一个 signatures 数组。客户端验证是选择性的,未签名的 cards 仍会原样返回。

使用 MastraClient.getA2A() 验证签名 card:

src/a2a-client.ts
const card = await a2a.getAgentCard({
verifySignature: {
algorithms: ['ES256'],
keyProvider: async ({ kid, jku }) => {
return fetchTrustedPublicJwk({ kid, jku })
},
},
})

if (!card.signatures?.length) {
throw new Error('Expected a signed A2A agent card.')
}

当客户端必须在调用远程 agent 之前强制使用可信密钥时,请使用客户端签名验证。

验证 subagent card
验证 subagent card的直接链接

当父 agent 在委派工作之前应验证远程 agent 时,使用 verifyAgentCard。验证 hook 接收获取到的 agent card,以及关于获取位置和时间的上下文。

src/mastra/agents/support-agent.ts
import { A2AAgent } from '@mastra/core/a2a'

const remoteWeatherAgent = new A2AAgent({
url: 'https://weather.example.com/api/.well-known/weather-agent/agent-card.json',
verifyAgentCard: {
verify: async (card, context) => {
if (card.provider?.organization !== 'Weather Inc') {
throw new Error(`Unexpected provider for ${context.cardUrl}`)
}
},
},
})

使用此 hook 在父 agent 委派给远程 agent 之前,强制执行预期的提供方、预期端点、证书绑定的身份、签名 cards 或其他信任要求。