最近看 Agent 相关的项目,经常会同时碰到三个词,Function Calling、MCP 和 Skill。

它们看起来都和「让模型做事」有关,于是很容易混在一起。有人把 MCP 当成更高级的 Function Calling,也有人觉得有了 Skill 就不需要 MCP。

其实它们并不在同一层。

最简短的理解是,Function Calling 负责表达调用,MCP 负责接入能力,Skill 负责组织做事的方法。

一个让模型知道怎么开口,一个把工具接进来,一个告诉 Agent 事情应该怎么完成。

Function Calling 解决的是调用格式

大语言模型只能生成内容。哪怕它说自己要查询天气,也不会真的凭空发出网络请求。

应用需要先把可用函数的名称、用途和参数结构告诉模型。模型判断需要使用工具后,再返回一个结构化调用请求。宿主程序收到请求,执行真实函数,然后把结果放回对话。

一个简化后的工具定义可能长这样。

1
2
3
4
5
6
7
8
9
10
11
{
"name": "get_weather",
"description": "查询指定城市的天气",
"parameters": {
"type": "object",
"properties": {
"city": { "type": "string" }
},
"required": ["city"]
}
}

模型并没有亲自执行 get_weather。它只是在回答里表达「我想调用这个函数,参数是某个城市」。真正执行请求的,仍然是模型外面的程序。

所以 Function Calling 更像一种模型和程序之间的调用语言。它解决的是单次调用怎么表达,却没有规定工具该如何独立部署、怎样被发现,也不负责让同一套工具跨客户端复用。

工具只有两三个时,手动维护这些定义并不麻烦。等项目里出现几十个工具,问题就来了。每个应用都要保存一份 schema、路由代码和权限逻辑,工具接口一变,所有接入方还得跟着改。

MCP 处理的正是这一层麻烦。

MCP 解决的是能力接入

MCP 是 Model Context Protocol。它把工具、数据和提示模板包装成遵循统一协议的服务,让支持 MCP 的客户端能够连接、发现并使用这些能力。

一套 MCP 架构里,通常能看到三个角色。

  • Host 是承载 Agent 的应用,负责管理连接和安全边界
  • Client 位于 Host 内部,负责与某个 MCP Server 通信
  • Server 暴露具体能力,例如文件读取、数据库查询或 GitHub 操作

Server 不需要了解上面连接的是哪一种模型。它只按照协议公布自己能提供什么,并响应 Client 发来的请求。

MCP Server 暴露的内容也不只有工具。

  • Tools 用来执行操作,例如创建文件或调用接口
  • Resources 用来提供只读上下文,例如文档、日志和数据库记录
  • Prompts 用来提供可复用的提示模板

它们通过 JSON-RPC 消息交互,可以运行在本地的 stdio 连接上,也可以通过 Streamable HTTP 提供远程服务。

当 Client 连接 Server 后,会先获取能力列表,再把可调用工具转换成模型能够理解的工具定义。模型仍然产生普通的工具调用,Client 负责把请求路由到对应 Server,拿到执行结果后再交还给模型。

从模型视角看,它未必知道 MCP 存在。模型只知道眼前有一批工具可以调用。自动发现、协议转换和请求路由,都是 Host 与 Client 在外面完成的。

因此 MCP 没有取代 Function Calling。很多实现里,它反而会把 MCP 工具转换成模型原生的工具调用格式。

Function Calling 是模型如何发出调用,MCP 是外部能力怎样标准化接进来。

这一点也是 Function Calling 单独使用时最容易碰到的痛点。

如果只靠 Function Calling,每个项目都要自己维护一份工具 schema、调用路由和结果处理逻辑。A 项目接了 10 个工具,B 项目想复用,通常就要复制 schema、重写适配代码;模型供应商的工具调用格式稍有不同,还要再写一层转换。等某个工具参数变了,又得去每个项目里逐个更新。

MCP 的价值就在这里。工具提供方把能力做成独立的 MCP Server,对外统一暴露工具、资源和提示模板。支持 MCP 的 Host 连上来以后,可以自动发现这些能力,而不是每个项目从零手写一遍对接代码。

从模型视角看,它甚至不一定知道 MCP 存在。MCP Client 会把 Server 暴露的工具转换成模型能够理解的调用格式,模型仍然输出普通的工具调用请求,Client 再把请求转发给对应的 Server。换句话说,Function Calling 仍然是模型表达调用的语言,MCP 是管理和复用这些工具的生态层。

这也意味着,MCP 不是让模型凭空拥有执行能力。它需要 Host、Client、Server 和模型工具调用能力一起配合。模型负责提出结构化调用意图,宿主程序负责真正执行和回填结果。

Skill 解决的是工作方法

工具接进来以后,Agent 就一定能把事情做好吗?

不一定。

给一个 Agent 文件系统、终端和 GitHub 工具,只能说明它有能力读文件、跑命令、访问仓库。至于代码审查应该先看哪些文件、检查哪些风险、用什么格式交付,它仍然可能每次做得不一样。

Skill 处理的是这类问题。

一份 Skill 通常会包含名称、适用场景和详细执行说明,还可以附带脚本、参考资料与输出模板。它不是新的工具协议,更接近一份能够被 Agent 主动选择和执行的操作手册。

1
2
3
4
5
6
7
8
9
10
11
---
name: code-review
description: 审查代码修改,识别正确性、安全和性能问题
---

# 执行流程

1. 确认修改范围和预期行为
2. 阅读相关代码与测试
3. 运行针对性的检查
4. 按严重程度输出问题和证据

这份 Skill 没有直接提供读取文件的能力。它只规定审查应该怎样展开。真正读取文件时,Agent 仍然需要使用本地工具或 MCP Server 暴露的能力。

所以可以把两者记成一句话。

MCP 让 Agent 有工具可用,Skill 让 Agent 知道怎样把工具组合起来完成任务。

三者怎样一起工作

用一次代码审查就能把整条链路串起来。

用户要求 Agent 审查一批代码修改。Agent 先从可用 Skill 的名称和描述中,发现 code-review 与任务匹配,于是加载它的完整说明。

Skill 要求先确认修改范围。Agent 随后选择 GitHub MCP Server 提供的变更读取工具。模型通过 Function Calling 生成工具名和参数,MCP Client 把调用转发到 Server,再把返回的 diff 送回模型。

接下来,Skill 要求检查测试。Agent 又通过文件系统工具读取相关测试文件,并调用终端运行测试命令。

等证据收集完毕,Agent 按 Skill 规定的严重程度和格式输出报告。

这一次任务里,三者各自做了不同的事。

  • Function Calling 表达每一次工具调用
  • MCP 负责发现、连接和路由外部能力
  • Skill 决定完整任务的步骤、标准与交付形式

少一个都还能运行,但完成度会不一样。只有 Function Calling,工具接入容易散落在各个项目里。只有 MCP,Agent 有很多工具,却不一定有稳定的方法。只有 Skill,流程写得再清楚,没有真实工具也做不了事。

Skill 为什么要渐进式加载

Skill 还能携带脚本、模板和参考文档。如果 Agent 启动时把所有内容一次性塞进上下文,很快就会浪费大量 token,真正的任务信息反而被挤到角落。

更合理的方式是逐层加载。

Agent 启动时只看到每个 Skill 的名称和简介。任务到来后,它只加载匹配 Skill 的完整说明。执行到确实需要某份模板、脚本或参考资料时,再读取对应文件。

这有点像查手册。开始时只看目录,确定需要哪一章后再读正文,遇到具体表格时才翻到附录。不是知道得越多越好,而是在正确的时刻拿到正确的信息。

这个设计看起来只是节省上下文,实际上还在减少干扰。无关规则越少,Agent 越容易把注意力放在当前任务上。

Skill 真正解决的是「重复贴 prompt」的问题。

比如每次让 AI 做代码审查,都要重新说明检查安全漏洞、性能问题、边界条件和输出格式。一个人复制粘贴还勉强能忍,团队里每个人都手写一份标准时,质量就会开始漂移:有人漏掉安全检查,有人格式不统一,有人还在使用旧版规范。

Skill 把这些反复使用的指令、流程、脚本和模板放进一个可维护的文件夹里。一个成熟的 Skill 通常不只是几句提示词,而是有明确的结构。

1
2
3
4
5
6
7
8
code-review/
├── SKILL.md
├── scripts/
│ └── check_security.py
├── references/
│ └── review_standards.md
└── assets/
└── report_template.md

SKILL.md 是核心说明,顶部用 frontmatter 写名称和描述,正文写执行步骤。scripts 可以放自动化脚本,references 放参考资料,assets 放报告模板或其他资源。

这样 Agent 不只是拿到一句「帮我审查代码」,而是拿到一整套可复用的工作方法。需要读文件时,它可以调用 MCP 或本地文件工具;需要安全扫描时,它可以运行 Skill 自带脚本;需要交付结果时,它可以套用 Skill 里的模板。

所以「MCP + Skill」经常会一起出现:MCP 让 Agent 有手,Skill 让 Agent 有操作手册。

实际接入时是什么感觉

我自己理解 MCP,最直观的一点是:它把原来散落在项目里的工具对接,变成了「配置一个 Server,然后让客户端发现它」。

比如在 Claude Desktop 里配置文件系统或 GitHub MCP Server,通常是在配置文件里加上 Server 的启动方式和参数。客户端启动后会连接这些 Server,读取它们暴露的工具列表,然后在对话中按需调用。

开发者不需要在每个对话里手写「read_file 这个函数怎么定义」「GitHub issue 这个接口怎么传参」。这些能力由 MCP Server 标准化提供,Host 负责发现和接入,模型只看到最终可用的工具。

这就是 MCP 和普通 Function Calling 最大的区别:Function Calling 更像一次调用的格式,MCP 更像工具服务的注册、发现和复用机制。

再把边界说清楚

理解这三个概念,关键不是背定义,而是看清它们分别负责哪一段链路。

Function Calling 位于模型与宿主程序之间,让调用能够被结构化表达。

MCP 位于应用与外部能力之间,让工具和数据能够被发现、连接与复用。

Skill 位于任务与执行过程之间,把经验沉淀成可以重复使用的工作方法。

它们不是三个互相争位置的方案,而是一套 Agent 系统里可以同时存在的三层能力。

模型会不会调用,工具能不能接入,任务做得是否稳定。

这三件事,终于可以分开看了。