深入解析Function Calling、MCP和Ski
你想象一下这个画面——
北方冬天的早上,你缩着脖子站在公交站台,想查查今天到底有多冷。你掏出手机解锁,打开天气页面,选城市、点日期、把年份一页页往前翻…最后点查询。一套操作下来,手指头冻僵了!
你看,如果是大模型调用天气查询工具呢?你只需要说一句:「北京今天天气怎么样?」
啪,搞定。
这就是AI Agent调用工具的体验——把那些又笨重又繁琐的操作藏起来,你动动嘴就完事。
但说到这儿,我想问你一个问题:那些整天说在做Agent的人,真的搞清楚了Function Calling、MCP、Skills这三兄弟的关系吗?
说实话,不少人在吹牛的时候,连地基都没打稳。
Function Calling:就是那块地基,你别看不起
我当年做Lynxe框架的第一版demo,连MCP是什么都不知道,就是最原始的ReAct循环。凭什么撑起来的?
就靠一个东西——Function Calling。
它解决了一个最根本的问题:大模型只会吐文字啊!它怎么去调数据库?怎么去查API?
好,Function Calling说:行,我给你一套“黑话”。模型收到你的问题,根据系统给的工具描述,生成一个结构化的JSON——里面写清楚要调用哪个函数、参数是什么。宿主程序接住这个JSON,执行代码,把结果塞回给模型。
你看,简单到令人发指。
我见过太多把Function Calling吹上天的文章,实际它的工作模式就这么朴素。但恰恰是这种朴素,让它成了踩不烂的地基。
可一旦开始做真实产品,问题就来了——
你要做个Agent,可能要对接GitHub API、操作本地数据库、调用企业内部的ERP系统。每个工具接口格式都不一样,每个都得写单独的适配代码。GitHub那套REST API是一套认证,MySQL是另一套,第三方SaaS又来一套。一个项目里光写工具适配器就上千行。
你以为这就完了?
更痛苦的是——把这些工具扔给模型之后,调用成功率跟开盲盒一样。同个功能,写工具描述的人不同,模型理解的程度就千差万别。
MCP:标准化它的野心
所以MCP的出现,说白了就是在解决一个社区痛点:能不能别让每个人都在重复造“工具接入”这个轮子?
Anthropic在面临OpenAI的竞争压力时,干了一件特别聪明的事——把MCP捐给了Linux基金会。
你想啊,这一捐,微软、AWS这些大厂就没啥站队顾虑了。MCP从一个商业公司的协议,变成了行业标准。这步棋,绝。
我实际测试MCP跑了一整个周末。说下真实感受——
MCP Server把工具暴露出来,Client连上后自动发现工具列表,然后按Function Calling的格式塞给模型。这个“自动发现”的机制,确实爽。你再也用不着手动维护几十行几百行的tool schema了,MCP Server声明了什么工具,Client自动接进来。
但是!
MCP远没有到“万能”的程度。
我用Lynxe框架对接了17个MCP Server之后,发现一个特别尴尬的问题:不同Server返回的数据模型冲突了。两个不同的MCP Server都暴露了一个叫“search”的工具,一个用来搜百度百科,一个搜内部文档。模型经常搞混,要么选错工具,要么两个一起跑,结果互相对不上号。
你发现问题了没?
MCP只解决了工具接入的标准化问题,但没有解决工具调用的“知识”问题。
模型拿到一堆工具,但它不知道什么时候该用什么工具,更不知道怎么组合使用才能完成一个复杂任务。
这就好比给你一套顶级的厨具,但你连菜谱都没有。锅再好,也只能干瞪眼。
Skills:把你经验变成工具的知识包
这就是Skills存在的意义。
我在Lynxe里花时间最多的模块是什么?不是Function Calling的JSON解析器,也不是MCP的Server接入层。是Skills的管理系统。
为什么?
因为真正让Agent变聪明的,不是它能调用多少个MCP工具,而是它知道怎么用这些工具完成一个任务。
Skills的想法特别朴素:你把完成某类任务的步骤、规则、最佳实践写成一份结构化文档,放到一个特定目录下。Agent启动时扫描这个目录,当用户任务来了,Agent自己判断哪个Skill相关,然后加载这个Skill的指令作为上下文。
我仔细翻过Nanobot的开源代码,它的Skills结构和Anthropic的目录结构像倒——
.claude/skills/
├── pdf-parsing/
│ ├── script.py
│ └── SKILL.md
├── python-code-style/
│ ├── REFERENCE.md
│ └── SKILL.md每个Skill必须有一个SKILL.md文件,用Markdown写清楚:这个Skill是什么、什么场景触发、执行步骤是什么、需要调用哪些工具。
我举个自己测试过的例子——在Lynxe里我写了一个“代码审查”的Skill:
name: code-review
description: 对代码变更进行逐行审查,识别潜在问题并给出改进建议里面的指令我写得非常细:先拉取diff,然后从测试覆盖率、性能、安全、代码风格四个维度逐行分析,最终输出结构化审查报告。
你知道区别在哪吗?
没有这个Skill时,模型也能做代码审查,但它的“审查”就是扫一眼说“看起来不错”或者“有个地方可以优化”。有了Skill后,审查质量完全就是两个量级。
MCP和Skills:它们到底是互补还是竞合?
我直接说吧——竞合的关系大于互补。
为什么?
因为它们都在解决同一个问题:怎么让模型更好地使用工具。
只不过MCP走的是标准化和服务化的路子,Skills走的是知识化和流程化的路子。
MCP的思路是:“我把工具接入标准化了,你自己看着用。”
Skills的思路是:“不光给你工具,我还教你怎么用。”
如果一个MCP Server把工具描述写得无比详细、把用法都文档化了,那它就是在抢占“Skills”的生态位。反过来,如果一个Skills里不仅写了指令,还包装了具体的工具调用脚本,那它就是在吞食“MCP”的地盘。
现在很多文章喜欢说“MCP和Skills是互补的”。我觉得这是把问题想简单了。
拿我自己的项目说——我让Lynxe既支持MCP也支持Skills。但实际用下来跑得最多的,不是那些复杂的MCP Server组合,而是几个精心维护的Skills。
为什么?
因为对大多数任务来说,用户要的不是“更多工具”,而是“更聪明的做法”。
Function Calling才是真正的底层核心
写到这里,我想说一个可能会得罪人的结论——
在这三者里面,MCP和Skills某种程度上都是在“粉饰”Function Calling的不完美。
本质上,模型需要知道的就三样东西:有哪些函数可以用,每个函数需要什么参数,函数的预期输出是什么。
Function Calling已经把这三件事定义清楚了。MCP只是把“函数的发现方式”标准化了。Skills则是把“怎么用函数”知识化了。
但不管加多少层封装,最后落到模型面前的,还是那个tool_calls的JSON。
我在Lynxe的源码里加了一层叫“Skill Dispatcher”的东西,做的事其实很简单:根据用户任务,从Skills库里匹配最相关的Skill,然后把这个Skill里定义的MCP工具映射成Function Calling能理解的格式,再塞给模型。
你看,转了一圈,最后还是Function Calling在干活。
一些实操建议,拿走不谢
如果你刚入门Agent开发,我给你四个建议:
第一,别一上来就搭MCP。
先用手写的Function Calling跑通整个ReAct循环。我当年就是这么干的,三轮迭代之后,我才知道自己真正需要什么样的工具抽象。地基没打稳,别急着盖楼。
第二,MCP Server不是越多越好。
我用Lynxe测试过——10个以内的MCP Server,模型调用的准确率还能维持在85%左右。超过20个,准确率就明显往下掉,有时会低于60%。模型面对太多选择时,决策质量会下降。这不是模型的问题,是人性的问题。
第三,把时间花在写好SKILL.md上。
别整天折腾MCP Server的部署。一个精心编写的Skill,比你挂在10个MCP Server上的100个工具都有用。
第四,功能调用层一定要兼容多种模式。
我Lynxe的做法是:用Function Calling做底层通信,MCP做工具扩展,Skills做上层知识管理。三者各安其位,各有各的用处。
我的判断,不一定对,但很真
Function Calling会一直存在。它是AI Agent调用外部世界的基础。
MCP会成为企业的标准配置,但不是个人开发者的必需品。它解决的是组织层面的标准化问题,而不是个人层面的效率问题。
Skills会在未来一年内变得更重要。
为什么?
因为当工具越来越多时,稀缺的不是“能调用什么”,而是“知道怎么用”。Skills就是这种“知道怎么用”的封装。
你应该看得出来,我站Skills。
不是因为Skills是Anthropic推的。是因为我在它上面花的时间最多,看到的效果也最明显。Lynxe从第一版到现在,最大的变化不是在MCP对接上做了什么优化,而是在知识管理和技能封装上做了三次重构。
你得明白一个道理——
你用不着把所有工具都塞给模型。
你只需要让模型知道:面对这个任务,该怎么开展,先做什么后做什么,调用哪些工具,拿到结果后怎么判断。
这才是Agent真正需要的能力。
好的Agent不是工具箱,而是有经验的老师傅——不是把整个世界的工具背在身上,而是知道面对一个活儿,该怎么干。
读者评论 5