☰
大模型的执行者 Function Calling与MCP协议:从工具调用到TaoToken统一通道的落地实践
2026/10/2 11:42:57 网站建设 项目流程

1. 从“只会聊天”到“动手做事”:Function Calling 与 MCP 到底解决了什么

很多人第一次用大模型 API 时都会有个疑问:模型明明能写代码、能分析问题,为什么让它“查一下今天北京天气”就只会回复“我无法获取实时信息”?原因很简单,纯文本模型本质上是个“语言接龙机器”,它的输出空间被限制在 token 序列里,没法主动去碰外部世界。Function Calling 就是给模型开的一扇门:开发者提前把可用工具用 JSON Schema 描述清楚,模型在对话中判断“这一步该调工具了”,就吐出结构化的函数名和参数,由你的后端去真正执行,再把结果塞回对话让模型整合成自然语言。MCP(Model Context Protocol)则更进一步,它想解决的是“工具多了以后怎么统一管理”的问题——把工具、资源、提示模板抽象成标准协议,让不同客户端(IDE、聊天应用、Agent 框架)都能用同一套方式接入同一批服务端能力。

我试过在同一个项目里既写 Function Calling 又接 MCP Server,最直观的感受是:Function Calling 像“临时叫个外卖”,你每次都得自己定义菜单;MCP 像“公司食堂”,菜单是标准化的,谁来都能点。两者不是替代关系,而是层次不同——Function Calling 是模型侧的能力开关,MCP 是工程侧的集成规范。对于想快速验证“模型能不能调工具”的开发者,Function Calling 是最短路径;对于要做多模型、多工具、长期维护的 Agent 系统,MCP 的上下文标准化和会话状态管理会省掉大量胶水代码。

这篇文章会从零跑通一条完整链路:先写一个最小可用的 Function Calling 请求,再把它包装成 MCP Server 能识别的工具定义,最后用 TaoToken 的统一 Key 和 API 通道完成一次真实的工具调用验证。全程只依赖一个 API Key,不需要在多个厂商控制台之间来回切换。适合已经会写 Python 或 Node.js、想搞明白“模型决策到外部执行”闭环到底怎么落地的人。

2. 前置准备:用 TaoToken 统一 Key 打通多模型调用通道

在写任何工具调用代码之前,得先解决“模型从哪来”的问题。Function Calling 和 MCP 都依赖一个能返回结构化 tool_calls 的模型接口,而不同厂商的请求格式、鉴权方式、模型 ID 命名规则都不一样。如果每个模型都单独申请 Key、单独改 base_url,光是环境变量就能把人搞晕。TaoToken 在这里的角色是统一通道:你拿一个 Key,通过同一个 API 入口就能调用多家模型,请求格式保持 OpenAI 兼容,Function Calling 的 tools 参数、tool_choice 参数都能正常透传。

先注册并拿到 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 只在创建时完整显示一次,关掉页面就看不到了,建议先贴到本地密码管理器里。

接下来确认 API 入口。TaoToken 的 API 基础地址是 https://taotoken.net/api ,所有 OpenAI 兼容的请求都往这个地址发。比如聊天补全的完整路径是 https://taotoken.net/api/v1/chat/completions。如果你用的是 openai Python SDK,只需要把 base_url 改成 https://taotoken.net/api/v1,api_key 填刚才拿到的 Key,其余代码不用动。Node.js 的 openai 包同理,baseURL 设为 https://taotoken.net/api/v1 即可。

模型 ID 方面,TaoToken 支持多家主流模型,具体可用列表可以在控制台的模型页面查看,或者直接调 https://taotoken.net/api/v1/models 拉取。常见的有 qwen-plus、qwen-max、claude 系列、gpt 系列等。Function Calling 对模型能力有要求,建议选支持 tools 参数的模型,qwen-plus 和 claude 系列实测都能稳定返回 tool_calls。如果你不确定某个模型是否支持,可以先发一个带 tools 的请求,看返回里有没有 tool_calls 字段。

环境变量建议这样设置,避免把 Key 硬编码进代码:

export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api/v1"

Python 里读取:

import os from openai import OpenAI client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url=os.environ["TAOTOKEN_BASE_URL"], )

到这里前置就完成了。你不需要单独去申请 qwen 的 DashScope Key、也不需要 claude 的 Anthropic Key,一个 TaoToken Key 就能覆盖后面的所有调用。如果你后面要长期跑编码类 Agent,可以关注 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,里面有适合持续调用的套餐说明。验证模型是否通,可以先用模型对话页面 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 发一条简单消息确认 Key 有效。

3. 可复制配置:Function Calling 请求示例与 MCP Server 工具定义

这一节直接给可运行的代码。先写一个最小的 Function Calling 示例:定义一个查天气的工具,让模型判断是否需要调用,然后执行并回传结果。工具定义用 JSON Schema,这是 Function Calling 的核心——模型靠 description 和 parameters 来决定“这个工具是干什么的、需要哪些参数”。

import json import os from openai import OpenAI client = OpenAI( api_key=os.environ["TAOTOKEN_API_KEY"], base_url=os.environ["TAOTOKEN_BASE_URL"], ) # 1. 定义工具:查天气 tools = [ { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的当前天气,返回温度和天气状况", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如 北京、上海", }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "description": "温度单位,默认摄氏度", }, }, "required": ["city"], }, }, } ] # 2. 模拟工具执行函数 def get_weather(city: str, unit: str = "celsius") -> dict: # 真实场景这里调天气 API,这里返回模拟数据 return {"city": city, "temperature": 26, "unit": unit, "condition": "晴"} # 3. 第一轮请求:让模型决定是否调工具 messages = [{"role": "user", "content": "北京现在天气怎么样?"}] response = client.chat.completions.create( model="qwen-plus", messages=messages, tools=tools, tool_choice="auto", ) msg = response.choices[0].message print("模型返回:", msg) # 4. 如果模型要求调工具,执行并回传 if msg.tool_calls: messages.append(msg) for tool_call in msg.tool_calls: args = json.loads(tool_call.function.arguments) result = get_weather(**args) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": json.dumps(result, ensure_ascii=False), }) # 5. 第二轮请求:模型整合工具结果生成自然语言 final = client.chat.completions.create( model="qwen-plus", messages=messages, tools=tools, ) print("最终回复:", final.choices[0].message.content)

这段代码跑通后,你会看到模型先返回一个 tool_calls,里面包含 get_weather 和 {"city": "北京"},然后第二轮返回“北京当前26度,晴天”。这就是 Function Calling 的完整闭环:模型决策 → 参数生成 → 外部执行 → 结果整合。

接下来把同一个工具包装成 MCP Server 能识别的格式。MCP 的工具定义和 Function Calling 的 JSON Schema 高度相似,但 MCP 要求服务端通过 stdio 或 SSE 暴露能力,客户端通过协议握手发现工具。下面是一个最小 MCP Server 的配置片段,用 Python 的 mcp 包实现,工具定义和上面的 get_weather 保持一致:

{ "mcpServers": { "weather-server": { "command": "python", "args": ["/path/to/weather_mcp_server.py"], "env": { "TAOTOKEN_API_KEY": "sk-你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api/v1" } } } }

对应的 weather_mcp_server.py 核心部分:

from mcp.server import Server from mcp.server.stdio import stdio_server from mcp.types import Tool, TextContent import json app = Server("weather-server") @app.list_tools() async def list_tools(): return [ Tool( name="get_weather", description="查询指定城市的当前天气", inputSchema={ "type": "object", "properties": { "city": {"type": "string", "description": "城市名称"}, "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}, }, "required": ["city"], }, ) ] @app.call_tool() async def call_tool(name: str, arguments: dict): if name == "get_weather": city = arguments["city"] unit = arguments.get("unit", "celsius") result = {"city": city, "temperature": 26, "unit": unit, "condition": "晴"} return [TextContent(type="text", text=json.dumps(result, ensure_ascii=False))] async def main(): async with stdio_server() as (read, write): await app.run(read, write, app.create_initialization_options()) if __name__ == "__main__": import asyncio asyncio.run(main())

这个 Server 启动后,任何支持 MCP 的客户端(比如 Claude Code、Cline、Cursor 的 MCP 插件)都能通过上面的 JSON 配置发现 get_weather 工具。注意 env 里同样用的是 TaoToken 的 Key 和 Base URL,这样 MCP Server 内部如果要调模型做二次决策,也走统一通道。

如果你用的是 Claude Code 这类工具,配置路径通常在 ~/.claude/settings.json 或项目级的 .mcp.json。Claude Code 的接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有完整的 Base URL、Key、Model ID 三件套说明。Cline 的 MCP 配置类似,在 Cline 设置里找到 MCP Servers,粘贴上面的 JSON 即可。Codex 的 auth.json 则需要把 api_key 和 base_url 指向 TaoToken,具体格式参考文档页。

4. 验证请求:用 TaoToken 通道跑通一次真实工具调用

配置写完后必须验证,否则你不知道是模型不支持 tools、还是 Key 没生效、还是 MCP Server 没启动。验证分两步:先确认 TaoToken 通道能返回 tool_calls,再确认 MCP Server 能被客户端发现并调用。

第一步,用 curl 直接打 TaoToken 的 chat completions 接口,带 tools 参数。这样能排除 SDK 封装的干扰:

curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen-plus", "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,结构类似:

{ "id": "call_xxx", "type": "function", "function": { "name": "get_weather", "arguments": "{\"city\": \"上海\"}" } }

如果返回的是普通 content 而没有 tool_calls,说明模型没匹配到工具,检查 description 是否够明确、tool_choice 是否为 auto。如果返回 401,说明 Key 不对或没带 Authorization 头。如果返回 model not found,说明模型 ID 写错了,去控制台确认可用模型列表。

第二步,验证 MCP Server。以 Claude Code 为例,把前面的 mcpServers JSON 写进配置后重启客户端,然后在对话里输入“用 get_weather 查一下深圳天气”。如果配置正确,Claude Code 会显示正在调用 MCP 工具,并返回天气结果。如果客户端提示 “MCP server failed to start”,检查 command 路径是否正确、python 是否在 PATH 里、依赖包 mcp 是否安装。如果提示 “no tools found”,检查 list_tools 是否返回了工具、inputSchema 是否符合 JSON Schema 规范。

第三步,把 Function Calling 和 MCP 串起来验证。在 MCP Server 内部,当 call_tool 被触发时,可以再调一次 TaoToken 的模型接口做参数补全或结果润色。比如用户说“帮我查下天气然后决定穿什么”,MCP Server 先调 get_weather,再把天气结果和用户问题一起发给 qwen-plus,让模型生成穿衣建议。这样一次请求里既有 MCP 的工具发现,又有 Function Calling 的模型决策,完整闭环就跑通了。

实测下来,qwen-plus 在 TaoToken 通道上返回 tool_calls 的稳定性不错,参数 JSON 也基本合法。偶尔会出现 arguments 里多包一层转义的情况,用 json.loads 之前先 strip 一下即可。Claude 系列对工具描述的理解更细,适合参数复杂的场景。如果你要验证模型对话本身是否正常,可以先去 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 发几条消息确认通道畅通。

5. 常见报错排查:401、local proxy failed、reading choices、OAuth

这一节按真实遇到的报错来排。第一个高频错误是 401 Unauthorized。返回体通常是 {"error": {"message": "Invalid API key"}}。原因有三种:Key 复制时带了空格、Key 被删除或过期、请求头没带 Bearer 前缀。排查方法:用 echo $TAOTOKEN_API_KEY 确认环境变量非空,用 curl -H "Authorization: Bearer $TAOTOKEN_API_KEY" https://taotoken.net/api/v1/models 测试 Key 是否有效。如果 models 接口能返回列表,说明 Key 没问题,问题在 chat 请求的 body 或模型 ID。

第二个错误是 local proxy failed 或 connection refused。这通常出现在 MCP Server 启动时,客户端尝试连接本地 stdio 或 SSE 端口失败。检查 MCP 配置里的 command 是否是可执行文件、args 路径是否存在、Python 环境是否装了 mcp 包。如果是 SSE 模式,确认端口没被占用。Windows 下路径要用双反斜杠或正斜杠,避免转义问题。

第三个错误是 reading choices 相关,比如 KeyError: 'choices' 或 reading 'choices' failed。这说明返回体里没有 choices 字段,通常是接口返回了错误但代码没检查。打印完整 response 看 error 字段。常见原因是模型 ID 不支持 tools,或者 tool_choice 设成了具体函数名但模型没匹配到。把 tool_choice 改成 auto 再试。

第四个是 OAuth 相关报错,比如 invalid_grant 或 token expired。如果你用的是 Claude Code 或 Codex 这类带 OAuth 流程的客户端,注意它们可能默认走官方登录,需要手动改成 API Key 模式。Claude Code 的配置里要把 apiKey 和 baseURL 显式指向 TaoToken,参考 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 里的 ClaudeCodeAnthropic 接入说明。Codex 的 auth.json 里要填 "OPENAI_API_KEY" 和 "OPENAI_BASE_URL",base_url 设为 https://taotoken.net/api/v1。

还有一个容易忽略的点:MCP 工具定义里的 inputSchema 如果 required 字段写错,客户端可能能发现工具但调用时报参数校验失败。确保 required 数组里的字段名和 properties 里的 key 完全一致。另外,Function Calling 的 tools 数组里每个 function 的 name 不能有空格或特殊字符,用下划线连接。

如果遇到 429 rate limit,说明短时间内请求过多,TaoToken 通道一般有并发限制,降低频率或换模型即可。如果遇到 500 或 502,先重试一次,持续失败就去控制台看服务状态。排障时建议把请求体和响应体都打到日志里,尤其是 tool_calls 的 arguments 字段,很多时候是 JSON 解析失败而不是模型没返回。

6. 从验证到长期使用:把统一通道接进你的编码工作流

跑通一次工具调用只是起点。真正要长期用,得把 TaoToken 的统一 Key 和 Base URL 固化到你的开发环境里。如果你主要用 Claude Code 做编码,配置路径在 ~/.claude/settings.json,把 env 里的 ANTHROPIC_BASE_URL 指向 https://taotoken.net/api ,ANTHROPIC_API_KEY 填 TaoToken Key,模型 ID 按文档填。这样 Claude Code 的所有请求都走统一通道,不用再单独维护 Anthropic 的 Key。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有 ClaudeCodeAnthropic 的完整字段说明。

如果你用 Cline 或 Continue 这类 VS Code 插件,在设置里找 OpenAI Compatible 或 Anthropic Compatible,Base URL 填 https://taotoken.net/api/v1,API Key 填 TaoToken Key,Model ID 填 qwen-plus 或 claude 系列。Cline 的 MCP 配置直接粘贴第 3 节的 JSON,把 env 里的 Key 换成你的。这样 Cline 既能调模型,又能通过 MCP 调本地工具,两条链路共用一个 Key。

对于要跑批量任务或 Agent 的场景,建议把 Function Calling 的 tools 定义抽成独立的 JSON 文件,MCP Server 的 list_tools 直接读同一份定义,避免两处维护不一致。工具执行函数也抽成独立模块,Function Calling 的本地执行和 MCP 的 call_tool 都调同一个函数。这样你新增一个工具时,只改一处,两条链路同时生效。

长期使用还要关注 Key 的额度。TaoToken 控制台 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite 里能看到用量和余额。如果要做持续编码或 Agent 任务,Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 有适合高频调用的方案。API Keys 管理页 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 可以创建多个 Key 做环境隔离,比如开发用一个、生产用一个,方便排查和轮换。

最后提醒一个实操细节:MCP Server 的 stdio 模式在客户端重启后会重新拉起进程,如果你在 Server 里维护了内存状态(比如缓存),重启会丢。需要持久化的数据写到本地文件或数据库。另外,Function Calling 的 tool_calls 可能一次返回多个,代码里要用 for 循环处理,不要只取第一个。工具执行失败时,把错误信息作为 tool 角色的 content 回传,模型能根据错误调整参数重试,这比直接抛异常给用户友好得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询