叶小钗

一文搞懂 Function Calling、MCP、A2A 和 Skills

关注公众号,回复666,获取AI全路径学习资料

系列文章:

  1. 《Agent 01:BrowserUse》
  2. 《Agent 02:工具链》

之前我们已经单独介绍了MCP、Fuction Calling、A2A乃至(Claude)Skills。

但是很多粉丝依旧觉得有些懵逼,我想了想原因,大概是单点知识不具备连贯性,要把他们完全搞懂,可能还是要从全局出发、从目的出发。

追本溯源,这个东西还是得从智能体(Agent)开始。因为,MCP、Fuction Calling、A2A 这三者与Agent直接相关,属于智能体与外界交互的三种方式:

Image

Fuction Calling

首先是Fuction Calling,他是一种让大模型在推理过程中,能够主动选择并调用外部函数的能力:

Image

具体的交互逻辑为:在对话中,LLM根据用户的问题(今天北京的天气如何?)判断是否需要调用函数。如果需要,它会输出一个结构化的JSON请求,其中包含了要调用的函数名和参数,然后再调用我们的程序:

Image

具体的流程,直接看GPT官方的定义即可:

# 这是给模型看的工具定义,不是真正的代码
tools = [{
"type": "function",
"name": "get_weather",
"description": "Retrieves current weather for the given location.",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and country e.g. Bogotá, Colombia"
      },
      ......
    },
  }
}]

每次调用API时都需要传递tools参数,这里直接调用GPT接口(发起请求):

# 每次调用API时都需要传递tools参数,调用GPT接口发起请求
response = client.responses.create(
    model="gpt-5",
    messages=[...],
    tools=tools  # ← 这个每次都要带
)

这里也可以看出来,模型具有哪些工具调用能力,全部是我们预定义好的,模型会根据用户输入,选择使用哪个工具:

# 用户输入
user_query = "今天北京天气怎么样?"

# 模型会分析:
# - 用户问的是"天气" → 匹配到 get_weather 的 description
# - 提到了"北京" → 对应 location 参数
# - 决定调用 get_weather 函数

只不过,模型事实上并不会直接调用工具,他只会返回结构化的调用指令,这里返回这个东西是模型专门任务训练的结果:

{
"function_call": {
"name": "get_weather",
"arguments": "{\"location\": \"北京\", \"unit\": \"celsius\"}"
  }
}

真正根据返回JSON数据调用脚本的是我们后台程序,并且这里需要将调用结果给到模型:

# 把天气结果返回给模型
# result是返回的结果
final_response = llm.chat(
  model="gpt-5",
  messages=[
    {"role": "user", "content": "今天北京天气怎么样?"},
    {"role": "assistant", "function_call": model_response["function_call"]},
    {"role": "function", "name": "get_weather", "content": json.dumps(result)}
  ]
)

逻辑很清晰,如果意图识别没有工具(函数)调用,则直接调用大模型返回给用户,如果判断有工具调用,那么就拿着首次模型返回的参数,拿到结果后二次调用模型,再将最终结果返回给用户。

这里有几个点要特别注意:如果你的工具接口挂了,容错做得不好的话,那么模型就不会回答用户了。

另外还有几个问题,理解后几乎就能了解Fuction Calling的本质,乃至为什么他用得不太频繁的原因:

第一,如果Tools工具过多(数组过多),这个也是要占用上下文长度的,所以真实使用会做很多小处理;

第二,Fuction Calling 使用好坏非常依赖于模型本身对意图识别的能力,模型判断要不要调用一个函数,主要是根据description参数来的。这里看上去风险挺大的,因为一定会出现模型调用出问题的情况,只不过模型容错能力强,多拿了数据也未必会影响回答;

在这个基础下,我们再来说MCP:

MCP

Function Calling 本身没有问题,但频繁使用后会有很多痛点,比如:

  1. 不同基座模型都有类似Function Calling的概念,但具体叫法或者参数上可能有差异;
  2. 整个Tools预定义的过程,硬编码痕迹很重,但他其实比较适合注册机制;
  3. ...;

Function Calling解决的是:单个模型怎么“按你定义的 JSON 协议”去调你自己的API的问题。

而MCP就是为了解决这些问题而生的,所以MCP的但是其实是帮Function Calling “擦屁股”的...

MCP 是由 Anthropic 提出的一种开源协议,旨在标准化LLM与外部资源和工具的连接方式。可以把它想象成 AI 世界的 “USB标准” 或 “应用商店”。

Image

只不过,这里有个非常关键的点要注意:

  1. 现在主流厂商大多都实现了各自的 Function Calling;
  2. MCP 目前主要是 Anthropic 这条线在推,是工具协议层;

换句话说就是:并不是所有模型厂商都跟进了MCP,如果基座模型没做,那么应用层就得自己套壳包装,如果不被支持的情况下,要使用MCP其实有两个前提:

  1. 第一是在模型侧需要用 Function Calling;
  2. 第二是在客户端自己把 MCP 工具转成 Function Calling 描述再喂给模型;

最直接的说法就是OpenAI就是不支持MCP,人家要自己玩,所以想用的话还得做个中间代理!

而我们会屈就的核心原因不是因为MCP本身很屌,而是社群一直在倒推基座模型厂商,现阶段由于社区活跃度,MCP的插件(Server端)已经很多了,如果我们想省事不想自己去做这个工具代码实现,那么就要做这个中间桥接层:

这里首先来个天气查询,Server端的伪代码:

start_mcp_server("weather-mcp-server"):
# 1)声明一个工具:get_weather
  register_tool(
    name = "get_weather",
    description = "查询指定城市今天的天气情况",
    input_schema = {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名称,例如:Beijing" }
      },
"required": ["city"]
    },
    output_schema = {
"type": "object",
"properties": {
"city": { "type": "string" },
"temperature": { "type": "number" },
"condition": { "type": "string" }  # 晴、多云、小雨...
        }
    }
  )
# 2)真正被调用时要做的事
  on_tool_call("get_weather", args):
      city = args.city

# 这里调用真实天气接口,或者你的内部系统
      raw = http_get("https://api.weather.com", { "q": city })

return {
"city": city,
"temperature": raw.temp,
"condition": raw.text
      }

Server端的代码我们其实是不关心的,这里写出来只是为了让大家更了解一点,我们需要关注的是Client端的配置,如果是OpenAI的话,需要将之匹配到桥阶层,有两件事要做:

  1. 调用天气 MCP Server,拉取工具定义;
  2. 把工具定义 转换成 OpenAI 的 tools(Function Calling)格式,并在模型发起 tool_call 时,代为转发给 MCP Server;

这里的代码就很简单了,几乎全部由MCP Server端完成了:

# 启动你的应用服务时:

# 1)创建 MCP Client,连接到天气 MCP Server
mcp_client = MCPClient.connect("weather-mcp-server")

# 2)从 Server 询问:你有哪些工具?
mcp_tools = mcp_client.list_tools()
# 例如返回:
# [
#   {
#     name: "get_weather",
#     description: "查询指定城市今天的天气情况",
#     input_schema: { ...上面的 JSON Schema... }
#   }
# ]

接下来就是把 MCP 工具转成 OpenAI 的 tools:

# 3)把 MCP 的工具描述转换成 OpenAI 的 tools 数组
openai_tools = []

for each tool in mcp_tools:
    openai_tools.append({
"type": "function",
"function": {
"name": tool.name,               # "get_weather"
"description": tool.description, # "查询指定城市今天的天气情况"
"parameters": tool.input_schema  # 直接复用或稍作转换
        }
    })

# 之后你在调用 OpenAI 时,就可以直接把 openai_tools 作为 tools 传入

再之后的流程跟Function Calling 很类似,我们这里就不赘述了。

A2A

到了 Agent to Agent 这里,情况马上变得简单又麻烦了,简单好像就是一次调用;麻烦的话是两个直接的通信可以很复杂,而且可控性得不到保障。

这里以之前的天气调用为例,基本的流程是模型 → 工具,到A2A这里就不这么完了,他需要模型 → 模型 → 工具。

而且在复杂场景下,这样做是有好处的,必定不同Agent擅长的方向不一样,对应着底层的提示词就不一样,单Agent的话因为任务简单也不会有Tools过多导致的问题:

Image

为了更方便的说明,我们这里再举个例子:这里有两个Agent,一个天气Agent、一个旅游Agent,而旅游Agent需要天气情况。

他的触发词可能是:五一想去成都玩三天,要不要带厚一点的衣服?

在这个场景下会出现三个角色:

  1. 天气API:最底层天气查询API;
  2. 天气MCP:天气工具MCP Server服务,他最终会调用底层天气API,也会被天气Agent调用;
  3. 天气Agent:需要时候,他会调用天气MCP获得天气情况;

这里唯一的不同是天气Agent会对外暴露一个接口,输入是 城市 + 日期范围,输出一段天气(或者旅游建议)。

换句话说,天气Agent给出去的信息是自己根据天气信息,认为对面需要的跟天气相关信息,但未必是天气本身,其实将天气Agent当大模型调用就好

而具体的调用方式就比较原始了,又要回归最初的Function Calling了,将天气Agent对外释放的API,当成旅游Agent一个Tool,只是Agent的调用对参数要求没那么高,可以传更多的上下文:

{
"city": city,
"start_date": start_date,
"end_date": end_date,
"question": "是否适合旅游?要怎么穿衣?"
}
# 天气Agent返回
{
"summary": "五一期间成都气温 15–25℃,白天舒适,早晚偏凉。",
"advice": "白天薄长袖+长裤,晚上建议加一件外套。",
"risk": "5月2日有小雨,记得带雨伞。"
}

所以读到这里大家就很清晰了,真他嘛的AI名词多,其实全部是Function Calling!!!

最后我们说下Claude Skills:

Skills

Anthropic 对Skill的定义是:一个文件夹,里面装着说明文档、脚本和资源,Claude 在执行任务时会先扫一圈所有 Skills,觉得哪个对当前任务有用,就把完整内容拉进上下文来用。

我这边翻译翻译是:

Skill = 一份可反复调用的专业SOP+说明书,由模型自己按需加载

可以把 Skill 想成:给“AI 员工”写的一本 岗位说明书 + 培训手册;不用每次在 Prompt 里“从零复述”:

Image

Skills长什么样?

每个Skill有一段非常短的描述(meta),比如:

品牌写作技能:教你如何按照 XX 公司品牌规范写公众号文章和活动文案。

Claude 在接到一个任务时,会先用这些 meta 做 “技能检索”:看看当前任务跟哪些 Skills 相关、只选命中的那几个;

最后,只有确认相关的 Skills,才会把完整的 instructions.md、例子、甚至附带脚本拉进上下文。

所以,你可以给模型装很多技能,上下文不会撑爆,他主打一个“按需加载”。

抽象一下,一个Skill看起来大概是这样(不是官方格式):

brand_style_skill/
  meta.json          # 很短的技能简介 & 什么时候用
  instructions.md    # 详细的写作规范、SOP、负面示例
  examples/          # 正反例、few-shot 样本
  scripts/           # 可选:比如检查用词的小脚本
  assets/            # 品牌色、Logo 使用规范等资源

meta.json 里的东西会被优先加载:

{
"name": "brand_style_cn",
"description": "按照 XX 品牌规范撰写中文营销文案,保持语气、结构和禁用词一致。",
"good_for": [
"公众号推文",
"电商详情页",
"活动落地页"
  ]
}

当你说:帮我写一篇双十一活动主推文案,品牌是 XX。

Claude 在内部大致会做两件事:

  1. 看当前任务内容 → 觉得“品牌写作”相关;
  2. 在所有 Skills 的 meta 里找到 brand_style_cn → 决定:加载这个 Skill 的完整说明;

之后,Skill 里的各种“套路”就会生效,比如:

  1. 标题要包含的要素;
  2. 开头 100 字要解决什么问题;
  3. 哪些词属于品牌禁用词;
  4. 有哪些可复用的模块化段落结构;
  5. ...;

Skills的定位

上述的描述太孤立,所以在说明Skill的定位前,我们先总结一下:

  1. Function Calling出现的目的是为了让模型在受控的自由下去调用第三方服务;
  2. 而后MCP是为了“偷懒”而生,核心主旨是让大家多创造插件生态(MCP Server);
  3. A2A的话反而跟前两者不是一个赛道,他属于复杂架构的拆解;

所以,A2A赛道不一样,就不参与后续讨论,我们这里回归最初的起点Function Calling,前面说过他有两个问题:

第一,Tools多了影响Token上下文,所以需要很多小策略;

第二,Function Calling的准确性很依赖于意图识别,如果调错了,会比较麻烦;

在这两点以外还有第三点:每次Function Calling的数据中途,我们都要处理工具返回数据,这其实挺烦的;

之前上述工作完全是我们自己手动处理的,但基座模型厂商可能觉得“他们也有责任”,所以Anthropic又提出了Skill概念,协助解决上述Function Calling一部分问题,比如:

Skill 主要关注数据如何用的问题,而其中按需加载的部分是值得Function Calling参考的策略

从关系上说是这么回事:

Function Calling / MCP 提供“手”,Skills 提供“干这类活的整套套路”,两者是“上下游”的关系

Skills 可以内部用 Function Calling 和 MCP,Function Calling/MCP 不知道什么叫 Skill

Skills =  业务服务 + 调用这些接口的代码 + 使用说明

为了让大家理解的更清晰,我们这里加个说明:

流程说明

在一个老板 BI Agent里配置了,一批 Tools/MCP 工具:

  1. get_sales_report
  2. get_marketing_spend
  3. get_cashflow_status

两个 Skills:

  1. boss_bi_dashboard_cn:教模型如何用上面几个工具,按老板视角解释数字、写结论,里面有详细的 KPI 口径、话术、注意事项;
  2. excel_analysis_skill:教模型如何用 code execution 对上传的 Excel 做分析;

这时候触发条件来了:帮我看看今年 Q3 的营收和投放,简单说说是不是该收一收预算?这里具体的流程是:

一、先走Skill检索

模型首先会理解,这是一个老板视角的财务分析 + 决策建议,接下来模型会遍历Skills里所有的meta,发现 boss_bi_dashboard_cn 的描述里有类似:

用于解读公司经营指标、营收、成本、预算,输出老板视角决策建议

所以它会激活这个 Skill,读取 instructions.md,把这份“说明书 + SOP”拉进当前上下文。此时,Tools/MCP 还没被调用,模型完成了:从很多工具定义 + 一堆 Skills里,选出一个最合适的技能包。

二、Skill里面决定工具调用

boss_bi_dashboard_cn 的 instructions.md 可能会写类似这样的话:

如果用户问的是某个时间段的“营收 + 投放”,
先调用 get_sales_report 拿营收数据,
再调用 get_marketing_spend 拿投放数据;
然后根据「同比 / 环比 / ROAS」给出老板能看懂的判断;
输出结论时,要用以下结构:
一句话结论
2–3 个关键数字
建议动作
风险提示

Claude 读了这份说明后,就知道下一步应该:

  1. 先查 KPI → 需要工具;
  2. 然后就是具体工具调用:
get_sales_report({"period": "2024-Q3"})
get_marketing_spend({"period": "2024-Q3"})

到这里Function Calling / MCP 出场了。

三、数据处理 + SOP

这里走Function Calling的逻辑拿到最终反馈数据即可(这里不展开):

{
"sales": 12345678,
"growth_rate": 0.12,
"gross_margin": 0.31
}

然后就是数据使用,这个instructions.md里面已经说得很清楚了:

输出结构怎么写;
哪些数字要重点强调;
语气要偏老板视角,少讲实现,多讲结论和动作;
有哪些“常见坑”要避开;
# 输出案例
结论:Q3 的营收仍在增长,但投放效率明显走低,建议适度收缩预算、把钱放到 ROAS 更高的渠道上。
......
后面是建议 + 风险......

从这里看就比较清晰了,Skills在意图识别和数据使用上做了不少工作。

结语

从最基础的 Function Calling,到旨在统一工具的 MCP,再到实现复杂协作的 A2A,乃至封装业务逻辑的 Skills,这一系列令人蛋疼的名词,其演进脉络始终清晰:它们为AI智能体更好与现实世界交互而生:

  1. Function Calling 是底层原子能力,是弥合用户无限意图与模型与现实交互的基础;
  2. MCP 做的是“工具/数据的统一协议 + 生态”,方便别人写好一个 Server,更多客户端直接复用,而不是一个个项目 copy 粘贴;
  3. A2A 不是“更高级的 Function Calling”,而是“系统怎么拆成一群 Agent,并用结构化方式彼此调用”的架构问题;
  4. Skills 也不是来抢工具活的,而是帮你沉淀“怎么用工具 + 怎么用数据”的那套 SOP;

理了一圈后,就发现这些名词其实没那么玄乎,其实依旧是模型 + 工具 + 数据 + 说明书这套组合。

Image

好了,篇幅不小了,希望这篇文章对大家有用!

点击上方卡片关注叶小钗公众号,查看下方二维码,添加我个人微信:

Image

往期推荐

《万字:详述AI编程》

《简单聊聊GEO》

《AI团队组织结构该如何设计?》

《AI学习路线图2.0》

《生产级别的RAG系统是什么样的?》

《LangChain、 Dify、 n8n、 Coze:四大AI框架怎么选?》

《个人IP的一些心得》

《万字:聊聊微调》