上一篇讨论了 Agent 之间如何交接工作。

一个 Agent 完成任务后,可以通过 Message 沟通过程信息,通过 Task 表达正在处理的工作,再通过 Artifact 交付正式结果。

解决了“传什么”,本文继续讨论 Agent 和外部能力之间,以及 Agent 之间,具体应以什么方式协作。

比如,一个 Agent 要查询数据库,可以直接调用 API,也可以通过 MCP 使用数据库工具;如果它无法独立完成任务,还可能把工作交给另一个专业 Agent。

这几种情况看起来都是“调用”,实际面对的问题并不相同。

一、Agent 系统的三类交互方式

先看 Agent 系统中最常见的三种情况。

第一种是直接调用已有服务:

代码片段Text
Agent
  |
  | REST / RPC
  v
业务服务

例如客服 Agent 查询订单:

代码片段Text
GET /orders/{id}

如果服务地址固定、接口明确,直接使用 REST 或 RPC 已经足够。

第二种是 Agent 使用外部工具:

代码片段Text
Agent
  |
  | MCP
  v
MCP Server
  |
  v
数据库 / 搜索 / 文件 / 业务系统

这里 Agent 关心的是:

代码片段Text
当前有哪些工具
每个工具有什么用途
需要哪些参数
会返回什么结果

这属于 MCP 主要解决的范围。

第三种是两个独立 Agent 之间协作:

代码片段Text
Agent A
   |
   | A2A
   v
Agent B

Agent A 面对的已经不是一个简单工具,而是另一个可以独立完成任务的 Agent。

它可能有自己的模型、Prompt、工具和运行状态,甚至由另一个团队开发和部署。

此时需要了解的是:

代码片段Text
对方具备哪些能力
怎样提交任务
任务当前是什么状态
最终能够得到什么结果

这属于 A2A 主要解决的范围。

A2A(Agent2Agent Protocol)是由 Google 发起、现由 Agentic AI Foundation 推进的开放标准,用于让不同厂商、框架和语言实现的 AI Agent 能够发现彼此能力、交换消息、委派和跟踪任务,并交付结构化结果,从而实现跨系统的 Agent 协作与互操作。

因此,REST/RPC、MCP 和 A2A 面向的是不同场景,不需要互相替代。

二、MCP 的工具接入机制

假设我们正在开发一个客服 Agent,它需要:

代码片段Text
查询客户资料
查询订单
搜索知识库
创建工单
读取退款记录

传统做法通常是分别调用各个系统的 API,再在 Agent 中维护相应的函数和参数定义。

调用工具较少时,这种方式没有问题。

随着工具越来越多,会变得越来越不易维护。

每个工具怎样描述自己的用途?参数格式放在哪里?不同 Agent 是否都要重复开发适配代码?模型怎样知道当前有哪些工具可用?

MCP 为这些问题提供了一套统一方式。

外部系统可以通过 MCP Server 暴露能力,Agent 一侧通过 MCP Client 获取并使用这些能力。

其调用流程如下:

代码片段Text
Agent
  |
MCP Client
  |
  | MCP
  v
MCP Server
  |
  +-- Tool A
  +-- Tool B
  +-- Tool C

其中最值得关注的是 Tool。

一个 Tool 通常需要说明:

代码片段Text
工具名称
工具用途
输入参数
返回结果

例如,一个查询发票的工具可以描述为:

代码片段JSON
{
  "name": "get_invoice",
  "description": "根据发票编号查询发票信息",
  "inputSchema": {
    "type": "object",
    "properties": {
      "invoice_id": {
        "type": "string"
      }
    },
    "required": ["invoice_id"]
  }
}

这里的 name 是工具名称,description 说明用途,inputSchema 定义参数结构。

Agent 获取这些信息后,就能知道当前有哪些工具,以及调用时应该提供什么参数。

因此,MCP 能够让 Agent 以统一方式认识和使用外部能力。

至于 Tool 背后调用 PostgreSQL、REST API,还是企业内部服务,并不是 Agent 必须了解的内容。

三、A2A 的 Agent 协作机制

假设客服 Agent 收到用户的问题:

我的退款已经申请五天了,为什么还没有到账?

Customer Agent 可以处理普通客服问题,但退款调查由 Billing Agent 负责。

于是任务会转交给 Billing Agent:

代码片段Text
Customer Agent
      |
      | A2A
      v
Billing Agent

Customer Agent 不需要知道 Billing Agent 使用什么模型,也不需要了解它内部有哪些 Prompt 和工具。

它更关心:

代码片段Text
Billing Agent 是否能处理退款问题
任务应该怎样提交
当前处理到哪一步
结束后会返回什么

A2A 主要解决的正是这类 Agent 之间的协作问题。

其中有几个核心概念:

代码片段Text
Agent Card
Task
Message
Artifact

它们分别解决能力描述、任务状态、过程沟通和结果交付等问题。

四、Agent Card 的能力描述

A2A 使用 Agent Card 描述 Agent 对外提供的信息和能力。

可以把它理解为 Agent 的服务说明。

例如:

代码片段JSON
{
  "name": "Billing Resolution Agent",
  "description": "处理账单和退款相关问题",
  "skills": [
    {
      "name": "refund_investigation"
    },
    {
      "name": "invoice_query"
    }
  ]
}

其中,skills 表示这个 Agent 对外提供的能力。

真实的 Agent Card 还可以包含访问地址、支持的交互方式和安全相关信息。

这样,系统面对一个新任务时,可以先确认:

代码片段Text
有哪些 Agent
各自擅长什么
哪个 Agent 能处理当前任务

确定目标之后,再进入具体的任务交互。

五、Task 的任务状态管理

在 Agent 内部,Tool 调用通常比较直接:

代码片段Text
get_invoice("INV-1001")
        ↓
返回发票信息

但 Agent 接到的往往是一项需要持续处理的工作。

例如:

代码片段Text
调查这笔退款为什么迟迟没有到账,
检查支付记录、退款状态和客服历史,
最后给出处理结果。

Billing Agent 可能需要连续访问多个系统,中途还可能等待新的信息。

因此,A2A 使用 Task 表示一项正在处理的任务。

一个 Task 可能经历类似这样的状态变化:

代码片段Text
submitted
    |
    v
working
    |
    v
completed

有些任务还可能进入等待输入、失败或取消等状态。

这里体现了 Agent 与普通函数调用的一个明显区别。

函数通常关注:

代码片段Text
请求
  ↓
结果

Agent 协作还需要知道:

代码片段Text
任务是否已经开始
当前进行到哪一步
是否需要额外信息
最终是否完成

对于耗时较长的任务,系统还可以通过 Streaming、订阅状态变化或通知机制持续获得进展,而不必一直等待一个同步请求返回。

六、Artifact 的结果形态

任务完成之后,还需要交付结果。

如果只是简单回复:

代码片段Text
退款正在处理中

Message 已经足够。

但 Agent 的正式产出可能是一份报告、一段结构化数据,甚至一个文件。

例如 Billing Agent 最终生成:

代码片段Text
Refund Investigation Report

- 退款申请时间
- 支付渠道状态
- 当前退款状态
- 异常原因
- 建议处理方式

这类可以继续被其他系统使用的正式结果,可以通过 Artifact 表达。

因此,上述几个概念的职责如下:

代码片段Text
Agent Card
描述“我具备什么能力”

Task
表示“当前正在处理什么工作”

Message
承载任务过程中的沟通

Artifact
保存任务产生的正式结果

这也与上一篇中介绍的 Agent 交付内容完全一致。

上一篇关注 Agent 之间交接什么,这里进一步关注这些内容怎样在独立 Agent 之间流转。

七、MCP 与 A2A 的组合结构

MCP 和 A2A 并不冲突。

它们很可能同时出现在一个系统中。

继续使用前面的客服场景:

代码片段Text
Customer Agent

      |
      | A2A
      v

Billing Resolution Agent

      |
      +---- MCP ----> CRM
      |
      +---- MCP ----> Invoice Database
      |
      +---- MCP ----> Ticket System

用户询问退款问题后,Customer Agent 判断自己无法完成完整调查,于是通过 A2A 把任务交给 Billing Resolution Agent。

Customer Agent 关心:

代码片段Text
Billing Agent 擅长什么
怎样提交 Task
当前处理到哪一步
最终返回什么 Artifact

Billing Agent 接到任务后,需要查询客户资料、发票和客服工单。

这时它通过 MCP 使用对应工具:

代码片段Text
get_customer()
get_invoice()
search_tickets()

Billing Agent 关心:

代码片段Text
有哪些 Tool
每个 Tool 需要哪些参数
调用后返回什么数据

所以,同一个系统中,两种协议承担的是不同职责:

代码片段Text
Agent
  |
  | A2A
  | Agent 之间的任务协作
  v
Agent
  |
  | MCP
  | Agent 对外部能力的使用
  v
Tool / Data / API

八、不同交互方式的适用边界

实际工程中,我们应根据不同场景选择不同的调用方式。

场景适合方式主要原因
同一程序中的固定逻辑函数 / 模块调用简单直接,没有网络交互
已知的内部业务服务REST / RPC地址和接口稳定
Agent 使用搜索、数据库、文件MCP工具可以统一描述和使用
独立 Agent 之间的任务委托A2A需要能力描述和任务状态
跨团队的 Agent 协作A2A双方无需了解内部实现
Agent 内部使用多个外部工具MCP工具可以独立接入和复用

如果当前对象提供一个明确、清晰的能力,例如:

代码片段Text
查询订单
搜索文档
创建工单
读取数据库

它应该以 Tool 的方式实现。

如果当前对象能够:

代码片段Text
接收完整任务
自行组织执行过程
调用多个工具
维护任务状态
返回完整结果

它应以 Agent 形式实现。

总结

Agent 系统中常见三种交互方式:直接调用 API、通过 MCP 使用工具,以及通过 A2A 与另一个 Agent 协作。

MCP 更关注工具和资源接入。它让 Agent 知道有哪些能力、需要哪些参数,以及调用后能够得到什么结果。

A2A 面向独立 Agent 之间的协作。Agent Card 描述能力,Task 表示正在处理的工作,Message 负责过程沟通,Artifact 承载正式结果。

在完整的 Multi-Agent 系统中,两者往往同时存在:

代码片段Text
Agent A
   |
  A2A
   |
Agent B
   |
  MCP
   |
Tools / Data / APIs

因此,系统设计中首先需要分清的是:当前对象是供 Agent 使用的一项能力,还是一个能够独立接收并完成任务的 Agent。边界确定后,就能够确定适合的交互方式。