
1. 三种调用范式到底在解决什么问题如果你最近在折腾 AI Agent大概率会被三个词反复轰炸MCP、Function Calling、A2A。它们经常被放在同一篇文章里讲但讲着讲着就混成一锅粥——有人以为 MCP 就是 Function Calling 的升级版有人觉得 A2A 是 MCP 的替代品还有人干脆把三者当成同一个东西的不同叫法。我一开始也踩过这个坑。去年做第一个 Agent 项目时我把 MCP Server 配好以为模型就能自动调用工具了结果发现模型根本不知道有这个工具存在得靠 Function Calling 的机制把工具描述塞进 prompt 里模型才会想起来去调。后来又遇到多 Agent 协作的场景两个 Agent 之间怎么互相发现、怎么分配任务又是另一套协议。折腾了一圈才明白这三个东西压根不在一个层面上它们解决的是三个不同的问题。用一句话概括三者的定位Function Calling 解决的是模型怎么决定调用哪个工具——它是模型和工具之间的调用协议本质上是模型输出一段结构化的 JSON告诉外部程序我要调这个函数参数是这些。MCP 解决的是工具怎么被标准化地发现和注册——它是 Agent 和工具之间的发现协议让工具能以统一的方式暴露自己的能力Agent 不用为每个工具写一套适配代码。A2A 解决的是Agent 之间怎么互相发现和分配任务——它是 Agent 和 Agent 之间的协作协议让多个 Agent 能像微服务一样互相调用、传递任务状态。这三个协议不是竞争关系而是互补关系。一个完整的 Agent 系统里三者可以同时存在A2A 负责 Agent 之间的任务分发MCP 负责每个 Agent 发现和注册自己的工具Function Calling 负责模型在具体执行时决定调用哪个工具。这篇文章我会用 TaoToken 的统一 Key 作为观察入口把三种范式的配置和验证步骤都跑一遍。为什么用 TaoToken因为它把多个模型的 API 通道统一成一个 Base URL 和一个 Key这样我们在验证 Function Calling 和 MCP 时不用来回切换不同厂商的 SDK 和鉴权方式能把注意力集中在协议本身的差异上。适合谁看已经写过简单 LLM 调用、想搞清楚 Agent 工具调用底层机制的开发者正在选型 Agent 框架、纠结该用 MCP 还是自己写 Function Calling 的工程师以及想理解多 Agent 协作协议到底怎么落地的技术负责人。接下来我会先讲清楚三种范式的核心差异和适用边界然后给出可复制的配置片段最后用一次 Function Calling 请求和一次 MCP 工具调用的对照验证帮你建立直观认知。2. TaoToken 统一 Key 的前置配置在开始验证三种范式之前我们需要先把 API 通道准备好。这里选择 TaoToken 的原因很简单它提供了一个统一的 Base URL 和 Key兼容 OpenAI 的接口格式同时支持多个模型。这样我们在测试 Function Calling 时可以用一个模型测试 MCP 时换另一个模型但鉴权方式完全一样不用改代码里的认证逻辑。2.1 获取 API Key首先到 TaoToken 控制台创建一个 API Key。地址是https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentapi_keys登录后点击创建 Key复制生成的密钥。这个 Key 就是后面所有请求的统一凭证。2.2 配置 Base URLTaoToken 的 API 入口是https://taotoken.net/api注意这个地址不带任何 UTM 参数是纯粹的 API 端点。所有兼容 OpenAI 格式的请求都往这个地址发。2.3 环境变量配置为了避免 Key 硬编码在代码里建议用环境变量管理。在项目根目录创建.env文件TAOTOKEN_API_KEYsk-你的实际Key TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用的是 Python可以这样读取import os from dotenv import load_dotenv load_dotenv() api_key os.getenv(TAOTOKEN_API_KEY) base_url os.getenv(TAOTOKEN_BASE_URL)2.4 验证通道连通性在正式测试 Function Calling 之前先确认通道是通的。用 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: 回复OK两个字}] }如果返回的 JSON 里有choices字段且内容包含OK说明通道正常。这一步很重要因为后面 Function Calling 和 MCP 的报错有时候会伪装成协议问题实际上是 Key 或 Base URL 配错了。2.5 模型选择建议TaoToken 支持多个模型不同模型对 Function Calling 的支持程度不一样。实测下来模型Function Calling 支持适合场景gpt-4o完整支持参数解析准确复杂工具调用gpt-4o-mini支持偶尔参数格式偏差简单工具、成本敏感claude-3.5-sonnet支持工具描述理解好长上下文工具调用deepseek-chat支持中文工具名友好中文场景如果你要测试 MCP建议用 gpt-4o 或 claude-3.5-sonnet因为 MCP 的工具描述通常比较长需要模型有较强的指令遵循能力。如果只是测试 Function Calling 的基础流程gpt-4o-mini 就够了。配置好这些之后我们就可以进入具体的协议验证了。下一节先讲 Function Calling因为它是另外两个协议的基础——MCP 的工具调用最终也要通过 Function Calling 来触发A2A 的任务分发也依赖模型理解任务描述。3. 可复制的配置片段与三种范式对照这一节给出三种范式在实际项目中的配置片段。我会用同一个 TaoToken Key 贯穿始终方便你对照差异。3.1 Function Calling 的配置Function Calling 的核心是在请求里带上tools参数每个工具用 JSON Schema 描述。下面是一个获取天气的工具定义{ model: gpt-4o-mini, messages: [ {role: user, content: 杭州明天天气怎么样} ], tools: [ { type: function, function: { name: get_forecast, description: 获取指定城市的天气预报, parameters: { type: object, properties: { city: { type: string, description: 城市名称例如杭州 } }, required: [city] } } } ], tool_choice: auto }把这个 JSON 发到https://taotoken.net/api/v1/chat/completions模型会返回一个tool_calls字段里面包含要调用的函数名和参数。你的程序解析这个字段执行实际函数再把结果作为role: tool的消息发回去模型才会生成最终回复。关键点Function Calling 的配置完全在请求体里不需要额外的服务端组件。工具的描述、参数 schema、调用决策全部通过一次 HTTP 请求完成。3.2 MCP 的配置MCP 的配置分两部分MCP Server 的定义和 MCP Client 的连接。以 Cline 为例MCP Server 的配置写在cline_mcp_settings.json里{ mcpServers: { weather: { command: python, args: [mcp_weather_server.py], env: { TAOTOKEN_API_KEY: sk-你的实际Key, TAOTOKEN_BASE_URL: https://taotoken.net/api } } } }这个配置告诉 Cline启动一个叫 weather 的 MCP Server用 python 运行mcp_weather_server.py并把 TaoToken 的 Key 和 Base URL 传进去。MCP Server 本身用 Python 写基于mcp库from mcp.server.fastmcp import FastMCP mcp FastMCP(weather-server) mcp.tool(get_forecast) def get_forecast(city: str) - str: 获取指定城市的天气预报 参数: city: 城市名称 返回: 该城市的天气信息 return f{city}明天有大暴雨 if __name__ __main__: mcp.run(transportstdio)注意 MCP Server 本身不调用大模型它只是把工具注册到 MCP 协议里。真正决定调用哪个工具的还是 Agent 内部的 Function Calling 机制。3.3 A2A 的配置A2A 的配置核心是 Agent Card放在/.well-known/agent.json路径下{ name: weather-agent, description: 负责查询天气的智能体, url: https://your-domain.com/a2a, version: 1.0.0, capabilities: { streaming: true, pushNotifications: false }, skills: [ { id: get_forecast, name: 获取天气预报, description: 根据城市名称返回天气预报, inputModes: [text], outputModes: [text] } ] }其他 Agent 通过读取这个 Agent Card 来发现 weather-agent 的能力然后通过 JSON-RPC 发送tasks/send请求来分配任务。3.4 三种范式的对照表维度Function CallingMCPA2A通信双方模型 ↔ 工具Agent ↔ 工具Agent ↔ Agent协议基础模型厂商自定义JSON-RPCJSON-RPC发现机制请求里带 tools 参数tools/list 方法Agent Card配置位置请求体MCP Server 配置Agent Card状态管理无状态有状态连接保持有状态Task ID适用场景单次工具调用工具生态集成多 Agent 协作这张表是理解三者差异的关键。Function Calling 是一次性的每次请求都要重新带上工具描述MCP 是常驻的Server 启动后工具列表就固定了A2A 是分布式的Agent 之间通过网络互相发现。3.5 配置时的常见坑第一个坑把 MCP Server 的配置和 Function Calling 的 tools 参数搞混。MCP Server 配置在客户端tools 参数在请求体两者不在一个地方。第二个坑A2A 的 Agent Card 路径必须是/.well-known/agent.json这是协议规定的不能改。第三个坑TaoToken 的 Base URL 在 MCP Server 里要用环境变量传入不要硬编码否则 Server 重启后配置就丢了。配置好这些之后下一节我们用实际请求来验证 Function Calling 和 MCP 的调用流程。4. 验证请求与成功结果对照这一节用两个实际请求来验证一次 Function Calling 调用一次 MCP 工具调用。两者都用同一个 TaoToken Key但调用路径完全不同。4.1 Function Calling 验证先构造一个完整的 Function Calling 请求。用 Python 写import os import json from openai import OpenAI from dotenv import load_dotenv load_dotenv() client OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY), base_urlos.getenv(TAOTOKEN_BASE_URL) ) tools [ { type: function, function: { name: get_forecast, description: 获取指定城市的天气预报, parameters: { type: object, properties: { city: { type: string, description: 城市名称 } }, required: [city] } } } ] response client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 杭州明天天气怎么样}], toolstools, tool_choiceauto ) message response.choices[0].message print(模型回复:, message.content) print(工具调用:, message.tool_calls)运行后如果模型决定调用工具message.tool_calls会包含类似这样的内容[ { id: call_abc123, type: function, function: { name: get_forecast, arguments: {\city\:\杭州\} } } ]注意arguments是一个 JSON 字符串需要二次解析。解析后得到{city: 杭州}这就是模型决定传给工具的参数。接下来执行实际函数把结果发回去tool_call message.tool_calls[0] function_args json.loads(tool_call.function.arguments) # 执行实际函数 result get_forecast(function_args[city]) # 把结果发回给模型 second_response client.chat.completions.create( modelgpt-4o-mini, messages[ {role: user, content: 杭州明天天气怎么样}, message, { role: tool, tool_call_id: tool_call.id, content: result } ] ) print(最终回复:, second_response.choices[0].message.content)最终回复会是类似杭州明天有大暴雨的内容。这就是完整的 Function Calling 流程模型决定调用 → 程序执行 → 结果回传 → 模型生成回复。4.2 MCP 工具调用验证MCP 的验证需要先启动 MCP Server然后在 Agent 里触发工具调用。以 Cline 为例配置好 MCP Server 后在对话框输入杭州明天天气怎么样Cline 会自动完成以下流程第一步Cline 的 MCP Client 启动时连接 weather Server发送initialize请求{method:initialize,params:{protocolVersion:2025-06-18,capabilities:{},clientInfo:{name:Cline,version:1.0.0}},jsonrpc:2.0,id:0}Server 返回自己的能力声明{jsonrpc:2.0,id:0,result:{protocolVersion:2025-06-18,capabilities:{tools:{listChanged:false}},serverInfo:{name:weather-server,version:1.0.0}}}第二步Client 查询可用工具{method:tools/list,jsonrpc:2.0,id:1}Server 返回工具列表{jsonrpc:2.0,id:1,result:{tools:[{name:get_forecast,description:获取指定城市的天气预报,inputSchema:{properties:{city:{type:string}},required:[city],type:object}}]}}第三步模型决定调用工具Client 发送调用请求{method:tools/call,params:{name:get_forecast,arguments:{city:杭州}},jsonrpc:2.0,id:2}Server 返回结果{jsonrpc:2.0,id:2,result:{content:[{type:text,text:杭州明天有大暴雨}],isError:false}}4.3 两种调用的关键差异从上面的日志可以看出Function Calling 和 MCP 的差异在于Function Calling 的工具描述在每次请求的tools参数里模型直接看到工具 schema输出tool_calls。整个过程是一次 HTTP 请求加一次函数执行。MCP 的工具描述在 Server 启动时通过tools/list注册Client 把工具列表转换成 Function Calling 的tools参数再发给模型。模型输出的tool_calls被 Client 转换成 MCP 的tools/call请求发给 Server 执行。整个过程是两次协议转换加一次函数执行。换句话说MCP 是在 Function Calling 之上加了一层标准化封装。Function Calling 是模型直接调工具MCP 是模型调 ClientClient 调 ServerServer 调工具。4.4 成功结果的判断标准Function Calling 成功的标志response.choices[0].message.tool_calls不为空且arguments能正确解析。MCP 成功的标志Cline 界面显示工具调用成功且mcp_io.log里有完整的tools/call和返回记录。两者都成功的情况下最终用户看到的回复是一样的。但底层的调用链路完全不同这决定了它们的适用场景简单工具用 Function Calling 就够了工具多了、需要跨项目复用才需要 MCP。5. 本篇常见错误排查这一节列出实际配置过程中最容易遇到的报错以及对应的排查方法。这些报错我都实际遇到过有些坑花了不少时间才定位到。5.1 401 Unauthorized报错信息{error:{message:Invalid API key,type:invalid_request_error}}原因TaoToken 的 Key 没配好或者环境变量没加载。排查步骤先确认.env文件里的TAOTOKEN_API_KEY是完整的没有多余空格。然后在代码里打印一下os.getenv(TAOTOKEN_API_KEY)看是不是 None。如果是 None说明load_dotenv()没生效检查.env文件是否在项目根目录。还有一个容易忽略的点MCP Server 的配置里也要传 Key。如果你在 Cline 的cline_mcp_settings.json里没写env字段MCP Server 启动后调用 TaoToken 时会报 401。5.2 local proxy failed报错信息Error: local proxy failed to connect原因MCP Server 启动失败或者 Client 连不上 Server。排查步骤先手动运行 MCP Server 的命令看能不能正常启动。比如配置里写的是python mcp_weather_server.py就在终端里直接跑这个命令看有没有报错。常见的问题是 Python 依赖没装或者mcp库版本不对。如果 Server 能启动但 Client 还是报 local proxy failed检查cline_mcp_settings.json的路径是否正确。Cline 的工作目录可能和你想的不一样用绝对路径更保险。5.3 reading choices 报错报错信息KeyError: choices原因API 返回的 JSON 里没有choices字段通常是请求格式不对。排查步骤打印完整的 response 内容看返回的是什么。常见的情况是 Base URL 写错了比如写成了https://taotoken.net而不是https://taotoken.net/api。另一个可能是 model 名称不对TaoToken 支持的模型名称和 OpenAI 官方可能不完全一样用之前先确认一下。5.4 OAuth 相关报错报错信息OAuth token expired原因如果你用的是 Claude Code 或类似的工具可能会遇到 OAuth 鉴权问题。排查步骤Claude Code 的配置在~/.claude/settings.json检查里面的apiKey和baseUrl是否正确。如果用的是 TaoToken 的统一 KeybaseUrl应该填https://taotoken.net/apiapiKey填 TaoToken 的 Key。5.5 MCP 工具不显示现象Cline 配置了 MCP Server但工具列表里看不到get_forecast。原因MCP Server 启动失败或者工具注册有问题。排查步骤查看 Cline 的 MCP 日志通常在~/.cline/logs目录下。日志里会显示 Server 启动的详细过程。如果看到tools/list返回空数组说明 Server 里的mcp.tool装饰器没生效检查函数定义是否符合规范。5.6 Codex auth.json 配置如果你用的是 Codex 类工具配置在~/.codex/auth.json{ baseUrl: https://taotoken.net/api, apiKey: sk-你的实际Key, model: gpt-4o-mini }三个字段缺一不可Base URL、Key、Model ID。少任何一个都会报鉴权或模型不存在的错误。5.7 排查通用思路遇到报错时按这个顺序排查先确认 TaoToken 通道是通的用 curl 发一个最简单的对话请求。如果这一步就失败后面都不用看了。再确认工具定义格式正确Function Calling 的tools参数是数组MCP 的mcp.tool装饰器要写在函数上。最后确认调用链路完整Function Calling 需要两次请求一次拿 tool_calls一次回传结果MCP 需要 Client 和 Server 都正常运行。把这几个环节都检查一遍大部分问题都能定位到。6. 按场景选型与后续接入三种范式讲完最后说说怎么选。这不是一个哪个更好的问题而是哪个更适合当前场景的问题。如果你只是想让模型调用一两个外部 API比如查天气、查汇率、发邮件直接用 Function Calling 就够了。配置简单一次请求搞定不需要额外的服务端组件。TaoToken 的统一 Key 让这个过程更省事不用为每个模型厂商写不同的鉴权代码。如果你在做一个工具生态比如想让多个项目复用同一套工具或者想让第三方开发者贡献工具那就上 MCP。MCP 的价值在于标准化工具的描述、注册、调用都有统一协议不用为每个 Agent 框架写适配层。代价是需要维护 MCP Server配置比 Function Calling 复杂一些。如果你在做多 Agent 协作比如一个 Agent 负责规划、一个负责执行、一个负责审核那就需要 A2A。A2A 解决的是 Agent 之间的发现问题每个 Agent 通过 Agent Card 声明自己的能力其他 Agent 读取 Card 后就知道该把什么任务发给谁。这个场景下Function Calling 和 MCP 都不够用因为它们只解决单个 Agent 内部的工具调用。实际项目里三者经常同时出现。一个典型的多 Agent 系统可能是这样的A2A 负责 Agent 之间的任务分发每个 Agent 内部用 MCP 注册自己的工具模型执行时用 Function Calling 决定调用哪个工具。三层协议各司其职互不冲突。如果你想继续深入可以从这几个方向入手想快速验证模型能力用 TaoToken 的模型对话功能直接测试不同模型的 Function Calling 表现https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentmodels想长期做编码类 Agent用 Coding Plan它把常用的编码工具和模型通道都配好了https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentcoding_plan想自己写 MCP Server参考接入文档里的示例代码https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentdoc如果你用的是 Claude Code配置方式略有不同参考这个页面https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentclaude_code最后提醒一点不管用哪种范式Key 的管理都要规范。不要把 Key 硬编码在代码里不要提交到 Git 仓库用环境变量或配置文件管理。TaoToken 的统一 Key 虽然方便但一旦泄露所有模型通道都会受影响。