3. 大模型的 Function Call 能力是怎么训练出来的Function Call 的能力主要靠两个训练阶段来培养这两个阶段解决的是不同的问题。第一个是 SFT就是给模型喂大量「包含工具调用的完整对话样本」每条样本覆盖工具定义、用户问题、模型应该输出的结构化 JSON 调用、工具执行结果、最终答案让模型通过模仿学会整套流程。但光有 SFT 不够模型可能学得过激遇到什么问题都想调工具。第二个阶段是 RLHF通过人类标注「哪种回答更好」来训练奖励模型再用强化学习调整主模型让它学会「能直接回答的就直接回答需要实时数据才去调工具」这个边界感。一句话总结SFT 教会怎么调RLHF 教会什么时候调。很多人以为 Function Call 是大模型「聪明」之后自然就会的东西其实不是。就算是 GPT-4如果没有经过工具调用的专项训练它遇到「北京今天天气怎么样」这个问题顶多也就输出一句「我需要查询天气数据」这是在描述一个意图而不是在输出可以被程序解析和执行的结构化 JSON。这两件事之间有本质的差距。为什么预训练学不会这件事关键原因是预训练语料里压根没有「标准工具调用 JSON」这种模式。预训练喂给模型的是互联网上的海量文本里面有代码、有文档、有对话但几乎没有「给定一组工具 schema该在什么场景输出什么 JSON」这样的成对样本。模型在预训练期间的所有权重更新都是基于「预测下一个最可能的 token」这个目标它记住的是人类文字里的统计规律而「遇到天气问题就输出一段固定格式的 tool_calls JSON」不是人类文字里自然存在的模式权重里根本没有相关经验可以调用。所以这件事必须靠专项微调来补。Function Call 的核心训练分两个阶段每个阶段解决不同的问题缺一不可。阶段一SFT让模型「学会怎么调」SFTSupervised Fine-Tuning监督微调的核心动作很简单给模型喂大量正确示例让它通过模仿来学习。对 Function Call 能力来说就是构造「包含完整工具调用流程的对话样本」一条样本里包含所有角色的消息。一条完整的训练样本大概是这样的结构首先是 system 消息里面注入了工具的定义工具叫什么名字、是干什么用的、接受哪些参数。模型是从这里「认识」工具的就像你给新员工一份工具手册告诉他公司有哪些系统可以用。接着是 user 消息就是用户的提问比如「北京今天天气怎么样」。然后是关键的一步assistant 的消息不是自然语言而是结构化的 JSON 调用请求类似{tool_calls: [{name: get_weather, arguments: {city: 北京}}]}这是「正确答案」是模型通过训练需要学会输出的内容。之后是 tool 角色的消息是工具执行后返回的结果比如「晴天15°C东北风 3 级」。最后 assistant 再出现一次根据工具结果给出最终的自然语言答案。这套完整的对话结构覆盖了整个调用链路。模型通过反向传播backpropagation来学习给定样本里的正确 JSON 调用当模型输出的内容偏离这个正确答案时损失函数就会产生惩罚信号梯度往回传一点点调整模型内部的参数权重让下次输出更接近正确格式。大量样本反复训练模型就把这套「看到工具定义 看到用户问题 - 输出规范 JSON」的模式「记住」了。就像背单词看一遍不够反复遇到、反复纠错最终形成了肌肉记忆。训练数据需要覆盖哪些场景训练数据的多样性直接决定了 Function Call 能力的上限不能只有「正常调一个工具」这一种情况。好的训练数据至少要覆盖这几类场景。最基础的当然是单工具调用一个问题对应一个工具这是入门款。但光有这个远远不够还需要多工具并行调用的样本比如用户问「帮我查北京和上海的天气」模型应该一次性输出两个调用请求而不是傻乎乎地一个一个来如果训练数据里没见过这种场景模型就不知道可以并行。另一个容易忽略但非常关键的场景是工具调用失败后的处理。现实中工具不可能百分百成功API 超时、参数格式不对、权限不足各种错误都有可能出现模型要能识别错误信息并换个方式处理而不是直接崩掉或者傻傻地重复同样的调用。还有一类场景很多人想不到不需要调工具、直接回答。这个其实非常重要「11 等于几」「帮我总结这段话」这类问题完全不需要工具模型得学会判断「我能自己解决不用调」。如果训练数据里全是「调用工具」的正例模型就会形成「遇到问题就调工具」的惯性该直接回答的时候也去调画蛇添足。最后是多轮对话中的工具调用上下文里已经有过工具结果模型要能正确理解和引用之前的结果而不是无视历史重新调用。这些场景的覆盖程度直接影响模型在实际使用中的表现缺哪个就会在哪个场景下翻车。训练数据从哪来构造 Function Call 训练数据主要有两种方式。第一种是人工标注雇标注员给定用户问题和工具定义让他们写出正确的调用示例。这种方式质量好因为人写的样本准确度有保障但成本极高通常只用于核心种子数据的构造没法大规模扩展。第二种是模型自动生成业界也叫 Self-Instruct 或 Distillation蒸馏。核心思路是用一个已经具备 Function Call 能力的强模型比如 GPT-4批量生成训练样本再人工抽查质量。这是现在业界的主流做法成本低、量大但有一个隐患如果上游模型本身生成了错误的样本下游模型就会把这个错误一起学进去业界叫做「模型蒸馏的幻觉传递」。所以抽查质量这一步不能省不然相当于在教模型学错误答案。阶段二RLHF对齐「该不该调」的边界感SFT 之后模型已经学会了「怎么调工具」但还有一个问题没解决什么时候应该调工具什么时候直接回答。SFT 的训练样本都是「正确调用」的例子模型看多了可能学得有点偏激遇到什么问题都想调工具不管有没有必要。「11 等于几」不应该调计算器直接回答 2 就行「帮我总结这段文字」完全不需要任何工具直接做就行只有「北京今天天气」这类需要实时数据的问题才应该调。这种「能直接回答就直接回答需要外部数据才去调」的边界感SFT 很难教好因为光靠固定的正确样本没法覆盖所有边界情况。RLHF 就是来解决这个问题的。RLHFReinforcement Learning from Human Feedback人类反馈强化学习的流程分四步。第一步是生成多样回答。对同一个问题让模型生成多种回答有直接回答的有调了工具的有调了工具但参数填错的故意覆盖各种情况。第二步是人类标注打分。标注员对这些回答进行排序比如「北京今天天气怎么样」这道题调了天气工具的回答排第一因为不调工具就没法给出准确的实时数据「11 等于几」这道题直接回答 2 的排第一调计算器工具的反而是画蛇添足排末尾。第三步是训练奖励模型。用这批打分排序的数据单独训练一个神经网络叫做「奖励模型」Reward ModelRM。奖励模型学会了一件事给定一个问题和一种回答预测人类会给这个回答多少分。它不直接回答用户只负责打分相当于一个专门评判「哪种回答更好」的裁判。第四步是用强化学习调整主模型。拿奖励模型的打分信号通过 PPOProximal Policy Optimization近端策略优化等强化学习算法持续调整主模型的参数让主模型越来越倾向于输出「奖励模型打高分」的回答。你可能会好奇为什么偏偏是 PPO强化学习算法那么多选 PPO 有两个很务实的理由一是它相对稳定训练过程不容易崩传统策略梯度算法很容易因为单步更新太大把模型直接调废二是它内置了一个 KL 散度约束强迫新模型和旧模型的输出分布不要差得太远这样就不会出现「为了讨好奖励模型模型把自己训成只会重复几句套话的怪胎」这种退化情况。RLHF 本质上要让模型在「追求高奖励」和「保持语言能力」之间走钢丝PPO 在这个平衡上是目前公认好用的工具。经过足够多的迭代模型就学会了那种边界感该调工具时调能直接回答时不折腾。RLAIF用 AI 代替人工打分RLHF 最大的痛点是人工标注成本极高标注员需要专业背景打分慢、价格贵很难大规模扩展。RLAIFReinforcement Learning from AI Feedback是它的改进版。你可以这么理解RLHF 是请一群专业的人类评委来给模型的回答打分质量很高但请评委的成本也很高RLAIF 就是换成了一个「AI 评委」用更强的 AI 模型比如 GPT-4来代替人类标注员做这个打分的活儿成本能低 10-100 倍速度也快得多。不过代价也很明显「AI 的偏见会传递」。如果打分的 AI 本身对某些场景的判断有偏差或者盲区这些偏差也会被学进去。打个比方如果 AI 评委觉得「遇到数学题都应该调计算器工具」那被它训练的模型也会学到这个倾向哪怕有些简单算术不用调工具。所以打分 AI 的质量很关键选什么模型来当评委、怎么设计评分标准都需要仔细考虑。现在业界很多模型训练都在混用 RLHF 和 RLAIF在关键数据上用人工保质量量大的地方用 AI 提效率两者互补。两个阶段各司其职SFT 解决的是「会不会」的问题RLHF 解决的是「该不该」的问题。只有 SFT 而没有 RLHF 的模型可能遇到什么问题都冲动地调工具反过来只有 RLHF 而没有 SFT模型连工具调用的格式都输不出来奖励信号根本没地方发力。两个阶段配合起来才能训练出「知道怎么调、也知道什么时候该调」的工具使用能力。面试回答这道题要把两个训练阶段讲清楚。SFT 阶段通过「system 工具定义 user 问题 assistant JSON 调用 tool 执行结果 assistant 最终回答」这样的完整对话样本来训练让模型通过反向传播学会整套流程。RLHF 阶段通过人类对多种回答的偏好排序训练奖励模型再用 PPO 等强化学习算法调整主模型建立「该不该调」的边界感。训练数据来源也要提到人工标注质量高但成本高用于种子数据模型自动生成Self-Instruct / Distillation成本低量大但要注意幻觉传递的风险。一句话总结SFT 教会怎么调RLHF 教会什么时候调。4. 什么是 MCP模型上下文协议讲讲它的核心内容MCP 是 Anthropic 在 2024 年底推出的开放协议我理解它主要解决的是「模型接工具太碎片化」的问题。在 MCP 出现之前每接一个新工具都要单独写集成代码、处理认证、适配格式而且这套代码和具体模型强绑定换个模型就得重写非常繁琐。MCP 的思路是把这件事标准化工具提供方按协议实现一个 Server任何支持 MCP 的 AI 客户端就能直接接进来一次实现到处复用。协议定义了三类能力Tools 用于执行有副作用的操作Resources 是只读数据Prompts 是提示词模板底层通信用 JSON-RPC 2.0。我把它理解成给「AI 接工具」这件事定了一套行业标准。没有 MCP 之前接工具有多麻烦想象你要给 Claude 接入 GitHub 工具。你得手写 GitHub API 的调用代码、处理认证OAuth token 怎么传、处理各种返回格式、把 API 响应转成模型能理解的格式……好不容易接好了。结果过了两个月Claude 升了个版接口有变化你的对接代码得改。更麻烦的是你同时接了十个工具每个工具都有自己的一套对接代码各自的格式、认证方式、错误处理逻辑都不一样。现在产品方说这套工具也要给 Cursor 用不好意思你得重写一遍因为 Cursor 和 Claude Desktop 的接入方式完全不同。这就是 MCP 出现之前AI 工具生态的真实状态碎片化、难复用、强绑定。每个工具、每个模型都是一座孤岛接一个新工具就要重新搭一座桥。MCP 的核心思路定一套行业标准接口MCPModel Context Protocol模型上下文协议的设计思路可以用 USB 接口来类比。在 USB 标准出现之前鼠标用这个接口、键盘用那个接口、打印机又是另一个换台电脑就要愁接口不兼容。USB 出现之后所有外设统一接口任何设备插到任何电脑都能工作设备厂商只需要做一次适配全球所有 USB 电脑都能用。MCP 做的是同一件事为「AI 接工具」这件事定了一套统一的协议标准。工具提供方比如 GitHub 官方按 MCP 规范实现一个 MCP Server里面封装好各种操作。任何支持 MCP 的 AI 客户端Claude Desktop、Cursor、各种 Agent 框架都能直接连上这个 Server自动发现里面的工具并使用不需要写任何定制化对接代码。工具只需要实现一次到处复用。MCP 的 Client-Server 架构MCP 采用标准的 Client-Server 架构。Server 是工具的实现方。比如 GitHub 官方维护一个 GitHub MCP Server里面封装了「列出 PR」「创建 Issue」「搜索仓库」「查看 Diff」等操作Client 是 AI 应用那一侧比如 Claude Desktop 或 Cursor连上 Server 之后就自动获得了这些工具能力。一个 Client 可以同时连接多个 Server。你把文件系统 Server GitHub Server PostgreSQL Server 都接上模型就同时拥有了操作本地文件、读写代码仓库、查询数据库这三套工具能力而你不需要写任何对接代码只需要在配置文件里加几行 JSON重启后 Claude 自动发现并使用这些工具。三类核心能力Tools、Resources、PromptsMCP Server 可以向 Client 暴露三类能力各有各的定位。先说Tools工具这是最核心的能力对应 Function Calling 里的「函数」。Tools 的本质是「有副作用的操作」什么叫有副作用就是执行之后会改变外部世界的状态。创建文件、提交代码、发送 Slack 消息、调用第三方 API这些都属于 Tools因为执行完之后环境发生了变化而且往往不可逆。正因为如此Tools 通常需要用户授权确认才能执行不能让模型想调就调。再说Resources资源它和 Tools 最本质的区别就一个字只「读」。Resources 不会改变任何东西只是把数据提供给模型看。读取日志文件、查询数据库记录、获取文档内容都属于 Resources 的范畴。你可以把 Resources 理解成「工具的资料室」模型可以进去查资料但不能修改里面的东西。正因为只读、无副作用Resources 可以更宽松地暴露给模型不需要像 Tools 那样谨慎授权。最后是Prompts提示模板这个能力很多人容易忽略但在团队协作场景下特别有用。Prompts 就是预定义的提示词模板带参数占位符解决的是「每次都要手写重复 prompt」的问题。举个例子你的团队有一套固定的代码审查标准 prompt接受「编程语言」和「代码内容」两个参数调用时只需传入参数值就能自动展开成完整的提示词不用每次从头写。把公司积累的优质 prompt 封装成 MCP Prompts所有人都能复用统一标准这在实际工程中很实用。底层通信JSON-RPC 2.0 是什么理解 MCP 的底层先要知道 JSON-RPC 是什么。JSON-RPC 是一种轻量级的远程函数调用协议用 JSON 格式来表达「调用」这件事。核心非常简单客户端发一个 JSON 请求里面说清楚「调哪个方法、参数是什么、这次请求的 ID 是多少」服务端执行完返回一个 JSON 响应里面是执行结果或者错误信息。用 JSON 而不是二进制格式好处是易读、易调试、语言无关任何编程语言都能轻松实现。MCP 用的是它的 2.0 版本JSON-RPC 2.0相比 1.0 加了批量请求、通知消息等功能。在传输层MCP 支持两种方式。第一种是stdio标准输入输出Server 作为本地子进程运行Client 通过管道和它通信Server 从 stdin 读消息把结果写到 stdout。这种方式适合本地工具不需要网络启动快、延迟低Claude Desktop 接本地 MCP Server 用的就是这种方式。第二种是Streamable HTTPServer 作为 HTTP 服务部署在远程Client 通过 HTTP 连接和它通信。这种方式适合远程部署的工具服务或者需要多个 Client 共享同一个 Server 的场景比如团队共用一个部署在服务器上的数据库 MCP Server所有人的 AI 客户端都连同一个地址就行。这里有个演进要说清楚MCP 早期版本2024-11-05 规范用的是「HTTP SSE」双端点方案一个 GET 端点开 SSE 长连接接收推送一个 POST 端点发请求两个端点绑在一起工作。2025 年 3 月的规范更新里这套方案被改成了 Streamable HTTP老的 HTTPSSE 被标记为 deprecated但仍保留向后兼容。Streamable HTTP 并不是「抛弃 SSE」而是把原来的两个端点合并成一个/mcp端点。Client 用 POST 发请求Server 根据情况灵活返回短请求直接回一个普通 JSON 响应长请求则把这个 HTTP 响应升级为 SSE 流持续推送中间结果。架构更简洁部署也更友好一个端点就够serverless 环境也能跑本质还是 HTTP 加 SSE只是用法变了。MCP 生态发展这么快背后的原因是什么MCP 是 Anthropic 在 2024 年底发布的发布后发展速度很快主要有两个原因。第一个原因是极低的实现门槛。Anthropic 开源了协议规范和多语言 SDKPython、TypeScript 都有写一个最简单的 MCP Server 不到 30 行代码任何有基础编程经验的人都能上手。协议文档也写得清晰社区很快就爆发出大量贡献。你想想一个新技术如果上手成本很高再好的设计也很难推广开MCP 在这一点上做得很聪明。第二个原因是头部工具第一时间跟进。GitHub、Slack、PostgreSQL、Puppeteer浏览器自动化、Google Maps 等高频工具都有了官方或社区维护的 MCP Server开发者不需要自己写直接用现成的就行。接一个新工具在 Claude Desktop 的配置文件里加几行 JSON重启后 Claude 自动发现并使用整个过程零代码。当生态里可用的工具足够多开发者就更愿意采用这套协议形成了正向循环。目前 Claude Desktop、Cursor、Windsurf 等主流 AI 工具都内置了 MCP 支持。对开发者来说MCP 把「给 AI 接工具」这件事的门槛从「写一堆对接代码」降到了「改一行配置」这才是它被快速采用的核心原因。5. MCP 由哪几部分组成MCP 由三层组成可以从角色、能力、协议三个维度来理解。角色层有三个Host 是 AI 应用本身比如 Claude DesktopClient 是 Host 里负责和 Server 通信的模块Server 是工具提供方实现的独立进程一个 Host 可以同时连多个 Server。能力层定义了 Server 能暴露三类东西Tools 是有副作用的操作比如创建文件、调 APIResources 是只读数据比如读取文档内容Prompts 是预定义的提示词模板。协议层是底层通信消息格式统一用 JSON-RPC 2.0传输方式支持 stdio本地子进程通信和 Streamable HTTP远程 HTTP 连接两种早期的 HTTPSSE 双端点方案在 2025 年 3 月的规范更新里被标记为 deprecated。这三层合在一起就是 MCP 的完整组成。先建立整体感三层来看就清楚了很多人第一次接触 MCP 会被各种概念搞乱什么 Host、Client、Server、Tools、Resources、Prompts、JSON-RPC、stdio、SSE……一堆名词扔过来确实容易懵。其实你只要把它拆成三层来看就清楚了第一层是角色架构搞清楚谁和谁在通信第二层是能力类型搞清楚 Server 能暴露什么东西给模型用第三层是传输协议搞清楚消息是怎么传的。这三层各自独立、互不耦合合在一起就是 MCP 的完整骨架。下面我一层一层拆开来讲。第一层角色架构Host / Client / ServerMCP 定义了三个角色弄清楚每个角色负责什么是理解整个系统的关键。先说 Host。Host 是整个系统的宿主也就是你在用的 AI 应用本身比如 Claude Desktop、Cursor、Windsurf。Host 负责启动和管理所有 MCP Client控制连哪些 Server、什么时候断开连接是整个 MCP 系统的调度中心。你可以把 Host 理解成一家公司它决定要和哪些外部供应商Server合作并派出自己的联络员Client去对接。再说 Client。Client 是 Host 内部的连接模块一个 Client 对应一个 Server 连接。它负责三件事初始化和 Server 的连接、向 Server 查询「你有哪些工具/资源/模板」能力发现、把模型的调用请求转发给 Server 并把结果带回来。Client 是 Host 派出的「驻场联络员」专门负责和某一个 Server 打交道Host 本身不直接和 Server 说话。最后是 Server。Server 是工具提供方实现的独立进程对外暴露自己的工具、资源和提示词模板。Server 完全不关心上面是哪个 Host 在用它只需要按 MCP 协议响应 Client 的请求就行。这也是 MCP 的核心价值所在Server 写一次任何支持 MCP 的 Host 都能直接用GitHub 的官方 MCP Server 不需要分别为 Claude Desktop 和 Cursor 各写一份。三者的关系用图来看是这样的一个 Host 同时连多个 Server模型就同时拥有了所有这些工具能力而应用代码完全不需要为此多写一行。第二层能力类型Tools / Resources / Prompts理解了角色分工接下来看 Server 到底能暴露什么给 Client。很多人以为 Server 就只提供「工具」其实不止MCP 定义了三类能力每类解决不同的需求设计上有明确的职责分工。第一类是 Tools工具这是最核心、使用最频繁的能力对应的是有副作用的操作执行之后会改变外部世界的状态。创建文件、提交代码、发送 Slack 消息、调用第三方 API都属于 Tools。由模型主动触发执行有不可逆性所以通常需要用户授权确认。Tools 对应 Function Calling 里「函数」的概念只是在 MCP 框架下被标准化打包了。第二类是 Resources资源这是只读数据没有任何副作用只是把数据提供给模型看。读取日志文件、查询数据库记录、获取文档内容都是 Resources。和 Tools 最本质的区别是什么呢Resources 不会改变任何东西可以更宽松地暴露不需要像 Tools 那样谨慎授权。你可以把 Resources 理解成「工具的资料室」可以进去查资料但不能修改里面的东西。第三类是 Prompts提示模板这是预定义的提示词模板带参数占位符。它解决的是「每次都要手写重复 prompt」的问题。比如把团队固定的代码审查标准封装成模板接受「编程语言」和「代码内容」两个参数调用时只需传参自动展开成完整提示词不用每次从头写。这个能力特别适合团队内部的最佳实践共享把积累的优质 prompt 模板化所有人统一复用标准也更一致。三者的本质区别可以这样记Tools 改变世界Resources 观察世界Prompts 结构化表达。第三层传输协议JSON-RPC 2.0 传输方式Client 和 Server 之间的通信由两部分组成消息格式和传输方式这两层是解耦的。消息格式统一用 JSON-RPC 2.0。每条消息是一个 JSON 对象格式固定Client 发请求时说清楚「调哪个方法、参数是什么、这次请求的 ID 是多少」Server 返回响应时带上执行结果或错误信息通过 ID 匹配请求和响应。用 JSON 格式的好处是易读、易调试、任何编程语言都能实现不管 Server 是 Python 写的还是 TypeScript 写的消息格式是一样的。// Client 向 Server 查询工具列表 {jsonrpc: 2.0, id: 1, method: tools/list, params: {}} // Server 返回工具列表 {jsonrpc: 2.0, id: 1, result: {tools: [{name: read_file, ...}]}} // Client 请求调用某个工具 {jsonrpc: 2.0, id: 2, method: tools/call, params: {name: read_file, arguments: {path: /tmp/log.txt}}}那消息格式定了怎么传呢MCP 支持两种传输方式适合不同的部署场景。第一种是 stdio标准输入输出Server 作为本地子进程运行Host 通过操作系统的管道和它通信Server 从 stdin 读请求、把结果写到 stdout。这种方式适合本地工具不需要网络延迟极低也没有端口占用和网络安全问题Claude Desktop 接本地 MCP Server 走的就是这种方式。第二种是Streamable HTTPServer 作为 HTTP 服务独立部署Client 通过 HTTP 连接和它通信。这种方式适合远程部署的场景支持多个 Client 共享同一个 Server比如团队共用一个部署在服务器上的数据库 MCP Server所有人连同一个服务不需要各自本地跑一份。这里有个演进要说清楚MCP 早期2024-11-05 规范的远程传输方案叫「HTTP SSE」是双端点结构一个 GET 端点开 SSE 接收 Server 推送一个 POST 端点用来发请求。这套方案在 2025 年 3 月的规范更新里被改成了单端点的 Streamable HTTP老的 HTTPSSE 被标记为 deprecated但保留向后兼容。Streamable HTTP 并不是抛弃 SSE而是把双端点合并成一个/mcp。Client 用 POST 发请求Server 根据情况灵活返回短请求直接回普通 JSON长请求则把 HTTP 响应升级为 SSE 流持续推送中间结果。这样一个端点就能干完所有事对负载均衡器和 serverless 环境都更友好。这里有一个很重要的设计点消息格式JSON-RPC 2.0和传输方式stdio / Streamable HTTP是解耦的同一套 JSON-RPC 消息可以跑在任意传输层上切换传输方式不影响上层的工具调用逻辑。这个设计让 MCP Server 既可以轻量地作为本地进程运行也可以作为正式的微服务部署实现方式灵活但协议层始终一致。面试回答这道题第一个要点是三层结构要说清楚角色层Host / Client / Server、能力层Tools / Resources / Prompts、协议层JSON-RPC 2.0 stdio / Streamable HTTP。特别是 Host 和 Client 的区别Host 是宿主应用本身Client 是 Host 内部负责和 Server 通信的模块一个 Host 可以同时连多个 Server这个一对多的关系是 MCP 的核心设计。第二个容易踩的雷是把 Server 暴露的能力全归为「工具」。Tools、Resources、Prompts 三者职责分明Tools 有副作用、改变外部状态Resources 是只读数据、没有副作用Prompts 是提示词模板。面试时说清楚三者的区别尤其是 Tools 和 Resources 的本质差异有无副作用会让面试官觉得你真正理解了 MCP 的设计意图而不是只停留在表面。第三个要提到的是协议层的解耦设计消息格式和传输方式是独立的JSON-RPC 2.0 定义了消息长什么样stdio 和 Streamable HTTP 定义了消息怎么传两者互不耦合这也是 MCP 灵活性的来源。6. MCP 和 Function Calling 有什么区别有没有实际跑过 MCP我理解这两者不是竞争关系解决的不是同一层面的问题。Function Calling 是「调用语言」定义的是模型怎么表达「我要调哪个函数、参数是什么」MCP 是「工具生态协议」定义的是工具怎么标准化打包、注册和被 AI 客户端发现。MCP 底层其实还是用 Function Calling 来触发工具调用只是在它之上加了一套工具管理框架让工具实现一次、到处复用。打个比方Function Calling 像 HTTP 请求格式MCP 像 REST API 的设计规范加服务注册发现机制两者是不同层次的东西。关于实际跑过的经验我用 Claude Desktop 配过文件系统和 GitHub 的 MCP Server在配置文件里加几行就能用Claude 会自动发现工具完全不用写对接代码。Function Calling 有了为什么还需要 MCP很多人第一次看到 MCP 会有一个直觉困惑Function Calling 不是已经能调工具了吗模型想用工具直接定义 schema 就行为什么还要再搞一个协议出来这个困惑的根源是把「能调工具」和「管好工具」混在一起了。这两件事完全是不同层次的问题。有一个很好的类比可以帮你理解HTTP 协议出来之后我们已经能在网络上传数据了为什么还需要 REST API 规范因为 HTTP 解决的是「怎么传」一次请求长什么样、用什么方法、怎么编码REST 解决的是「怎么组织和管理」资源怎么命名、端点怎么设计、状态怎么表达、多个服务之间怎么复用同一套约定两者是不同层次的事。Function Calling 和 MCP 也是同样的关系Function Calling 管「一次函数调用请求长什么样」MCP 管「一堆工具怎么被组织、发现、跨项目复用」。前者是格式后者是规范生态约定少了谁这套机制都运转不起来。Function Calling 解决的是「一次调用」的格式问题从开发者视角看Function Calling 回答的是这几个问题工具定义用什么格式传给模型模型想调工具时怎么表达「我要调哪个函数、参数是什么」工具执行结果怎么喂回对话它定义的是单次调用的消息格式仅此而已。每次使用开发者都要手动写工具的 schema 定义手动写调用逻辑手动处理结果。Function Calling 本身没有任何工具管理、工具发现、跨项目复用的概念。Function Calling 的痛点每次都是一次性的那「每次手动」到底有多痛你可能觉得复制一份 schema 也没多大工作量但工具一多、项目一多问题就暴露出来了。假设你在 A 项目里定义了 10 个工具的 schema 和对接逻辑。现在 B 项目也要用这些工具怎么办把那 10 个 schema 定义复制过来把对接逻辑重写一遍。好写完了。但 A 项目用的是 Claude APIB 项目换成了 GPT-4两边的 Function Calling 格式不完全一样又得各自维护一套适配代码。这还没完如果某个工具的接口改了呢你得去每个用到它的项目里逐一更新漏改一处就是 bug。把这个账算一下你就知道痛点的规模了假设你团队里有 5 个应用每个应用要接 8 个工具也就是 40 份工具对接代码在维护。某天 GitHub API 的某个字段变了你要在 5 个地方同步改只要其中一个忘了那个应用在凌晨报警再假设你要从 Claude 迁到 GPT-4这 40 份代码里的 Function Calling 格式全要重新适配一遍整个组的季度就这么交代了。有没有发现问题同一个工具换个项目就要重新对接一遍每次都是一次性的手工活。这就是 Function Calling 解决不了的核心问题工具的管理、复用和跨平台兼容。MCP 解决的是「工具生态」的问题既然痛点是「每个应用各自维护一套工具定义」那解决思路也就很自然了把工具做成独立的标准化服务谁要用就来连不用每次都重写一遍。这就是 MCP 的核心思路。工具提供方实现一个 MCP Server这个 Server 是一个独立运行的进程对外暴露标准接口告诉外界「我有哪些工具、每个工具怎么调用」。任何支持 MCP 的 AI 客户端连上来就能自动发现和使用里面的工具完全不需要手写任何对接代码。这带来的改变是质的工具只需要实现一次所有 AI 客户端都能用。GitHub 的官方 MCP Server 写好之后不管你用 Claude Desktop、Cursor 还是自己写的 Agent连上去就能用不需要各自维护一份 GitHub API 的调用代码。这才是 MCP 的真正价值。最关键的联系MCP 底层依然靠 Function Calling 驱动这是很多人没想清楚的一点MCP 不是 Function Calling 的替代品而是建立在 Function Calling 之上的。当 MCP Client 连上一个 Server 之后会自动向 Server 拉取所有工具的定义调用list_tools接口然后把这些定义转换成模型原生的 Function Calling 格式传给模型。模型依然通过输出tool_calls来表达「我要调哪个工具」MCP Client 再把这个请求路由到对应的 Server 去执行拿到结果后以 tool 消息的形式喂回对话。从模型的视角来看它完全感知不到 MCP 的存在它以为自己只是在做普通的 Function Calling根本不知道背后有一套 Server 在运行。MCP 的所有「魔法」都发生在宿主程序层工具的自动发现、schema 的格式转换、调用请求的路由、执行结果的返回全都在这一层默默完成。这也意味着一件事如果模型本身不支持 Function CallingMCP 就完全没办法用因为这个「翻译层」失效了。实际体验接入一个 MCP Server以 Claude Desktop 接入文件系统 MCP 为例。只需要编辑claude_desktop_config.json加入如下配置{ mcpServers: { filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, /Users/yourname/Documents ] } } }这几行配置告诉 MCP Client用npx这个命令启动文件系统 Server把/Users/yourname/Documents目录作为允许访问的范围。command和args组合起来就是启动 Server 进程的命令行MCP Client 会把它作为子进程启动通过标准输入输出和它通信。配好之后重启 Claude Desktop它会自动启动这个 Server 进程自动发现里面提供的工具。然后你直接问 Claude「帮我读一下 Documents 里的 report.md」Claude 会自动调用文件系统工具完成任务全程你没写一行对接代码。如果想自己写一个 MCP Server其实也很简单。核心就三步首先用app.list_tools()装饰器告诉 Client 这个 Server 提供哪些工具及其参数格式然后用app.call_tool()装饰器实现每个工具的真实执行逻辑最后用 stdio 方式运行让 Client 能通过管道和它通信。完整的代码如下import asyncio from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent # 1. 创建一个 Server 实例名字叫 calculator app Server(calculator) # 2. 定义工具列表 (告诉 Client 我有什么功能) app.list_tools() async def list_tools() - list[Tool]: return [ Tool( nameadd_numbers, description计算两个数字的和, inputSchema{ type: object, properties: { a: {type: number, description: 第一个数字}, b: {type: number, description: 第二个数字} }, required: [a, b] } ) ] # 3. 实现工具的具体逻辑 app.call_tool() async def call_tool(name: str, arguments: dict) - list[TextContent]: if name add_numbers: a arguments.get(a, 0) b arguments.get(b, 0) result a b # 返回结果必须是 TextContent 格式 return [ TextContent( typetext, textf计算结果: {result} ) ] # 如果工具名不认识返回错误 return [TextContent(typetext, textf未知工具: {name})] # 4. 启动 Server (使用标准输入输出模式) async def main(): async with stdio_server() as (read_stream, write_stream): await app.run( read_stream, write_stream, app.create_initialization_options() ) if __name__ __main__: asyncio.run(main())整个 Server 加起来不超过 30 行代码Anthropic 开源的 MCP SDK 把底层通信都封装好了。然后编辑你的claude_desktop_config.json添加这个 Python 服务{ mcpServers: { calculator: { command: python, args: [ /path/to/your/calculator_server.py ] } } }注意这里的路径要改成你自己实际存放文件的绝对路径如果你的系统中 Python 命令是python3也要把command对应改过来。配好之后重启 Claude Desktop直接输入「帮我算一下 25 加 17 等于多少」Claude 就会自动调用你写的add_numbers工具并返回结果。整个过程你只写了工具逻辑本身所有的通信、发现、调用路由都由 MCP 框架搞定了。什么时候选哪个如果只是临时给自己的应用接一两个工具Function Calling 就够用了简单直接不需要引入额外的进程和协议。但如果工具多了、需要跨项目复用、或者想直接用社区里已有的成熟 ServerGitHub、数据库、浏览器自动化都有现成的MCP 就值得上了。接一个新工具就是在配置文件里加几行重启后自动生效比手写对接代码省事很多。做 Agent 系统的话更应该考虑 MCP。工具来源杂、数量多如果全靠手写 Function Calling 来维护工具定义代码会散落在各处很难管理。MCP 的自动发现和统一管理能让架构干净很多新增工具不需要改主程序逻辑接上 Server 就行。面试回答这道题第一个必须说清楚的点是Function Calling 解决的是单次调用的消息格式问题MCP 解决的是工具生态的标准化管理和复用问题两者是不同抽象层次的东西。第二个关键点是MCP 底层依然靠 Function Calling 驱动模型根本感知不到 MCP 的存在所有的工具发现、schema 转换、调用路由都发生在宿主程序层。如果能再补充实际跑过 MCP 的经验就更好了比如在 Claude Desktop 里配置过哪些 MCP Server、接入流程是什么样的这些实操细节能让面试官看到你不是只背概念。要避免的误区是不要说 MCP 就是「换了个写法的 Function Calling」也不要说两者是竞争关系它们是上下层的配合关系。7. Function Calling 也属于工具调用请问什么场景下使用 Function Calling什么场景下使用 MCP如果只是给单个应用接一两个工具、场景临时、不需要复用Function Calling 就够了简单直接不需要引入额外的进程和配置。但只要工具需要跨项目或跨团队复用、或者数量多了管理麻烦、或者社区已经有现成的 MCP Server 可以直接配置MCP 就值得上了。判断的核心问题只有一个这个工具会不会在这个应用之外被用到会的话把它封装成 MCP Server 是更长远的选择。此外做 Agent 系统的话更应该选 MCP工具来源多、数量大手写 Function Calling 的维护成本会让代码变得难以管理。先建立一个直觉内嵌 vs 独立Function Calling 的工具是「内嵌」在应用代码里的工具定义schema和调用逻辑都直接写在你的项目代码中工具和应用绑在一起应用换了就要重写一遍。MCP 的工具是「独立」的封装成一个独立运行的进程对外暴露标准接口任何支持 MCP 的 AI 客户端都能连上来直接用。工具的生命周期和应用解耦可以独立部署、独立维护、一次实现到处复用。这个「内嵌 vs 独立」的本质区别直接决定了两者各自适合的场景。Function Calling 的适用场景什么时候用 Function Calling 就够了简单来说就是「轻量、临时、不需要复用」的场景。最典型的就是做快速原型和 Demo。你的目标是跑通一个想法或做演示直接在代码里定义 schema 和调用逻辑就行不需要启动任何额外进程也不需要额外配置。这种场景下搞 MCP 完全没必要你花在搭 Server 上的时间可能会超过原型本身的价值。再比如工具只为这一个应用服务的情况。假设你在做一个内部工具里面有一个查本公司私有数据库的接口这个接口绝不会被任何其他地方用到那用 Function Calling 把逻辑直接写在项目里反而更清晰何必额外维护一个独立的 MCP Server 进程呢还有一种情况是你需要对工具的执行逻辑做精细控制。Function Calling 的调用逻辑完全在你的代码里想加权限校验、参数二次处理、特殊错误处理、调用链路追踪都可以直接嵌进去。MCP Server 是独立进程这类定制逻辑要额外传递或约定不如直接写在调用代码里方便。最后还有一个容易被忽略的因素部署环境的限制。某些受限的云环境或 Serverless 平台不允许启动子进程stdio 模式的 MCP Server 就没法用了。这种情况只能退回 Function Calling工具逻辑都写在主进程里反而是最稳妥的选择。MCP 的适用场景那什么时候该上 MCP 呢一句话概括只要工具不是「自己用、用一次就扔」MCP 基本都值得考虑。最核心的场景就是工具需要跨项目或跨团队复用。想想看同一套 GitHub 操作工具你自己的 Claude Desktop 要用、同事的 Cursor 要用、团队的 CI/CD Agent 也要用。如果用 Function Calling意味着三处各维护一份 schema 和调用代码工具接口一变就要同步改三处漏改一个就出 bug。MCP 的做法就优雅多了工具封装成独立 Server任何人在配置文件里加几行就能接进来用维护的责任在 Server 那一侧所有客户端自动受益。还有一个特别务实的理由社区已经有现成的 MCP Server 了。GitHub、Slack、PostgreSQL、Puppeteer、Google Maps 这类高频工具都有经过测试、文档完整的官方或社区 MCP Server。你不需要自己写一遍直接配置就能用// 在 claude_desktop_config.json 里加几行一行工具代码都不用写 { mcpServers: { github: { command: npx, args: [-y, modelcontextprotocol/server-github] } } }这种情况下还用 Function Calling 手写 GitHub API 的调用代码那就是重复造轮子了完全没必要。另外当工具规模起来、MCP 的管理优势就很明显了。这里不想给一个绝对的数字门槛比如「超过 3 个就要上 MCP」因为实际判断要看几个维度综合工具的复杂度每个工具的 schema 和调用逻辑是几行还是几十行、团队规模是一个人维护还是多人协作、变更频率工具接口经常改还是基本稳定。如果你的工具平均复杂度不低、团队里好几个人都在碰这些代码、接口还时不时调整那就算只有 5 个工具也会很快把你拖进维护泥潭。反过来两个极其简单、几乎不动的工具没必要为它们引入一套独立 Server。为什么工具多了 Function Calling 就会难维护因为工具的 schema 定义和调用逻辑会散落在代码各处新加一个工具要改应用代码工具有 bug 也要进应用代码改出问题时定位链路很长。MCP 的自动发现机制正好解决这个问题主程序不感知具体工具只连 Server新增工具只需要在 Server 里加实现主程序完全不需要改动。最后如果你在构建 Agent 系统MCP 几乎是必选项。Agent 系统的工具需求往往多样、来源复杂可能同时需要代码执行、文件系统、数据库、外部 API 等各类工具。全靠 Function Calling 的话工具 schema 会成为 Agent 代码里最难维护的部分。MCP 让 Agent 可以按需连不同的 Server工具来源模块化Agent 的核心逻辑和工具管理完全解耦架构干净很多。一个实用的判断方法碰到「用 Function Calling 还是 MCP」这个选择题其实不需要纠结太久按几个问题依次过一遍就清楚了。首先看社区有没有现成的 MCP Server有的话直接用不要重复造轮子这是最省事的路径。如果没有现成的那就看这个工具需不需要在多个项目或多人之间复用需要复用就选 MCP一次封装到处用。接着综合看工具数量、复杂度、团队规模和变更频率只要规模一上来不一定是数字门槛而是「感觉到散乱」的那个拐点用 MCP 统一管理会比散落在代码各处的 Function Calling 清爽很多。然后考虑你是在做正式的 Agent 系统还是 Demo 原型正式系统选 MCP 更利于长期维护Demo 的话 Function Calling 上手更快。最后别忘了检查部署环境如果平台不允许启动子进程那 Function Calling 就是更稳妥的选择。总结成一句话「只用自己、只用一次、不需要复用」才适合 Function Calling其他情况优先考虑 MCP。两者不是竞争关系MCP 底层本来就是靠 Function Calling 驱动的选哪个取决于你的工程需求而不是技术层面的优劣。