三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

大模型应用开发:Function Call、Agent Skills与MCP协议的核心区别与实践指南

大模型应用开发:Function Call、Agent Skills与MCP协议的核心区别与实践指南

1. 从一次面试对话聊起:为什么这三个概念让人困惑?

前几天和一位在阿里做技术面试官的朋友聊天,他提到最近在面试大模型应用方向的候选人时,发现一个挺有意思的现象:当问到“Agent Skills、MCP和Function Call有什么区别”时,很多有经验的开发者也会卡壳,要么混为一谈,要么解释得云里雾里。这其实不怪大家,因为这三个概念都围绕着“如何让AI模型与外部世界互动”这个核心命题,在技术演进和不同厂商的语境下,边界确实容易模糊。

我自己在构建和部署智能体(Agent)的实践中,也深刻体会到理清这三者的关系至关重要。这不仅仅是应付面试的知识点,更是架构设计时的基石选择。选错了技术路径,轻则让智能体变得笨拙低效,重则导致整个系统难以维护和扩展。简单来说,你可以把Function Call看作是AI模型“伸手”去调用一个工具的基础能力,就像人的手;Agent Skills则是这只手经过训练后掌握的“一套熟练动作”,比如熟练地使用螺丝刀或钢笔;而MCP(Model Context Protocol)则是为这只手和这些动作建立的一套标准化“工具库管理规范”和“操作手册”,确保不同的手(模型)都能安全、高效地使用库里的任何工具。

下面,我就结合具体的实践场景和代码示例,把这层窗户纸彻底捅破,让你不仅知道它们是什么,更能理解在什么情况下该用谁,以及背后的设计哲学。

2. 核心概念拆解:各自解决什么问题?

在深入对比之前,我们必须先给每个概念下一个清晰、无歧义的定义。很多混淆都源于对基本术语的理解偏差。

2.1 Function Call:大模型与外部世界的“基础握手协议”

Function Call,通常被称为“函数调用”或“工具调用”,是大模型原生支持的一种核心机制。它的本质是让模型能够根据对话上下文,结构化地输出一个调用外部函数或工具的请求,而不是仅仅生成一段自然语言。

它解决了什么问题?在没有Function Call之前,如果我们想让AI帮我们查天气,对话可能是这样的:

  • 用户:“今天北京天气怎么样?”
  • AI:“今天北京晴转多云,气温15-25度,风力2-3级。”

这段回复看起来没问题,但它是AI“臆想”出来的,或者是从其训练数据中回忆的,并非实时数据。AI本身并没有真正去“查询”。Function Call的出现,让流程变成了:

  1. 用户:“今天北京天气怎么样?”
  2. AI(模型):识别出用户意图是查询天气,且需要地点(北京)和时间(今天)。于是,它不再直接生成答案,而是输出一个结构化的调用请求,例如:
    { "name": "get_current_weather", "arguments": { "location": "Beijing", "date": "2023-10-27" } }
  3. 应用程序:收到这个结构化请求后,在后台真正执行get_current_weather("Beijing", "2023-10-27")这个函数,调用气象API获取实时数据。
  4. 应用程序:将执行结果(真实的JSON格式天气数据)返回给AI模型。
  5. AI(模型):结合最初的用户问题和刚收到的真实数据,生成最终回复:“根据实时查询,今天北京晴转多云,气温15到25摄氏度,微风。”

关键特征与实操要点:

  • 模型原生能力:这是像GPT-4、Claude 3等先进模型内建的功能。你需要在调用模型API时,通过tools参数传入一个函数列表(包含函数名、描述和参数JSON Schema)来“告知”模型有哪些工具可用。
  • 结构化输出:模型的输出从非结构化的文本,变成了结构化的JSON数据,这极大方便了程序后续处理。
  • 依赖应用层实现:模型只负责“说”要调用哪个函数、参数是什么。具体的函数实现、API调用、错误处理、安全校验等,完全由开发者在其应用程序中完成。

注意:这里常有一个误区,就是认为Function Call的“执行结果”必须立刻放回对话上下文。实际上,这取决于架构。在简单的单轮交互中,确实如此。但在复杂的多步Agent场景中,结果可能被存入工作记忆、知识库或用于触发下一个动作。如果设计不当,没有妥善管理这个结果,确实可能导致本轮对话逻辑“死机”,即Agent无法基于结果做出正确决策。好的设计会将结果与推理过程分离。

2.2 Agent Skills:智能体的“组合技”与“肌肉记忆”

如果说Function Call是单个的“出拳”动作,那么Agent Skills就是一套复杂的“武术套路”。Skill不是一个官方的、严格的技术协议,而是一个更上层的、偏向于设计和应用的概念。它指的是一个智能体为完成特定类型任务所封装的一系列能力、步骤和决策逻辑的集合

它解决了什么问题?它解决的是任务复杂性问题。很多现实任务无法通过一次简单的函数调用完成。例如,“帮我分析一下这个GitHub仓库最近三个月的活跃度,并总结主要贡献者”这个任务,可能涉及:

  1. 调用GitHub API获取仓库信息。
  2. 调用GitHub API获取提交历史。
  3. 处理和分析时间序列数据。
  4. 调用另一个API或本地函数进行文本总结。
  5. 将结果格式化为报告。

一个“GitHub仓库分析Skill”,就是将步骤1-5的规划、工具调用顺序、中间结果处理和最终输出格式,封装成一个可复用的模块。当用户提出类似请求时,Agent可以直接启用这个“Skill”,而不需要每次都从头开始规划。

关键特征与实操要点:

  • 高层次抽象:Skill关注的是“做什么”和“做的流程”,而不是“如何调用某个具体API”。它可能由多个Function Call、条件判断、循环甚至调用其他子Skill组成。
  • 与Agent框架强相关:Skill的概念在如LangChain、AutoGen、Dify等Agent框架中非常常见。在这些框架里,你可以像搭积木一样,将不同的Skill(如“网页搜索Skill”、“代码执行Skill”、“数据分析Skill”)组装成一个强大的智能体。
  • 包含决策逻辑:一个Skill内部通常封装了何时使用、如何应对失败、如何解析结果等逻辑。例如,一个“订机票Skill”可能包含先搜索航班、再比价、最后验证用户支付信息的完整流程。

2.3 MCP (Model Context Protocol):工具生态的“统一插座标准”

MCP,由Anthropic公司提出,全称是Model Context Protocol。你可以把它理解成智能体领域的“USB标准”或“应用商店协议”。它定义了一套标准化的通信协议,用于在AI模型(或AI应用)与外部工具、数据源之间建立安全、高效的连接

它解决了什么问题?在MCP之前,每个AI应用开发者如果想连接一个新工具(比如Notion、Figma、公司内部CRM),都需要:

  1. 为该工具编写特定的API集成代码。
  2. 将该工具的API格式手动转换成模型能理解的Function Call描述。
  3. 处理身份认证、错误码、速率限制等琐事。
  4. 如果换一个模型(比如从Claude换到GPT),可能还要重新适配一遍。

这个过程繁琐、重复且难以维护。MCP的目标是让工具提供者按照统一标准发布工具(称为MCP Server),而AI模型或应用(称为MCP Client)可以通过统一的方式发现、描述和调用这些工具,无需关心底层实现。

关键特征与实操要点:

  • 服务器-客户端模型:核心是MCP Server(工具提供方)和MCP Client(模型/应用使用方)的分离。Server向Client宣告自己提供了哪些“资源”(如文件、数据库连接)和“工具”(即可执行函数)。
  • 协议标准化:MCP定义了标准的JSON-RPC over STDIO/SSE/HTTP通信方式、工具描述格式、资源类型、错误处理等。这带来了巨大的互操作性优势。
  • 动态与静态工具:MCP Server不仅可以提供预定义的函数(静态工具),还能动态生成工具。例如,一个文件系统Server可以提供一个“list_directory”工具,Client调用它并获得一个目录列表后,Server可以动态为每个文件创建“read_file”和“delete_file”工具。
  • 蓬勃发展的生态:这正是MCP最强大的地方。现在已经有大量开源的MCP Server,例如:
    • playwright-mcp: 提供网页浏览、自动化操作工具。
    • filesystem-mcp: 提供本地文件读写工具。
    • sqlite-mcp: 提供SQLite数据库查询工具。
    • figma-mcp: 连接Figma设计平台。
    • brave-search-mcp: 提供联网搜索工具。
  • 与具体模型解耦:一个MCP Server可以被任何兼容MCP Client的AI应用使用,无论是Claude Desktop、Cursor IDE,还是你自建的Agent系统。这打破了工具生态与模型供应商的绑定。

3. 三维对比:关系、层级与应用场景

理解了各自定义后,我们可以从几个维度进行立体对比。

3.1 核心关系:层级与依赖

它们三者并非并列关系,而是存在清晰的层次结构:

  1. Function Call 是基石:它是大模型与外部交互的原子能力。无论是Agent Skills内部的步骤,还是MCP Client调用MCP Server的工具,其最终落地的技术动作,绝大多数都是一个Function Call(或类似的工具调用)。没有这个能力,后续的一切都无从谈起。

  2. Agent Skills 是业务封装:它位于Function Call之上,是业务逻辑层的抽象。一个Skill利用一个或多个Function Call(这些Call可能来自MCP,也可能来自原生集成),按照特定流程组织起来,完成一个高级目标。Skill是面向任务和用户的。

  3. MCP 是基础设施与生态:它位于Function Call和Agent Skills的侧面,提供的是连接层和标准化层。MCP定义了工具如何被描述、被发现、被调用的标准。Agent Skills可以通过MCP Client去获取和使用由MCP Server提供的标准化工具,从而丰富自己的能力,而无需重复造轮子。

一个类比

  • Function Call像是电脑的“硬件指令集”(如ADD, MOV)。
  • Agent Skills像是用这些指令集编写的“应用软件”(如Photoshop、Chrome)。
  • MCP像是“操作系统API”和“应用商店协议”。应用软件(Skills)通过操作系统API(MCP Client)来调用系统功能(如打印、网络访问),而各种硬件驱动和第三方服务(MCP Servers)则按照标准协议(MCP)接入系统,供所有应用软件使用。

3.2 能力范围与灵活性对比

特性维度Function CallAgent SkillsMCP
定义层级模型层/原子操作应用层/任务流程协议层/连接标准
核心目的让模型输出结构化工具调用请求封装复杂任务逻辑,实现能力复用标准化工具集成,构建可互操作生态
标准化程度中等(OpenAI/Anthropic等各有格式,但大同小异)低(各框架自有定义,如LangChain Tool, Dify Skill)(开放协议,统一规范)
可复用性低(函数描述需随请求发送,与业务逻辑耦合)中等(在同一个框架或应用内可复用)极高(一次开发MCP Server,所有兼容Client均可使用)
动态性静态(一次请求中工具列表是固定的)可在运行时根据逻辑选择不同技能支持动态(Server可动态注册新工具、新资源)
开发关注点定义函数Schema,实现后端函数设计任务流程,编排工具调用与决策实现MCP Server协议,封装工具功能

3.3 典型应用场景与选择指南

在实际项目中,如何选择?

场景一:快速验证一个简单功能

  • 需求:你想在聊天机器人里加一个“查股价”的功能。
  • 选择:直接使用Function Call。在调用模型API时,传入一个get_stock_price的函数描述,并在你的后端实现这个函数。这是最直接、最快的方案。
  • 理由:功能简单,无需复杂流程,也没有跨平台共享的需求。

场景二:构建一个专业的、多步骤的客服助手

  • 需求:客服助手需要能处理“退货”、“查询订单”、“产品推荐”等多种复杂请求,每种请求都涉及多个系统查询和条件判断。
  • 选择:设计多个Agent Skills。例如,“处理退货Skill”可能包括验证订单状态、检查退货政策、生成退货单、通知仓库等步骤。你可以使用LangChain等框架来编排这些Skills。
  • 理由:任务复杂,需要封装固定的业务流程和决策逻辑,便于维护和迭代。

场景三:为团队或社区构建一个通用的AI开发环境

  • 需求:你希望团队内的AI应用都能方便、安全地访问公司内部的GitLab、JIRA、CRM等系统,而不希望每个开发者都去研究各自的API。
  • 选择:为每个内部系统开发一个MCP Server。然后,团队成员可以在Claude Desktop、Cursor或自建Agent中统一配置这些MCP Server,即刻获得所有工具。
  • 理由:需要标准化、安全且可复用的工具集成,避免重复劳动,并统一管控权限和审计。

场景四:希望AI能使用一个不断增长的外部工具集

  • 需求:你正在开发一个代码助手,你希望它不仅能写代码,还能搜索网页、查询文档、操作数据库,并且未来能轻松接入更多工具(如Docker管理、K8s调试)。
  • 选择:采用MCP作为核心架构。你的代码助手作为MCP Client,可以接入postgres-mcpplaywright-mcpbrave-search-mcp等大量现成Server。当需要新能力时,只需寻找或开发对应的MCP Server即可。
  • 理由:追求极致的生态扩展性和工具丰富度,避免被锁定在某个特定模型或框架的工具集里。

4. 实战解析:从概念到代码

光说不练假把式。我们通过一个具体的例子,来看三者如何协同工作。假设我们要构建一个“智能数据分析助手”,它能根据用户自然语言描述,从数据库拉取数据并生成图表。

4.1 基于纯Function Call的实现(传统方式)

在这种方式下,我们需要在应用层定义所有工具。

# 伪代码示例:应用后端 import openai import pandas as pd import plotly.express as px from database import query_db # 1. 定义工具列表,在每次调用模型API时传入 tools = [ { "type": "function", "function": { "name": "query_sales_data", "description": "查询销售数据表,可指定时间范围和地区", "parameters": { "type": "object", "properties": { "start_date": {"type": "string", "description": "开始日期,YYYY-MM-DD格式"}, "end_date": {"type": "string", "description": "结束日期,YYYY-MM-DD格式"}, "region": {"type": "string", "description": "地区,如‘华东’、‘华北’,默认为全部"} }, "required": ["start_date", "end_date"] } } }, { "type": "function", "function": { "name": "generate_chart", "description": "根据提供的数据和图表类型生成图表HTML", "parameters": { "type": "object", "properties": { "data": {"type": "string", "description": "JSON格式的序列化数据"}, "chart_type": {"type": "string", "enum": ["line", "bar", "pie"], "description": "图表类型"}, "title": {"type": "string", "description": "图表标题"} }, "required": ["data", "chart_type"] } } } ] # 2. 实现工具对应的后端函数 def run_query_sales_data(start_date, end_date, region=None): """实际执行数据库查询""" sql = f"SELECT * FROM sales WHERE date BETWEEN '{start_date}' AND '{end_date}'" if region: sql += f" AND region = '{region}'" data = query_db(sql) return data.to_json(orient='records') # 返回JSON字符串 def run_generate_chart(data_json, chart_type, title): """实际生成图表""" df = pd.read_json(data_json) if chart_type == "line": fig = px.line(df, x='date', y='amount', title=title) elif chart_type == "bar": fig = px.bar(df, x='region', y='amount', title=title) # ... 其他图表类型 return fig.to_html() # 3. 主循环:与模型交互 def chat_with_ai(user_input): messages = [{"role": "user", "content": user_input}] # 调用模型,传入工具定义 response = openai.chat.completions.create( model="gpt-4", messages=messages, tools=tools, tool_choice="auto" ) message = response.choices[0].message # 检查模型是否想调用工具 if message.tool_calls: for tool_call in message.tool_calls: func_name = tool_call.function.name args = json.loads(tool_call.function.arguments) if func_name == "query_sales_data": result = run_query_sales_data(**args) elif func_name == "generate_chart": result = run_generate_chart(**args) # 将结果追加到消息历史,让模型继续 messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": result }) # 再次调用模型,让其基于工具结果生成最终回复 second_response = openai.chat.completions.create(...) return second_response.choices[0].message.content else: return message.content

这种方式的问题

  • 紧耦合:工具定义和实现都硬编码在应用里。
  • 难以扩展:每加一个新工具(比如“发送邮件报告”),都要修改代码、更新工具列表。
  • 无法共享:其他项目想用同样的数据库查询工具,得重新复制一遍代码和定义。

4.2 引入MCP:将工具服务化

现在,我们用MCP来改造。我们将数据库查询和图表生成分别做成独立的MCP Server。

第一步:创建sales-db-mcp-server这个Server提供一个query_sales_data工具。

第二步:创建chart-generator-mcp-server这个Server提供一个generate_chart工具。

第三步:AI应用(MCP Client)配置我们的AI应用(比如一个定制的Agent框架)不再需要硬编码工具实现,只需配置MCP Servers的地址。

# 应用配置文件 mcp_config.yaml servers: - name: "sales-db" command: "node" # 假设Server是Node.js写的 args: ["/path/to/sales-db-mcp-server/dist/index.js"] env: DB_CONNECTION_STRING: "postgresql://..." - name: "chart-gen" command: "python" args: ["/path/to/chart-generator-mcp-server/main.py"]

当AI应用启动时,它会通过MCP协议与这两个Server握手,自动获取它们提供的工具列表。模型需要查询数据时,会通过MCP Client向sales-dbServer发起调用;需要生成图表时,则调用chart-genServer。

优势立刻显现

  1. 解耦:数据库逻辑、图表生成逻辑与AI应用核心逻辑分离。
  2. 复用:其他任何兼容MCP的AI应用(如Claude Desktop)都可以直接配置这两个Server来获得相同能力。
  3. 独立演进:可以单独升级chart-generator-mcp-server以支持新的图表库,而不影响AI应用。

4.3 封装为Agent Skill:提供完整解决方案

最后,我们创建一个高级的“销售数据分析Skill”。这个Skill内部会:

  1. 理解用户意图(如“帮我画一张华东地区上周的销售趋势图”)。
  2. 规划步骤:先调用query_sales_data,再调用generate_chart
  3. 处理中间逻辑:可能需要将用户说的“上周”转换为具体的起止日期。
  4. 处理错误:如果查询无数据,则给出友好提示,而不是直接报错。
  5. 最终呈现:将图表HTML嵌入回复,或保存为文件。

这个Skill可以被安装到你的数据分析Agent中。当用户提出相关问题时,Agent会优先启用这个Skill,而不是从零开始思考每一步。

5. 深度探讨:常见困惑与进阶思考

在实践中,关于这三者的边界和选择,还有一些更深入的讨论点。

5.1 MCP Server与普通API服务有何不同?

这是一个非常关键的问题。MCP Server确实通过HTTP等协议提供服务,但它不是简单的REST API。

  1. 协议标准化:普通REST API千奇百怪,而MCP Server遵循统一的JSON-RPC格式进行“工具列表声明”、“调用”和“资源通知”。Client无需为每个Server写特定的适配器。
  2. 动态发现:Client启动时通过标准握手过程,就能获取Server提供的所有工具和资源的完整描述(包括名称、参数Schema、文档)。这是一种“自描述”机制。
  3. 双向通信与资源:MCP不仅支持工具调用,还支持“资源”(Resources)概念。Server可以主动向Client推送或通知资源的变更。例如,一个文件系统Server可以将一个目录声明为“资源”,当目录内文件变化时,可以通知Client。这对于需要实时感知外部状态变化的Agent至关重要。
  4. 为AI交互优化:工具的描述(名称、参数说明)是专门为让大模型理解而设计的,比普通的API文档更结构化、更精准。

5.2 “Skill”和“MCP工具”可以互相转换吗?

可以,但它们处于不同层面,转换意味着封装层次的改变。

  • 将一组MCP工具封装成一个Skill:这是最常见、最推荐的做法。例如,利用postgres-mcpchart-generator-mcp提供的工具,加上一些业务逻辑(日期解析、错误处理),封装成一个“销售报告生成Skill”。这个Skill对用户暴露的是一个高级任务接口。
  • 将一个复杂Skill暴露为MCP工具:如果你开发了一个非常强大的“代码重构Skill”,你也可以将它包装成一个MCP Server,对外提供一个如refactor_code(代码, 重构类型)的工具。这样,其他AI应用就能通过标准MCP协议来使用你的重构能力,而不必理解其内部复杂的步骤。

生成技巧:设计Skill时,思考其复用边界。如果这个能力是你当前Agent独有的业务逻辑,就作为内部Skill。如果这个能力具有通用性,可以被其他任何AI应用使用,那么考虑将其实现为MCP Server会带来更大的生态价值。

5.3 性能与安全考量

  • Function Call的延迟:模型生成工具调用本身需要时间,且工具执行(尤其是网络调用)是同步阻塞的,会显著增加单轮对话的响应延迟。在设计Skill时,要考虑将必要的工具调用并行化,或者设置合理的超时。
  • MCP的通信开销:MCP Server通常运行在独立的进程,通过STDIO/HTTP通信,这比进程内函数调用有额外的序列化/反序列化和IPC开销。对于延迟极度敏感的场景,需要评估。
  • 安全边界:这是MCP的核心优势之一。通过MCP,你可以将具有潜在危险或高权限的操作(如执行Shell命令、删除文件、访问生产数据库)隔离在独立的Server进程中。你可以严格管控每个Server的权限(例如,通过Linux用户权限、容器隔离),即使Server被恶意提示词操控,其破坏范围也被限制在该Server的权限内。而在纯Function Call架构中,所有工具代码都运行在应用主进程,风险更高。

5.4 生态现状与工具选择

当前,MCP生态正在爆炸式增长。除了前面提到的通用工具,一些垂直领域的集成也非常有趣:

  • 安全领域burp-mcp将Burp Suite的安全测试能力暴露给AI,可以辅助安全审计。
  • 逆向工程ida-pro-mcp让AI可以交互式地分析二进制文件,这是一个革命性的想法。
  • 设计协作figma-mcp尽管可能“还原度很低”(原因可能是Figma API的限制或MCP Server实现的复杂度),但它开启了AI辅助设计的新方式。
  • 本地工具filesystem-mcp,sqlite-mcp让AI智能体具备了操作本地环境的能力,为AI PC助理铺平道路。

对于开发者而言,选择变得清晰:优先寻找现成的MCP Server。在构建自己的工具时,如果通用性强,优先实现为MCP Server。对于高度定制、与核心业务逻辑绑定的复杂流程,则设计为内部的Agent Skill

6. 总结与个人实践心得

回到开头的面试题。Agent Skills、MCP、Function Call的区别,本质上是在问我们对AI应用架构层次的理解。

  • Function Call是“我能做什么动作”的声明与执行机制,是模型能力的直接体现。
  • Agent Skills是“我如何完成一项复杂工作”的策略与流程封装,是面向用户价值的。
  • MCP是“我的动作从哪里来,如何管理”的供应链与标准协议,是面向系统扩展性和开发者生态的。

在我自己的项目中,现在的标准做法是:

  1. 底层连接:尽可能使用MCP来接入各种外部能力和数据源。这就像为我的智能体搭建了一个标准化、可插拔的“外设库”。
  2. 能力构建:在MCP提供的原子工具基础上,构建面向特定垂直领域(如客服、代码评审、内容运营)的Agent Skills。这些Skills是核心业务资产。
  3. 模型交互:在Skill内部的具体步骤中,以及Agent的顶层决策中,依赖模型(通过Function Call)的理解和规划能力,将MCP工具和业务逻辑串联起来。

这样的架构,让系统变得清晰、可维护且充满弹性。当需要新能力时,我不再焦虑于如何从零集成一个晦涩的API,而是先去MCP市场找找有没有现成的Server。如果没有,我会评估将其开发成一个MCP Server是否能惠及更多人,从而决定投入。

最后一个小技巧:当你开始设计一个AI功能时,可以自问三个问题:“这是一个简单的工具调用吗?(Function Call)”、“这是一个需要多步决策的复杂任务吗?(Skill)”、“这个能力是否通用,值得被标准化?(MCP)”。回答会帮你找到最合适的技术路径。

← 返回列表