1. 面试被追问 Function Call 和 MCP 到底差在哪
面试里被问到「Function Call、Skill、MCP 有什么区别」,很多人背得出「一个管怎么调、一个管调什么、一个管按什么规矩调」这三句话,但面试官只要再追一句「你实际写过吗?请求长什么样?」就露馅了。概念背得再顺,落到代码上说不清 endpoint、参数结构、返回字段,面试官基本就判定你只是看过文章。
这篇不打算再复述一遍定义,而是带你用一套统一的 Key 和 API 通道,把 Function Call 和 MCP 各跑一次真实请求。跑通之后你会发现,这两个概念在代码层面长什么样、返回的 JSON 里哪个字段代表「模型决定调工具」、MCP 的握手过程又发生在哪一步,全都变得具体了。面试时你能直接说「我实测过,Function Call 返回的 tool_calls 字段里……」,这比背定义有说服力得多。
适合谁看:正在准备后端或 AI 应用岗面试、知道这几个词但没动手跑过、想要一份能照着复现的最小示例的人。全程只需要一个 API Key、一个能发 HTTP 请求的环境(curl 或 Python 都行),不需要 GPU,不需要本地部署模型。
我试过把 Function Call 和 MCP 放在同一个 Key 下验证,好处是省去了分别注册、分别记 endpoint 的麻烦,注意力能集中在「请求结构」和「返回结构」这两个面试真正会问的点上。下面从拿到统一 Key 开始,一步步来。
2. TaoToken 统一 Key 与 API 通道准备
要复现后面的调用,先得有一个能同时访问对话模型和工具调用能力的入口。TaoToken 提供的是 OpenAI 兼容的 API 通道,也就是说你原来写openaiSDK 的那套代码,改一下base_url和api_key就能直接用,不用重学一套 SDK。这对面试复习特别友好——你练的就是业界通用的请求格式,面试官问起来你答的也是通用结构,不是某个私有平台的方言。
先注册并拿到 Key。打开官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,完成账号注册后进入控制台。控制台地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在里面找到 API Keys 管理页,新建一个 Key。Key 一般以sk-开头,创建后只显示一次,复制下来存到环境变量里,别直接写死在代码里提交到仓库。
API 的基础地址是 https://taotoken.net/api ,注意这个地址后面不加任何查询参数,SDK 会自动在它后面拼/v1/chat/completions这类路径。如果你用 curl 手写请求,完整地址就是https://taotoken.net/api/v1/chat/completions。
把 Key 写进环境变量,Linux/macOS 下:
export TAOTOKEN_API_KEY="sk-你的Key"Windows PowerShell:
$env:TAOTOKEN_API_KEY="sk-你的Key"验证 Key 是否可用,最省事的方式是先用模型对话页发一条消息。模型对话入口在 https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite ,进去选一个支持工具调用的模型(比如 GPT 系列或 Claude 系列里带 function calling 能力的版本),随便问一句「你好」,能正常返回就说明 Key 和通道都没问题。
这里有个面试常被问的点:为什么用 OpenAI 兼容格式?因为 Function Call 最早就是 OpenAI 在 Chat Completions 接口里引入的,tools/tool_calls这套字段已经成了事实标准,很多模型和框架都跟进兼容。你练熟这套格式,换任何兼容平台都能迁移,面试时也能讲清楚「这是行业通用约定,不是某家私有」。
Key 准备好之后,先别急着写 Function Call。建议在控制台里确认一下账户余额和可用模型列表,避免后面请求报 401 或模型不存在时,分不清是 Key 问题还是模型名写错。这一步花两分钟,能省掉后面大量排障时间。
3. 可复制的 Function Call 与 MCP 配置片段
这一节给你可以直接抄的配置。先明确一个原则:Function Call 和 MCP 在「请求层」的差异,主要体现在工具是怎么声明的。Function Call 是把工具定义直接塞进请求体的tools数组;MCP 则是先有一个独立的服务端声明它提供哪些工具,客户端再去拉取这份清单。理解了这个差异,面试时你就能一句话点破本质。
先看 Function Call 的请求体结构。下面是一段可以直接用的 JSON,描述了一个查询天气的工具:
{ "model": "gpt-4o-mini", "messages": [ {"role": "user", "content": "北京今天天气怎么样?"} ], "tools": [ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的天气信息", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如 北京" } }, "required": ["city"] } } } ], "tool_choice": "auto" }关键字段解释:tools是工具清单,每个工具用 JSON Schema 描述参数;tool_choice设为auto表示让模型自己决定要不要调工具。面试官如果问「模型怎么知道有哪些工具」,答案就是这段tools数组——它是你在请求里显式告诉模型的。
如果你用 Python 的openaiSDK,配置片段是这样:
from openai import OpenAI client = OpenAI( api_key="sk-你的Key", base_url="https://taotoken.net/api" ) tools = [ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的天气信息", "parameters": { "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"} }, "required": ["city"] } } } ]注意base_url写的是https://taotoken.net/api,SDK 会自动补全路径。这是最容易写错的地方,很多人把/v1也写进去,结果变成/api/v1/v1/...报 404。
再看 MCP 侧。MCP 的配置通常是一个描述服务端的 JSON,客户端根据它去连接并拉取工具清单。一个典型的 MCP 服务端配置片段长这样:
{ "mcpServers": { "weather-server": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-weather"], "env": { "API_KEY": "sk-你的Key" } } } }这段配置的意思是:启动一个名为weather-server的 MCP 服务端进程,客户端通过标准输入输出和它通信,服务端启动后会声明自己提供哪些工具。和 Function Call 最大的区别在于——工具清单不是写死在请求里的,而是服务端动态声明的。这就是 MCP 想解决的「标准化」问题:客户端不用预先知道有哪些工具,连上服务端问一下就行。
如果你在 Claude Code 这类工具里接入 MCP,配置会写进对应的 settings 文件,路径和字段名要按工具要求来。核心三件套始终是:服务端启动命令(command + args)、环境变量(env,放 Key)、以及服务端标识名。把这三样配对,MCP 就能连上。
4. 验证请求与预期返回结果
配置写完,跑一次真实请求看返回。先验证 Function Call。用 curl 发请求:
curl https://taotoken.net/api/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "北京今天天气怎么样?"}], "tools": [{ "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的天气信息", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } } }], "tool_choice": "auto" }'预期返回里,choices[0].message会多出一个tool_calls字段,结构大致是:
{ "choices": [{ "message": { "role": "assistant", "content": null, "tool_calls": [{ "id": "call_abc123", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\": \"北京\"}" } }] }, "finish_reason": "tool_calls" }] }看到finish_reason是tool_calls、arguments里是{"city": "北京"},就说明模型正确识别了意图并生成了结构化调用指令。注意arguments是一个 JSON 字符串,不是对象,用的时候要再json.loads一次——这是面试高频细节,很多人栽在这。
拿到这个返回后,你的后端代码要真正去执行get_weather("北京"),把结果作为一条role: "tool"的消息追加回messages,再发一次请求,模型才会基于真实天气数据生成自然语言回答。这个「两轮请求」的流程是 Function Call 的核心,面试时能讲清楚这两轮,基本就过关了。
再验证 MCP。MCP 的验证方式和 Function Call 不同,它不是发一个 HTTP 请求,而是启动服务端进程后,客户端和服务端之间做一次初始化握手,然后客户端调用tools/list拉取工具清单。如果你用支持 MCP 的客户端(比如 Claude Code),接入后可以在客户端里看到服务端声明的工具列表。预期结果是:客户端能列出weather-server提供的工具,并且能调用其中某个工具拿到返回。
MCP 握手成功后,客户端会拿到类似这样的工具声明:
{ "tools": [{ "name": "get_weather", "description": "获取指定城市的天气信息", "inputSchema": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } }] }对比一下就能看出:Function Call 的parameters和 MCP 的inputSchema描述的是同一件事——工具需要什么参数。区别只是 MCP 把它标准化成了协议的一部分,任何支持 MCP 的客户端都能读懂,不用为每个工具单独写适配。这就是面试里「MCP 解决按什么规矩调」的代码级证据。
5. 本篇常见报错排查
跑不通是常态,下面这几个报错我基本都踩过,对照着查能省不少时间。
401 Unauthorized:最常见。先确认Authorization头是不是Bearer sk-xxx格式,中间有空格;再确认 Key 没复制错、没多带换行;最后确认环境变量在当前终端真的生效了(echo $TAOTOKEN_API_KEY看一眼)。如果 Key 是对的还报 401,去控制台看下 Key 是不是被禁用或余额不足。
local proxy failed / connection refused:这类报错通常是本地网络或代理配置问题。检查你的base_url是不是写成了https://taotoken.net/api,有没有误加/v1导致路径重复。如果你本地配了 HTTP 代理,确认代理没有拦截这个域名。注意不要使用任何非正规的网络访问方式,正常直连即可。
reading choices 报错 / KeyError: 'choices':说明返回体里没有choices字段,多半是请求本身失败了,返回的是错误 JSON。先把原始返回print出来看,别直接取choices。常见原因是模型名写错(比如写了个不存在的模型),或者tools数组格式不对导致 400。
OAuth / 认证相关报错:如果你在 Claude Code 或类似工具里接 MCP,报 OAuth 或认证失败,检查 MCP 配置里的env有没有把 Key 正确传进去,以及服务端启动命令command和args是否可执行(npx是否装了、包名对不对)。MCP 服务端启动失败时,客户端往往只报一个笼统的连接错误,实际原因要看服务端进程的日志。
tool_calls 为空 / 模型不调工具:模型返回了正常回答但没触发工具调用。检查tool_choice是不是auto,工具描述description是否清晰(描述太模糊模型可能判断不需要调),以及你用的模型是否支持 function calling。有些轻量模型不支持工具调用,换一个支持的重试。
arguments 解析失败:arguments是字符串不是对象,直接当 dict 用会报错。记得json.loads(tool_call.function.arguments)。另外模型偶尔会生成不完整的 JSON,生产环境要加 try/except 兜底。
排查时记住一个顺序:先看 HTTP 状态码,再看原始返回体,最后才看业务字段。很多人一上来就盯着choices看,其实问题出在更前面。
6. 面试前动手复现与进阶入口
把上面两段跑通,你对 Function Call 和 MCP 的理解就不再是纸面上的了。面试时被问到,你可以这样组织回答:Function Call 是在单次请求里通过tools数组声明工具、模型返回tool_calls结构化指令、后端执行后再回填结果的两轮流程;MCP 是把工具声明标准化成协议,客户端连上服务端动态拉取工具清单,inputSchema和 Function Call 的parameters描述的是同一件事,只是前者成了通用规范。Skill 则是工程上把一组相关 Function 打包的组织方式,介于两者之间。
想继续练,可以往两个方向走。一是把 Function Call 的多工具、并行调用、错误重试这些边界情况都跑一遍,面试常问「模型一次返回多个 tool_calls 怎么办」。二是把 MCP 服务端真正接进一个编码 Agent 里,体验一下工具动态发现的过程。长期做编码和 Agent 方向的话,Coding Plan 入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,适合需要持续调用、反复调试的场景。
如果你只是想先把概念验证清楚,用模型对话页快速试几条请求就够了:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。需要新建或管理 Key 时去控制台:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 。API Keys 管理页在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,遇到字段细节可以直接查文档。
最后给个实用建议:面试前把这两段请求的返回 JSON 截图存下来,面试时如果被追问细节,你脑子里有真实的字段结构,比临时编要稳得多。概念会忘,但跑过一次的请求结构很难忘。