Max
搜索
返回故事会

从 Function Call 到 MCP、A2A、Skills 与 Subagent:Agent 能力分层全解

62 分钟阅读2Max ZhangLLMOps
Function CallingMCPA2ASkillsSubagent

上周三下午三点,我盯着屏幕上的五个术语发呆——function call、MCP、A2A、skills、subagent。每个词单独看我都认识,但它们之间的关系像一团乱麻。有人说 MCP 是 function call 的升级版,有人说 A2A 是 MCP 的竞品,还有人说 skills 就是个 fancy 的 prompt。我当时就想:卧槽,这东西到底能不能有个明白人讲清楚?

它们都和"让模型不只是聊天,而是真的去做事"有关,但它们并不是同一层概念——很多文章把它们混在一起讲,越看越乱。

这篇文章的目标很明确:把这五个概念放回它们各自出现的背景中,讲清楚它们分别解决什么问题、解决到什么程度,以及它们之间是怎么拼起来工作的。


先说结论:它们不是并列关系

如果你只想先抓住主线——先说清楚,这五个货根本不是并列关系,傻逼才把它们放在一张表里比参数——可以先记住一句话:

function call 解决"模型如何调用一个明确的外部能力"; MCP 解决"外部能力如何用统一协议接入模型系统"; A2A 解决"不同 Agent 之间如何用统一协议互相通信和协作"; skills 解决"模型如何复用一套稳定的任务经验和工作方式"; subagent 解决"一个 agent 不够时,如何把复杂任务拆给多个 agent 协作完成"。

这五者更像是不同层次:

用户任务
  ↓
主 Agent 推理与规划
  ↓
如果任务复杂,需要分工
  → subagent(单系统内拆分)
  → A2A(跨系统 Agent 协作)
  ↓
执行时需要经验模板和工作方式
  → skills
  ↓
真正和外部世界交互
  → function call / tools
  ↓
而这些外部能力的接入方式
  → 可以通过 MCP 统一提供

换句话说:

  • function call 更像"调用动作"
  • MCP 更像"工具接入协议"
  • A2A 更像"Agent 间通信协议"
  • skills 更像"可复用的操作知识包"
  • subagent 更像"任务组织方式"

理解了这个分层,后面很多概念就不会串线了。


一、为什么光靠聊天式 LLM 不够

最早的大模型应用,基本都是这种模式:

用户输入问题
  ↓
模型根据上下文生成文本
  ↓
返回答案

这个阶段的模型很强,但它有三个天然限制:

1. 它知道很多,但碰不到外部世界

比如你问:

  • "帮我查一下今天订单总数"
  • "帮我把这条工单指派给后端组"
  • "把这个 PR 拉下来并跑一下测试"

模型本身并不能直接访问数据库、调用内部 API、执行 shell 命令。它最多只能"告诉你理论上怎么做"。

2. 它能生成步骤,但步骤不一定真的执行

比如用户说:

帮我分析这次线上故障,顺便看看最近 24 小时的错误日志。

纯聊天模型可以一本正经地写出排查流程:

  1. 查看日志
  2. 检查报警
  3. 分析错误类型
  4. 给出结论

问题在于:它没真的去看日志。 他妈的,它能生成排查流程,但生成完就完了——这跟给你一份菜谱然后说"我已经帮你做完饭了"有什么区别?

3. 它能临场发挥,但不擅长长期稳定重复执行

假设你要一个 AI 代码助手稳定遵守团队约定:

  • 先读代码再改
  • 优先用 rg
  • 修改文件必须走 patch
  • 先跑最小验证
  • review 时先报问题,再给总结

如果每次都靠 prompt 临时讲,这套行为不稳定,很容易漂。

于是,AI 工程逐渐开始分层演进。接下来这些概念,都是在这个过程中长出来的。


二、Function Call:先解决"怎么让模型动手"

2.1 它出现的背景

最早大家做"会干活"的 AI,通常是这样干的:

  1. 让模型输出一段 JSON
  2. 后端自己解析 JSON
  3. 判断里面是不是要调用某个 API
  4. 调完 API 再把结果喂回模型

例如:

{
  "action": "get_weather",
  "city": "Shanghai"
}

这个方法能用,但问题很明显:

  • 输出格式不稳定
  • 字段名经常漂
  • 参数类型容易错
  • 很难做标准化约束
  • 不同模型之间兼容性差

所以,function calltool calling 出现了。它本质上是在说:

不要让模型随便生成"像工具调用的文本",而是让模型在受约束的结构里选择要调用哪个函数,并给出符合 schema 的参数。

2.2 它解决什么问题

function call 解决的是:

1. 让模型能以结构化方式请求外部操作

例如给模型声明一个函数:

getWeather(city: string, date?: string)

当用户说:

帮我查一下上海明天的天气

模型不再返回一段模糊文本,而是可以返回类似:

{
  "name": "getWeather",
  "arguments": {
    "city": "Shanghai",
    "date": "2025-04-15"
  }
}

2. 把"推理"和"执行"拆开

模型负责判断:

  • 该不该调用工具
  • 应该调用哪个工具
  • 参数是什么

宿主程序负责:

  • 真正执行函数
  • 校验参数
  • 权限控制
  • 重试、超时、审计

这很关键。模型不是直接执行者,它只是提出调用请求。

3. 让 Agent 循环成为可能

典型循环是:

用户问题
  ↓
模型决定调用函数
  ↓
宿主程序执行函数
  ↓
把结果返回给模型
  ↓
模型继续推理
  ↓
给出最终答案,或继续调用下一个函数

没有 function call,Agent 很难进入稳定的"思考 -> 调工具 -> 看结果 -> 再思考"闭环。

2.3 它不解决什么问题

很多人第一次学到 function call,会误以为这就已经是完整 Agent 方案了——我当时也是,觉得卧槽牛逼,模型能调工具了这不就齐活了?其实不是。

它不解决:

  • 工具如何发现和注册
  • 工具来自本地还是远程
  • 不同系统如何共享同一套工具
  • 复杂任务的长期规划
  • 团队经验如何复用
  • 多个 agent 如何分工
  • 不同 agent 之间如何通信

所以 function call 很重要,但它只是第一层。

2.4 一个最小例子

比如你在做一个运维助手,有两个函数:

getServiceStatus(serviceName: string)
restartService(serviceName: string)

用户说:

帮我看一下 payment-service 挂了没,如果挂了就重启。

可能的执行过程是:

1. 模型先调用 getServiceStatus("payment-service")
2. 系统返回:status = unhealthy
3. 模型再调用 restartService("payment-service")
4. 系统执行重启
5. 模型总结结果返回给用户

这里 function call 解决的是"模型如何发起这两次动作"。


三、MCP:再解决"工具怎么统一接进来"

3.1 它出现的背景

当 function call 普及之后,新的问题来了:

函数定义写在哪里?工具怎么发现?不同客户端怎么共享?

假设你有这些能力:

  • 文件系统访问
  • Git 操作
  • 数据库查询
  • 浏览器自动化
  • 公司内部工单 API
  • 文档搜索

最原始的做法是:每个 AI 客户端都自己手写一遍工具定义和对接代码。

于是你会遇到这些问题:

  • A 应用接了数据库工具,B 应用还得重写一次
  • 本地 IDE 助手和 Web Agent 用的是两套工具封装
  • 工具描述文档不一致
  • 权限模型各做各的
  • 换个宿主程序,工具层要重做

这时候就需要一个统一的"工具接入协议"。MCP 就是在这样的背景下出现的。

3.2 MCP 到底是什么

你可以把 MCP 理解成:

模型宿主和外部能力提供方之间的一层标准协议。

它关心的是:

  • 外部系统如何把能力暴露出来
  • 宿主如何发现这些能力
  • 工具的输入输出如何描述
  • 资源、提示、工具等对象如何被统一读取

如果说 function call 解决的是"模型如何说出我要调用某个工具",那 MCP 解决的是"这个工具本身如何以标准化方式提供给模型系统"。

3.3 它解决什么问题

1. 工具接入标准化

以前你要在每个 agent 框架里重复写:

  • 工具名
  • 参数 schema
  • 执行逻辑
  • 描述文案

而 MCP 的思路是:由 MCP Server 统一暴露能力,MCP Client 统一消费能力。

比如一个 Git MCP Server 可以提供:

  • git_status
  • git_diff
  • git_log
  • create_branch

那不同宿主只要会说 MCP,就都能接这批能力。

2. 让工具和模型宿主解耦

这件事非常重要。

以前工具和应用常常是强绑定的:

某 IDE 插件
  ↔ 写死的工具实现

用了 MCP 之后,更像这样:

Agent 宿主
  ↔ MCP Client
  ↔ MCP Server
  ↔ 外部系统/能力

这样有几个直接收益:

  • 工具可以独立演进
  • 多个宿主可复用同一个 server
  • 企业内能力更容易标准化封装
  • 权限和审计可以集中治理

3. 让"资源"与"工具"一起被管理

很多人只盯着 tool,其实 MCP 还不止是工具调用。

在真实系统里,模型要接触的往往不只是"一个可执行函数",还有:

  • 文件
  • 文档
  • 数据表
  • 提示模板
  • 运行上下文

MCP 的价值之一,就是给这些对象也提供更统一的接入方式,而不是所有东西都伪装成函数。

3.4 MCP 的演进:从 v1.0 到 v2.0

理解 MCP 的版本演进,有助于理解它到底在解决什么层次的问题。

阶段一:MCP 诞生(2024-11-05)

最初的 MCP 协议很简单,定义了两种传输方式:

  • stdio:客户端把 MCP Server 作为子进程启动,通过标准输入/输出通信。适合本地工具。
  • HTTP + SSE:服务器通过 HTTP POST 接收请求,通过 Server-Sent Events 推送响应和通知。适合远程工具。

这个版本的 MCP 解决了"工具可以跨宿主复用"的问题,很快就获得了广泛采用。但它在生产环境中暴露了几个问题:

  • HTTP+SSE 对防火墙不友好:很多企业防火墙和代理对持续的长连接流不兼容
  • 连接管理复杂:需要同时维护 POST 端点和 SSE 端点,状态管理脆弱
  • 缺乏标准认证:远程 MCP Server 的访问控制完全靠实现者自己设计
  • 没有工具元数据:host 无法区分一个工具是只读的还是破坏性的,导致权限控制只能一刀切

阶段二:MCP 2.0(2025-03-26)

2025 年 3 月发布的 MCP 规范修订版(常被社区称为"MCP 2.0")是一次重大更新,主要引入了四个关键能力:

① Streamable HTTP 替代 HTTP+SSE

这是最核心的变化。Streamable HTTP 用单一的 HTTP 端点同时支持 POST(客户端到服务器)和 GET(服务器到客户端推送),不再需要维护两套端点。它就像一个"双向 HTTP 通道"——你可以把它想象成一根管道,双方都能往里面扔消息,但走的都是标准的 HTTP 请求,这意味着:

  • 任何支持 HTTP 的防火墙和代理都能正常工作
  • 连接可以优雅地建立和恢复
  • 支持会话管理(通过 Mcp-Session-Id header)
  • 断线后可以通过 Last-Event-ID 恢复流,不会丢消息

这个改变让 MCP 从"适合本地和开发环境"真正变成了"适合生产环境远程部署"的协议。

② OAuth 2.1 授权框架

有了标准化的认证之后,企业 MCP Server 可以有统一的权限体系,不再每个 server 自创一套。MCP Client 在连接时可以完成标准的 OAuth 授权流程,获取访问令牌。

③ 工具注释(Tool Annotations)

每个工具可以携带元数据,声明自己的属性:

  • readOnlyHint:是否只读
  • destructiveHint:是否具有破坏性
  • idempotentHint:是否幂等
  • openWorldHint:是否访问开放网络

这样一来,host 在做权限控制时就不再是"要么全开要么全关",而可以基于工具的具体属性做细粒度审批。例如:只读工具可以自动放行,破坏性工具需要用户确认,访问外部网络的工具需要额外审查。

④ JSON-RPC 批处理

客户端可以一次 HTTP 请求携带多个 JSON-RPC 调用,减少网络往返。对于需要密集调用工具的 agent 场景,批处理能显著降低延迟。

演进的内在逻辑

回过头来看,MCP 的演进线其实非常清晰:

MCP v1.0 (2024-11)
  - stdio: 解决"本地工具复用"
  - HTTP+SSE: 解决"远程工具接入"
  → 核心问题:工具能跨宿主共享了

MCP v2.0 (2025-03)
  - Streamable HTTP: 让远程接入真正生产化
  - OAuth 2.1: 让权限管理标准化
  - Tool Annotations: 让工具属性可描述
  - JSON-RPC Batching: 让调用更高效
  → 核心问题:工具共享不仅要"能通",还要"可管、可控、可维护"

所以 MCP 不是一个静态的协议,而是在不断回答越来越具体的问题:"工具能共享了吗?→ 共享上生产了吗?→ 生产上安全可控了吗?"

3.5 它不解决什么问题

MCP 也很容易被神化。它不负责:

  • 替你做任务规划
  • 替你决定何时调用什么工具
  • 替你写好高质量 prompt
  • 替你把复杂任务拆给多个 agent
  • 负责不同 Agent 之间的通信(那是 A2A 的事)

MCP 更像"标准化基础设施",不是"自动变聪明"。

3.6 一个直观类比

你可以这样理解:

  • function call 像"拨打一个电话号码"
  • MCP 像"建立统一电话网络和通讯协议"
  • MCP 从 v1.0 到 v2.0,就像电话网络从"能打通"进化到"有来电显示、有通话加密、有套餐管理"

只有电话号码,没有通信协议,系统没法大规模互通。 只有通信协议,没有人去拨号,也不会自动解决业务问题。

3.7 一个例子:企业内部助手

假设公司内部有三套系统:

  • Jira
  • Confluence
  • 内部发布平台

以前每个 AI 产品都要分别对接这三套 API。

现在团队可以做三个 MCP Server:

  • jira-mcp
  • confluence-mcp
  • deploy-mcp

然后让不同宿主去接:

  • IDE 里的编码助手
  • Slack 里的问答助手
  • Web 后台里的运维助手

这样它们都能共享同一套企业能力,而不是每个产品重复造轮子。

更妙的是,用上 MCP 2.0 之后:

  • deploy-mcp 的工具可以标上 destructiveHint: true,这样助手在调用发布操作前会自动请求用户确认
  • 所有 MCP Server 统一走 OAuth 认证,权限管理员只需要在一个地方配置
  • 即使助手在海外办公室,通过标准 HTTP 也能正常连接到内网的 Streamable HTTP 端点

四、Skills:解决"模型会不会做"而不只是"能不能做"

4.1 它出现的背景

当工具接入越来越丰富后,大家很快发现一个新问题:

有工具,不等于会用工具。 能调函数,不等于能稳定完成任务。

比如同样是"修一个 bug",两个 agent 的表现可能天差地别:

  • 一个上来先改代码,结果改错模块
  • 一个先读日志、查调用链、定位测试,再做最小修复

区别不在于它们有没有 shell、有没有 grep、有没有 git,而在于它们有没有稳定的工作方法

这就是 skills 要解决的问题。

4.2 Skills 到底是什么:不止是 prompt,更是一种"上下文重载"

skills 可以理解为:

针对某类任务沉淀出来的可复用操作经验包。

这个"经验包"可能包括:

  • 任务目标定义
  • 适用场景
  • 操作步骤
  • 约束规则
  • 示例
  • 推荐工具
  • 常见坑

但如果只理解到这里,你可能会觉得 skill 就是一个"写得比较详细的 prompt"。这种理解没有错,但它缺失了一个关键洞察。

Skills 的本质是一种"上下文重载(Context Override)"。

什么意思呢?LLM 在回复用户时,它的行为模式很大程度上是由"上下文窗口里的内容"决定的。正常情况下,它的上下文是:

系统提示词 + 用户消息 + 历史对话 + 工具定义

当 Agent 激活一个 skill 时,等于在上下文窗口里注入了一套完整的"专业认知框架"——不仅是"你要怎么做",还包括"你现在是谁"、"你应该优先考虑什么"、"你有哪些陷阱不能踩"、"你的输出应该长什么样"。

这套框架实际上"重载"了模型的默认行为方式,就像面向对象编程中,子类的方法重载(override)了父类的默认实现:

默认 LLM 行为(父类):
  - 通用聊天模式
  - 遇到编程问题,随便给个方案
  - 输出格式自由

加载"代码修复" skill 后(子类重载):
  - 先读代码,再动手
  - 最小改动优先
  - 必须有验证步骤
  - 输出带文件位置和 diff

从这个角度,skill 和 RAG 有一个重要的区别:

  • RAG 注入的是"知识"——它告诉模型"这个事实是什么"
  • Skill 注入的是"方法"——它告诉模型"你现在应该怎么想、怎么做"

举个更具体的例子。假设你在做一个 code review 的 skill,它包含:

# Code Review Skill

## 身份
你现在是一个只关注代码质量的审查者。你不需要讲客气话,
也不需要评价代码风格以外的东西。

## 审查优先级
1. 逻辑错误和边界条件
2. 安全漏洞
3. 性能问题
4. 可维护性

## 输出格式
- 先按严重性列出问题
- 每个问题带文件路径和行号
- 没有问题就明确说"未发现问题"
- 不要在前面放"变更摘要"

当 agent 加载这个 skill 时,发生了几件事:

  1. 身份覆盖:从"通用助手"切换为"代码审查者"
  2. 优先级重塑:从"什么都可能提"聚焦到"按严重性排查"
  3. 输出约束:从"自由格式"锁定为"结构化的审查报告"
  4. 行为边界:明确"没问题就说没问题",避免"非要找出点什么"

这些都不是外部工具能做到的——工具只提供能力,不提供使用能力的方法论。skill 的价值,就是把这套方法论固化成上下文里的"行为程序"。

4.3 Skill 的上下文重载为什么重要

这一层的重要性经常被低估,但它解决了一个非常本质的问题:

LLM 是通用的,但任务是需要专业性的。

一个通用 LLM 的知识广度极大(读过人类历史上几乎所有的文本),但正因为它"什么都懂一点",它在具体任务上很容易表现出"什么都懂一点,但不够深"或者"知道怎么做但执行时不够稳"。

Skills 的上下文重载机制,就是在不改变模型参数的前提下,让模型在特定任务的上下文中表现出专家级的执行一致性

这种一致性来自于 skill 在上下文中建立的"软约束系统":

  • 先做什么,后做什么 → 防止步骤跳跃
  • 什么能做,什么不能做 → 防止越界
  • 遇到问题怎么处理 → 防止卡住
  • 结果应该长什么样 → 防止输出漂移

这就是为什么一个好的 skill 文件往往比一个"同样意思的长 prompt"效果好得多——prompt 只是"说了一句",而 skill 是在上下文中系统性地"搭建了一个工作环境"。

4.4 它解决什么问题

1. 让高频任务的执行风格稳定下来

例如上面提到的代码评审 skill。每次做 code review,agent 的行为都不会飘得太厉害。

2. 降低复杂 prompt 的重复成本

如果每次都在对话里临时写一大段:

你现在是一个资深架构师,请先这样,再那样,遇到什么情况如何处理……

这很难维护,也不利于团队共享。

skill 的价值就在于把这套东西固化下来,变成可复用资产。

3. 把"领域知识"和"操作流程"绑定起来

举个例子,你做的是"生成 OpenAI API 使用建议"的 skill,它可以明确包含:

  • 优先查官方文档
  • 避免引用过时参数
  • 推荐模型时区分延迟、成本、能力
  • 给代码示例时优先官方 SDK 形式

这样它就不是一段抽象 prompt,而是有明确边界的专业工作流。

4.5 它不解决什么问题

skill 不直接解决:

  • 外部系统怎么接入
  • 工具实际怎么执行
  • 多 agent 之间如何调度
  • 跨系统 Agent 之间如何通信

所以 skill 不是 tool,也不是 protocol,更不是 scheduler。

4.6 一个例子:写博客 skill

假设你有一个"技术写作" skill,里面写着:

适用场景:
- 写工程实践文章
- 写原理解释文章

要求:
- 先讲问题背景,再讲方案
- 不只给定义,要讲为什么需要
- 必须给具体例子
- 避免把概念堆成术语表
- 默认输出中文

这时候,agent 在接到"写一篇讲解 MCP 的文章"时,会更像一个稳定的作者,而不是临场发挥的聊天机器人。


五、A2A:再解决"Agent 之间怎么互相通信"

5.1 它出现的背景

前面的 function call、MCP 和 skills 解决了一个核心问题:

单个 Agent 怎么使用工具、接入能力、稳定执行。

但当 Agent 系统越来越成熟,一个新的问题浮出水面:

不同 Agent 之间怎么协作?

想象一个企业的真实场景:

  • 一个 Agent 专门负责代码审查(部署在公司 GitLab 旁边)
  • 一个 Agent 专门负责工单管理(对接 Jira)
  • 一个 Agent 专门负责文档检索(对接 Confluence)
  • 一个 Agent 专门负责部署协调(对接 CI/CD)

这些 Agent 可能是不同团队、用不同框架、跑在不同服务器上构建的。现在用户提了一个需求:"帮我排查这个线上的 500 错误,看看最近的代码变更、相关工单和部署记录。"

单靠一个 Agent 很难完成——它没有 Jira 的权限,也不了解部署流程。你当然可以用 MCP 把所有工具接进来,但问题是:

  • 你不想把所有敏感系统的访问权限都开给一个 Agent。安全上,让每个 Agent 只接触自己的领域更可控。
  • 各团队的工具实现细节不应该被外部 Agent 直接调用。比如代码审查 Agent 可能内部使用了复杂的 AST 分析工具链,开发团队不希望外部直接调用这些底层工具。
  • Agent 之间需要的不是"工具调用",而是"任务委托"。你说"帮我查一下这个 bug 关联的工单",这不是调用某个工具函数,而是把一个子任务委托给另一个 Agent 去思考、检索、总结。

这就是 A2A(Agent-to-Agent 协议)要解决的问题。

5.2 A2A 到底是什么

A2A(Agent-to-Agent Protocol)是由 Google 提出,后来贡献给 Linux Foundation 的开源协议。它的定义非常清晰:

一个开放协议,用于让不同框架、不同厂商、不同服务器上运行的 AI Agent 之间能够互相通信、发现能力、协作完成任务——而不需要暴露各自的内部状态。

如果说 MCP 解决的是 Agent 向下接入工具的问题,那 A2A 解决的就是 Agent 横向协作的问题。

5.3 几个核心概念

A2A 协议围绕几个关键设计展开:

1. Agent Card(Agent 名片)

每个 A2A Server 都暴露一张 Agent Card——一个 JSON 格式的能力声明,描述:

  • 这个 Agent 是谁(名称、描述、提供商)
  • 它能做什么(skills 列表,不是工具列表)
  • 怎么连接它(URL、支持的传输协议)
  • 认证要求(OAuth、API Key 等)

这有点像 DNS 之于互联网,或者 package.json 之于 npm 生态——它是一个标准化的"自描述文档",让其他 Agent 能够发现和理解它。

{
  "name": "Code Review Agent",
  "description": "Reviews pull requests for code quality and security issues",
  "url": "https://code-review.internal.example.com",
  "skills": [
    { "id": "code_review", "name": "Code Review", "description": "..." },
    { "id": "security_scan", "name": "Security Scan", "description": "..." }
  ],
  "defaultInputModes": ["text", "file"],
  "defaultOutputModes": ["text", "file"]
}

注意,它描述的是"skills"(能完成什么任务),而不是"tools"(有哪些函数)。这是刻意的设计——A2A 不关心对方内部怎么实现,只关心对方能交付什么结果。

2. Task(任务)

A2A 的核心工作单元不是"函数调用",而是"任务"。一个 Task 有自己的生命周期:

pending → working → input-required → working → completed
                  → failed
                  → canceled

这很重要,因为它意味着 A2A 天然支持长运行任务多轮交互。一个 Agent 可以给另一个 Agent 派一个任务,过一段时间再来查看结果。甚至在任务进行中,被委托方可以反过来请求更多信息(input-required 状态),形成多轮对话。

3. 支持流式和推送

A2A 支持三种交互模式:

  • 同步 request/response:简单的问答
  • 流式 SSE:实时的进度更新(比如"代码审查已完成 60%")
  • 异步推送通知:任务完成后通过 webhook 通知调用方

4. 不透明执行(Opaque Execution)

A2A 的一个核心设计原则是:Agent 之间协作时,不需要暴露内部状态。对方的 prompt、工具链、推理过程都是黑箱——你只需要知道"它能做什么"和"它产出了什么"。

这解决了企业级场景中的一个关键安全问题:你不需要让外部 Agent 看到你内部的工具实现、数据库 schema 或业务逻辑。

5.4 A2A 和 MCP 的关系

这两个协议经常被放在一起比较。关键在于理解它们的方向不同

        Agent ↔ Agent
            ↑
           A2A
            ↑
┌─────────────────────────┐
│         Agent           │
│  ┌───────────────────┐  │
│  │   LLM + Prompt    │  │
│  │   + Skills        │  │
│  └───────────────────┘  │
│           ↓             │
│      Function Call      │
│           ↓             │
│         MCP             │
│           ↓             │
│   Tools / Resources     │
└─────────────────────────┘
  • MCP 是纵向的:Agent 向下调用工具、访问资源、读取数据。它回答的是"这个 Agent 怎么接入外部能力"。
  • A2A 是横向的:Agent 之间互相委托任务、交换信息。它回答的是"这些 Agent 怎么组成一个协作网络"。

MCP 和 A2A 不是竞争关系,而是互补关系。MCP 给了 Agent "手",让它能干具体的活;A2A 给了 Agent "嘴和耳朵",让它们能互相商量着把活分掉。

一个经典的比喻:

  • MCP = 公司内部的工具标准(所有员工用同一套办公软件、同一套流程系统)
  • A2A = 公司之间的合作标准(不同公司之间怎么发订单、签合同、交付成果)

5.5 它不解决什么问题

A2A 不解决:

  • 单个 Agent 内部的任务规划
  • 工具的具体调用和执行
  • 单系统内的多 Agent 编排(那是 subagent 的事)
  • Agent 该用什么方法论做任务(那是 skills 的事)

A2A 只解决一件事:让 Agent 在网络层面能够发现彼此、安全通信、协作完成任务

5.6 一个例子:跨系统故障排查

回到开头的场景。用户说:

帮我排查这个线上 500 错误。

有了 A2A,系统可以这样工作:

主 Agent(用户对话界面)
  ↓ 通过 A2A 委托
  ├──→ 代码审查 Agent:查最近 24 小时的相关代码变更
  ├──→ 工单管理 Agent:查这个 bug 关联的 Jira 工单
  └──→ 部署协调 Agent:查最近的部署记录和环境变更

每个被委托的 Agent 独立运行:
  - 用 MCP 接入自己的专业工具
  - 用 function call 执行具体操作
  - 用 skills 保证工作方法稳定

最后各 Agent 通过 A2A 把结果传回主 Agent,主 Agent 汇总给用户。

关键是:每个 Agent 只暴露自己"能做什么"(通过 Agent Card),但不暴露"怎么做的"(内部工具和逻辑)。安全、可控、可审计。


六、Subagent:解决"一个 agent 忙不过来"

6.1 它出现的背景

当任务再复杂一点,单个 agent 即使会用工具、也有 skill,还是会遇到瓶颈。

比如这种任务:

帮我给这个仓库做一次全面改造: 先理解现有认证流程,再修复 3 个接口 bug,补测试,最后写迁移说明。

如果全靠一个 agent 顺着做,问题会很快暴露:

  • 上下文太长
  • 多条工作线互相打架
  • 一个任务没做完,另一个信息又来了
  • 局部细节挤占主线注意力

于是就有了 subagent 的需求。

6.2 Subagent 是什么

你可以把 subagent 理解为:

主 agent 为了完成复杂任务,派出去处理某个子问题的辅助 agent。

它不是普通函数,也不是普通工具。它更像是:

  • 一个独立的推理单元
  • 一个有自己上下文窗口的执行者
  • 一个能自己读代码、调用工具、产出结果的"小代理"

6.3 Subagent 和 A2A 的区别

这是一个很容易混淆的点。

  • Subagent单系统内部的任务拆分机制。它发生在同一个 Agent 框架内,由同一个宿主程序调度,共享同一套工具注册和上下文管理。subagent 之间不需要协议——宿主直接管理它们的生命周期。
  • A2A跨系统之间的协作协议。它连接的是独立部署、独立运行的 Agent,可能用不同框架、不同语言、跑在不同服务器上。它们通过标准化的协议通信,互相之间是"黑箱"。

类比一下:

  • Subagent 像是一个团队内部的分工(经理把任务分给组员,大家共享工具和上下文)
  • A2A 像是跨公司的合作(你给外部供应商下单,对方在自己的系统里完成,你不需要知道他们怎么做的)

两者可以组合使用:主 Agent 可以通过 A2A 委托任务给外部的专业 Agent,而那个外部的 Agent 内部可能再用 subagent 来拆分工作。

6.4 它解决什么问题

1. 任务拆分

主 agent 不必亲自做所有事情,可以把一部分工作分出去。

例如:

  • 主 agent 负责总体规划
  • subagent A 调研认证模块
  • subagent B 修复前端表单 bug
  • subagent C 跑测试并整理失败项

2. 并行化

有些事情天然可以并行:

  • 读不同模块代码
  • 查询不同文档
  • 修改不冲突的文件
  • 做独立验证

subagent 可以显著减少总耗时。

3. 隔离上下文

这是最容易被低估的一点。

单 agent 很容易被海量细节污染上下文。subagent 能把局部问题封装掉,只把结果带回来。

比如主 agent 不需要看到测试日志的全部细节,它只需要知道:

  • 哪些测试失败
  • 失败原因是什么
  • 需要不需要阻塞交付

6.5 它不解决什么问题

subagent 不是银弹。

它不能自动解决:

  • 工具接入标准化
  • 单个工具的 schema 设计
  • 任务方法论沉淀
  • 跨系统 Agent 之间的通信

换句话说,subagent 解决的是"组织问题",不是"能力定义问题"。

6.6 一个例子:代码库改造

主 agent 接到任务:

给支付系统加幂等保护,并补齐测试。

它可以这样拆:

主 Agent:
- 读整体架构
- 设计改造策略

Subagent A:
- 只看 API handler 和 service 层
- 找出幂等缺失点

Subagent B:
- 只看测试目录
- 评估现有测试缺口

Subagent C:
- 独立跑测试
- 汇总失败结果

最后主 agent 汇总这些结果,再决定下一步改哪些文件。


七、它们之间到底是什么关系

讲到这里,可以把它们串起来了。说实话,当我把这五个概念在纸上画出这五层的时候,我整个人是什么鬼——原来它们根本不是在同一个平面上竞争,而是像网络协议栈一样层层堆叠。

7.1 第一层:function call 是最基础的执行接口

它回答的是:

模型想调用外部能力时,怎么以结构化方式表达出来?

没有这层,模型只能"说自己会做",不能稳定请求执行。

7.2 第二层:MCP 是工具接入标准

它回答的是:

这些外部能力,如何统一暴露给模型系统?

function call 可以调用一个工具,但 MCP 让"工具生态"更容易被接入、发现和复用。从 v1.0 到 v2.0 的演进,让这个协议从"能通"走向了"可管可控"。

7.3 第三层:skills 是任务经验层

它回答的是:

即使有了工具,模型该按什么方法去完成某类任务?

skill 让 agent 不只是"有工具可用",而是"更像一个会干活的人"。通过上下文重载机制,skill 在模型的认知空间里搭建起一个专业的工作环境。

7.4 第四层:A2A 是 Agent 间协作协议

它回答的是:

不同 Agent 之间如何互相发现、安全通信、委托任务?

A2A 让 Agent 生态从"单打独斗"走向"网络化协作"。它不是替代 MCP 或 skills,而是在更高一层提供 Agent 间的"共同语言"。

7.5 第五层:subagent 是任务组织层

它回答的是:

当一个 agent 处理不过来时,任务怎么拆、怎么并行、怎么协作?

subagent 在单个系统内部组织任务,而 A2A 在跨系统层面组织协作。两者都解决"分工"问题,但作用域不同。

这五层的关系可以概括为:

用户任务
  ↓
规划层:subagent(拆任务)、A2A(跨 Agent 委托)
  ↓
经验层:skills(提供做事方法)
  ↓
执行层:function call(发起具体动作)
  ↓
接入层:MCP(标准化工具和资源的暴露与发现)

八、把它们放进一个完整场景里看

假设你要做一个"企业级研发助手",支持这些能力:

  • 看代码
  • 查文档
  • 调工单
  • 跑测试
  • 生成变更说明
  • 调用外部的专业 Agent(如安全审计 Agent)

这时五者可以这样协作。

8.1 MCP 提供接入层

你可能会有这些 MCP Server:

  • filesystem-mcp
  • git-mcp
  • jira-mcp
  • ci-mcp
  • docs-mcp

它们负责把外部能力标准化暴露出来。如果用的是 MCP 2.0,这些 server 可以:

  • 通过 Streamable HTTP 部署在生产环境
  • 用 OAuth 统一认证
  • 工具带上 annotations 让 agent 框架做细粒度权限控制

8.2 Function call 负责具体调用

主 agent 在推理过程中决定:

  • 先调用 search_code
  • 再调用 get_ticket
  • 然后调用 run_tests

这里每一次动作,本质上都是 function call。

8.3 Skills 提供工作方法

例如一个"代码修复" skill 规定:

  • 先读相关文件
  • 不要直接大改
  • 先做最小修复
  • 修改后必须验证
  • 如果测试跑不了,要明确说明

这个 skill 加载后,通过上下文重载,让 agent 从"能改代码的工具"变成了"会修 bug 的工程师"。它不是在给 agent 增加能力,而是在重新配置 agent 使用能力的方式。

8.4 A2A 负责调用外部专业 Agent

主 agent 发现这次修复涉及安全问题,需要额外的安全审计。但安全审计不是它自己的技能——公司有一个独立的"安全审计 Agent",由安全团队维护,跑在另一台服务器上。

主 agent 通过 A2A 协议:

  1. 发现安全审计 Agent 的 Agent Card(通过 URL 或注册中心)
  2. 理解它的能力:security_audit skill
  3. 发送任务:"请审计以下代码变更的安全性"
  4. 安全审计 Agent 在自己的环境中执行,用 MCP 接入自己的安全扫描工具
  5. 异步或流式返回审计结果

整个过程,主 agent 不需要知道安全审计 Agent 用了哪些工具、跑的什么模型、内部逻辑是什么——它只需要知道"它能做安全审计"和"结果是什么"。

8.5 Subagent 负责内部分工

对于在自己的 MCP 工具集和 skill 范围内能做的事情,主 agent 用 subagent 拆分:

  • subagent A 去分析认证流程
  • subagent B 去检查测试失败
  • subagent C 去收集工单背景

8.6 这个系统的全链路大概长这样

用户说:修复支付回调重复入账的问题,进行安全审计,并写出修复说明

主 Agent:
1. 根据 skill 决定先读代码、找上下文
2. 通过 MCP 接入的代码工具执行搜索
3. 用 function call 调用具体工具
4. 发现任务复杂,派出 subagent 去并行分析测试和调用链
5. 通过 A2A 委托安全审计 Agent 进行独立的安全审查
6. 汇总所有结果后决定修改方案
7. 修改代码、跑验证、生成说明

这时候你会发现:

  • 没有 MCP,工具接入会很散
  • 没有 function call,工具调用不稳定
  • 没有 A2A,无法安全地委托外部专业 Agent
  • 没有 skills,执行风格会漂
  • 没有 subagent,大任务效率低、上下文容易爆

九、最容易混淆的几个点

下面这几个坑我挨个摔过,傻逼如我当年,每一个都踩进去了,你大概率也会遇到。

9.1 "MCP 是不是 function call 的替代品?"

不是。

更准确地说,MCP 和 function call 关注点不同:

  • function call 关心"调用动作如何表达"
  • MCP 关心"能力如何被标准接入"

很多基于 MCP 暴露出来的工具,最后依然是通过 function call 这类机制被模型选中和调用。

9.2 "A2A 是不是 MCP 的竞品?"

这是最常见的误解。实际上它们解决的是不同方向的问题:

  • MCP:Agent → 工具/资源(纵向)
  • A2A:Agent ↔ Agent(横向)

两者不是二选一的关系,而是经常一起用。一个 Agent 可能同时是 MCP Client(连接各种工具)和 A2A Server(对外暴露自己的技能给其他 Agent 调用)。

9.3 "Skill 是不是 prompt 的别名?"

也不是。这是最容易踩的坑。

prompt 是"一段提示文本",而 skill 是"一套完整的任务执行系统"。区别在于:

  • prompt 是一次性的、临时的
  • skill 是结构化的、可复用的
  • prompt 主要影响"单次回复"
  • skill 通过上下文重载系统性地改变模型的执行模式

简单说:prompt 是"提醒",skill 是"培训"。

你可以把一段 prompt 塞进对话里说"请像资深架构师一样思考",也可以加载一个包含目标、步骤、约束和陷阱提示的 skill。后者的效果,通常远好于前者——因为它不是在模型原有的思维框架上"贴提示",而是直接在上下文中"换了一个思维框架"。

9.4 "Subagent 是不是 A2A 的一种实现?"

不是。

  • Subagent 是单系统内部的机制,通常由宿主框架直接管理,不走协议
  • A2A 是跨系统的开放标准,用于连接独立部署的 Agent

可以这样理解:subagent 是同一个进程中创建的辅助 Agent(共享工具注册、内存空间),而 A2A 连接的是不同进程、不同服务器、不同组织中的 Agent。

9.5 "只要有很多工具,Agent 就会变强吗?"

不一定,甚至经常会变差。

工具越多,越容易出现:

  • 选择困难
  • 错用工具
  • 上下文污染
  • 权限风险

所以成熟系统通常会同时做四件事:

  • 用 MCP 管理工具接入
  • 用 skill 约束任务方法
  • 用 A2A 安全委托外部专业能力
  • 用 subagent 管理复杂度

十、几个具体例子

10.1 例子一:客服助手查订单

用户说:

帮我查一下订单 A1024 为什么还没发货。

系统可能这样工作:

  1. 模型根据 schema 发起 getOrder(orderId) function call
  2. 工具来自公司内部订单 MCP Server
  3. 返回结果显示库存锁定异常
  4. 模型继续调用 getInventoryStatus(sku)
  5. 最终总结成客服可读答案

这里:

  • function call 负责"怎么调用"
  • MCP 负责"订单和库存工具怎么接进来"

如果团队还有一个"客服沟通" skill,它还会约束输出风格:

  • 先给结论
  • 再给原因
  • 不暴露内部字段名

10.2 例子二:写代码并修测试

用户说:

给这个接口加缓存,顺便修一下挂掉的测试。

系统可能这样工作:

  1. 主 agent 使用"代码修改" skill,先读代码再动手
  2. 通过代码搜索工具查依赖关系
  3. 任务变复杂,于是起一个 subagent 去专门分析测试失败
  4. 另一个 subagent 去看缓存层已有实现
  5. 主 agent 汇总后修改代码
  6. 再调用测试工具验证

这里 function call、MCP、skills、subagent 四者全出现。如果还需要安全审计,主 agent 还会通过 A2A 委托给独立的安全审计 Agent。

10.3 例子三:研究型 Agent

用户说:

帮我比较三种向量数据库的适用场景,并给出选型建议。

如果系统支持联网搜索和 A2A:

  1. 主 agent 先派 3 个 subagent,分别调研不同数据库
  2. 每个 subagent 调用搜索、打开网页、提取信息等工具
  3. 这些工具可能都通过某个 MCP Server 提供
  4. 如果主 agent 发现某个数据库的性能数据需要专门的基准测试,可以通过 A2A 委托给一个"基准测试 Agent"
  5. 最终主 agent 汇总,并按一个"技术选型分析" skill 输出结果

这时候五者的价值都体现出来了:subagent 拆解并行工作,A2A 委托外部专业能力,skills 约束输出质量,MCP 提供工具支持,function call 执行具体操作。


十一、工程上应该怎么设计这五层

如果你要自己做 Agent 系统,我建议按下面的思路分层设计。

11.1 把 function call 当成执行入口,不要让模型直接碰业务实现

也就是说:

  • 模型只负责"请求调用"
  • 真正执行一定放在宿主程序
  • 参数必须校验
  • 权限必须收口
  • 超时、幂等、审计必须由系统保证

11.2 把 MCP 当成能力接入总线

凡是可能被多个宿主复用的外部能力,都适合往 MCP 方向收敛,比如:

  • 文件系统
  • Git
  • 文档库
  • 知识库
  • 企业 API

建议尽量使用支持 Streamable HTTP 的 MCP 2.0 server,原因很简单:

  • 你的 MCP Server 迟早需要支持远程访问(团队不一定都在本机跑)
  • Tool annotations 让你能做更精细的权限控制
  • OAuth 标准化减少了安全性方面的重复投入

这样未来无论换模型、换 IDE、换 Agent 框架,工具层都更稳。

11.3 当需要跨 Agent 协作时,引入 A2A

如果你的系统需要:

  • 调用其他团队维护的 Agent(安全审计、合规检查、数据分析等)
  • 让多个独立 Agent 协作完成一个复杂任务
  • 保护各 Agent 内部的实现细节和敏感权限

那么引入 A2A 是很自然的选择。重要的设计决策:

  • 什么通过 A2A,什么通过 MCP:如果你的 Agent 只需要"调用某个系统的函数"——用 MCP。如果你的 Agent 需要"委托另一个 Agent 去思考和决策"——用 A2A。
  • Agent Card 的粒度:不要像 MCP 一样暴露工具函数列表。A2A 的 Agent Card 应该描述"skills"(能力),而不是"tools"(函数)。描述"能做安全审计",而不是描述"有 sqlmap、nmap、OWASP ZAP"。

11.4 把 skills 当成团队的"行为资产",而不只是提示词模板

不要把 skill 只当"提示词模板"。更好的做法是把它们做成:

  • 有名称
  • 有适用范围
  • 有步骤说明
  • 有示例
  • 有约束
  • 有引用文件

理解 skill 的本质是"上下文重载",能帮助你在设计 skill 时更有方向:你不是在写一段更好的 prompt,而是在搭建一个专业的工作环境。问问自己:

  • 这个 skill 应该覆盖模型的哪些默认行为?
  • 它的执行顺序够不够明确?
  • 边界条件处理清楚了没有?
  • 遇到异常状况时,skill 留下的空间够不够 agent 自主判断?

好的 skill 设计,是在"约束(决定怎么做)"和"自由(留出推理空间)"之间拿捏分寸——太松则退化回普通 prompt,太紧则变成死板的脚本。

11.5 把 subagent 用在真正值得拆分的任务上

不是所有任务都要多 agent。

适合上 subagent 的场景通常有:

  • 多模块并行探索
  • 多来源信息调研
  • 写 scope 明确且互不冲突
  • 独立验证任务

不适合的场景则是:

  • 任务太小
  • 下一步强依赖当前结果
  • 子问题边界模糊
  • 协作成本高于收益

十二、一个更实用的判断方法

以后再碰到新框架、新产品、新术语,你可以直接问五个问题:

1. 它是在解决"调用"问题,还是"接入"问题?

  • 调用问题:偏 function call
  • 接入问题:偏 MCP

2. 它是在解决"会不会做",还是"能不能做"?

  • 能不能做:偏工具、function call、MCP
  • 会不会做:偏 skills

3. 它是在解决"单点能力",还是"Agent 协作"?

  • 单点能力:偏 tool / function call
  • Agent 协作:偏 A2A(跨系统)或 subagent(单系统内)

4. 它是在增加自由度,还是在增加稳定性?

  • MCP 和 function call 多半是在增加能力接入和执行自由度
  • skills 多半是在增加任务执行稳定性——通过上下文重载,将"自由度"收束到专业的行为轨道上
  • A2A 和 subagent 多半是在增加系统的规模处理能力

5. 它的作用范围是系统内部还是跨系统?

  • 系统内部:function call、MCP、skills、subagent
  • 跨系统:A2A

这样你就不容易被术语带跑偏。


十三、最后总结

妈的,回头看这五个概念,逆天的地方就在于——它们各自只解决一层问题,但拼在一起的时候,整个系统就活过来了。

把这五个概念压缩成最短版本,就是:

  • function call:让模型能结构化地请求调用外部能力
  • MCP:让外部能力能被标准化地接入模型系统(从 v1.0 的工具共享发展到 v2.0 的生产级管控)
  • A2A:让不同 Agent 之间能用统一协议互相发现、通信和协作——不暴露内部实现
  • skills:让模型在某类任务上有更稳定的做事方法——通过上下文重载,在模型的认知空间里建立专业的工作框架
  • subagent:让复杂任务可以在单个系统内被拆分、并行和协作完成

它们不是互斥关系,而是典型的分层协作关系。

一个成熟的 Agent 系统,往往会同时具备这五层能力:

  1. MCP 接入工具生态
  2. function call 发起具体动作
  3. skills 约束做事方法
  4. subagent 组织单系统内的复杂任务
  5. A2A 连接外部的专业 Agent

如果你把它们都混成"AI 能调用工具"这一句话,后面做系统设计时一定会乱。

真正靠谱的理解方式,是把它们看成:

  • 有的解决"执行接口"
  • 有的解决"能力接入"
  • 有的解决"经验沉淀"
  • 有的解决"Agent 互操作"
  • 有的解决"协作组织"

而每一层都在不断演进:function call 在变得更细粒度(structured output、parallel calls),MCP 在从"能接入"走向"安全可控地接入",A2A 在从"Google 的提案"走向 Linux Foundation 的开放标准,skills 在从"复杂 prompt"走向"上下文工程",subagent 在从"一个辅助函数"走向"自主协作单元"。

当这五层拼起来,LLM 才会从"会聊天"慢慢进化成"能稳定干活的 Agent 网络"。

读者来信

0/1000

暂无来信,期待你的分享。