第2篇:Tools 与 MCP —— 让 LLM 从“只会说“变成“能做事“:TaoToken 统一 Key 打通 Function Calling 与 JSON-RPC 实战

发布时间:2026/10/15 22:14:03
第2篇:Tools 与 MCP —— 让 LLM 从“只会说“变成“能做事“:TaoToken 统一 Key 打通 Function Calling 与 JSON-RPC 实战
1. 从“只会说”到“能做事”LLM 工具调用的真实卡点LLM 本身只能做一件事文字进、文字出。你问它“北京到上海今天有哪些航班”它要么编一个看起来很像的答案要么告诉你“我无法获取实时信息”。这不是模型笨是它天生没有手和脚——它不能查数据库、不能读文件、不能发 HTTP 请求。Tools工具就是给 LLM 装上的手脚。但装手脚这件事在 MCP 出现之前和之后是两套完全不同的思路。我见过太多人卡在同一个地方本地写好了 Function Calling 的 schema换一个客户端比如从 Cline 换到 Claude Code就得把适配代码重写一遍工具从 5 个加到 20 个模型开始选错工具调用链直接断掉。这篇要解决的就是这条落地路径用 Tools 定义能力用 Function Calling 触发调用用 MCP 的 JSON-RPC 通信把工具从“模型私有字段”变成“系统协议”最后用 TaoToken 的统一 Key 和 API 通道把多模型调用集中管起来。适合谁正在把 LLM 从聊天框往 Agent 方向推、但被工具集成复杂度卡住的开发者。核心检索词先摆出来LLM Function Calling 与 MCP JSON-RPC 工具调用实战。下面每一步都给可复制的 schema、配置片段和验证命令你跟着做就能确认模型是否真的发起了工具调用。先说清楚 Tools 的本质。Tools 是给 LLM 用的外接能力单元常见的有 Search、Browser、File、Code Run、Database。关键点是LLM 不直接执行工具。它读完工具描述JSON Schema后输出一个标准 JSON告诉程序“该调用哪个函数、传什么参数”。真正干活的是你的程序。一个典型调用过程长这样{function: search, args: {query: 北京到上海航班}}程序收到这个 JSON执行 search 函数把结果返回给 LLMLLM 看到结果后再推理下一步输出下一个 JSON。整个循环里模型只负责“决策”不负责“执行”。写 Tool Schema 的关键是description。LLM 通过阅读描述来推理该用哪个工具所以 description 要写场景而不是功能。不要写“搜索互联网”而要写“当用户需要查找最新信息、实时数据时使用”。这个细节直接决定模型选工具的准确率。纯 Function Calling 的局限也很明显它是各家大模型厂商 API 里的私有字段——OpenAI 叫toolsAnthropic 叫tool_useGemini 叫function_declarations。它跟模型强绑定。如果你有 N 个客户端和 M 个工具需要写 N×M 份适配代码。3 个客户端 × 5 个工具 15 份适配10 个客户端 × 50 个工具 500 份适配。乘法增长规模一大就失控。MCPModel Context Protocol的核心贡献不是“更好的 Function Calling”而是把工具调用从模型能力升维成系统协议。它基于 JSON-RPC 2.0定义了标准握手、能力协商、Client/Server 分离。写一份 Server任意 Client 都能复用复杂度从 N×M 降到 NM。还是 10 客户端 × 50 工具500 份变成 60 份。MCP 的工作结构是这样的MCP HostAI 应用 └─ MCP Client协议转换 └─ MCP Server 1搜索 └─ MCP Server 2文件操作 └─ MCP Server 3数据库一次典型的 MCP 调用initialize握手确认双方能力→tools/listClient 问你能提供什么工具→tools/callClient 说调用 search参数是...→ 结果返回JSON-RPC 格式。这跟 HTTP 的工作方式如出一辙——握手建立连接、列出资源、请求资源、返回响应。这里有个反直觉的结论MCP 不是 Function Calling 的升级版它做了相反的事——把工具调用从模型 API 特有的魔法行为降级为普通的网络协议请求。任何语言、任何框架、任何模型只要实现了 JSON-RPC 2.0就能接入 MCP。这是它最核心的价值。2. TaoToken 前置统一 Key 与 API 通道怎么准备在写 Tools schema 和 MCP 配置之前先把调用通道准备好。为什么需要这一步因为 Function Calling 和 MCP 的调用链里模型请求是高频且多模型的——你可能用 Claude 做工具决策用另一个模型做结果总结。如果每个模型都单独配 Key、单独记 Base URL排障时会疯掉。TaoToken 在这里的角色是统一 Key 和 API 通道一个 Key 管多模型调用Base URL 统一省去在多个客户端里反复填不同厂商地址的麻烦。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 地址是 https://taotoken.net/api 这个不加 UTM。你需要准备三件套这三件套在后面每个客户端配置里都会出现配置项值说明Base URLhttps://taotoken.net/api所有请求的统一入口API Key在控制台生成形如sk-...只显示一次Model ID按需选择如claude-sonnet-4-5、gpt-4o等生成 Key 的路径进入控制台 → API Keys → 新建。这里有个坑Key 只在创建时完整显示一次关掉页面就看不到了所以生成后立刻复制到安全的地方。如果你要长期跑编码类 Agent建议直接看 Coding Plan它比按量计费更适合高频工具调用场景。模型对话入口可以用来快速验证 Key 是否可用不用写代码就能发一条测试消息。接入文档里有各客户端的详细配置说明遇到不确定的字段名先去那里对一遍。为什么强调“统一通道”因为 MCP 的调用链里模型请求和工具执行是分开的。模型请求走 TaoToken 的 API工具执行走你本地的 MCP Server。如果模型请求这一层用了多个厂商的 Key一旦工具调用失败你很难判断是模型没返回正确的 tool_use还是 Key 权限问题还是 Base URL 写错了。统一通道把变量减少到一个排障效率完全不一样。还有一点Function Calling 对模型的指令遵循能力有要求。不是所有模型都能稳定输出符合 schema 的 JSON。选模型时优先选工具调用能力强的别为了省钱选一个连 JSON 格式都守不住的模型后面调工具选择准确率会让你怀疑人生。准备好这三件套后先别急着写 MCP Server。先用一个最小的 Function Calling 例子验证通道是通的——这是下一节的内容。3. 可复制配置Tools Schema 与 MCP Server 片段这一节给可直接复制的配置。先给 Function Calling 的 Tools schema再给 MCP Server 的配置片段最后给客户端接入的三件套写法。3.1 Tools Schema一个搜索工具的完整定义下面是一个符合 OpenAItools字段格式的 schema你可以直接放进请求体{ tools: [ { type: function, function: { name: search_flights, description: 当用户需要查询两个城市之间的实时航班信息、起飞时间、价格时使用。不适用于历史航班或航班政策咨询。, parameters: { type: object, properties: { from_city: { type: string, description: 出发城市例如北京 }, to_city: { type: string, description: 到达城市例如上海 }, date: { type: string, description: 出发日期格式 YYYY-MM-DD } }, required: [from_city, to_city, date] } } } ] }注意description的写法写的是“当用户需要查询……时使用”而不是“搜索航班”。这个差别在模型选工具时非常关键。required字段也要写全否则模型可能漏传参数。3.2 MCP Server 配置片段MCP Server 的配置通常放在客户端的配置文件里。以常见的mcp.json或客户端 settings 为例一个 stdio 类型的 MCP Server 配置长这样{ mcpServers: { flight-search: { command: node, args: [/path/to/your/mcp-server/index.js], env: { API_BASE: https://taotoken.net/api, API_KEY: sk-你的Key } } } }如果你用的是支持 TOML 的客户端等价写法是[mcp_servers.flight-search] command node args [/path/to/your/mcp-server/index.js] [mcp_servers.flight-search.env] API_BASE https://taotoken.net/api API_KEY sk-你的Key这里command和args指向你的 MCP Server 启动脚本。env里放的是 Server 内部调用模型时用的通道信息。注意MCP Server 本身不一定要调模型但如果你的 Server 内部有二次推理逻辑就需要这三件套。3.3 客户端接入三件套无论你用 Claude Code、Cline 还是其他支持 MCP 的客户端接入时都要填全三件套。以 Claude Code 的配置为例settings 文件{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-sonnet-4-5 } }Cline 的 MCP 配置里Base URL、Key、Model ID 三个字段一个都不能少。Codex 的auth.json同理{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: gpt-4o }三件套缺一不可Base URL 决定请求发到哪Key 决定有没有权限Model ID 决定用哪个模型做工具决策。少任何一个调用链都会在第一步就断掉。配置写完后先别急着跑完整调用链。下一节用一条最小请求验证通道和工具调用是否正常。4. 验证请求一次完整调用链的成功结果这一节用 curl 发一条带 tools 的请求确认模型能正确返回 tool_use。这是整条链路里最关键的验证点——如果模型不返回工具调用后面 MCP 配得再好也没用。4.1 发一条带 Tools 的请求curl https://taotoken.net/api/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer sk-你的Key \ -d { model: claude-sonnet-4-5, messages: [ {role: user, content: 帮我查一下明天北京到上海的航班} ], tools: [ { type: function, function: { name: search_flights, description: 当用户需要查询两个城市之间的实时航班信息时使用, parameters: { type: object, properties: { from_city: {type: string}, to_city: {type: string}, date: {type: string} }, required: [from_city, to_city, date] } } } ] }4.2 期望的成功结果如果通道和 schema 都正确返回体里会出现tool_calls字段类似{ choices: [ { message: { role: assistant, tool_calls: [ { id: call_abc123, type: function, function: { name: search_flights, arguments: {\from_city\:\北京\,\to_city\:\上海\,\date\:\2025-01-16\} } } ] } } ] }看到tool_calls就说明模型正确发起了工具调用。注意arguments是一个 JSON 字符串需要二次解析。日期是模型根据“明天”推算的实际使用时建议在 prompt 里给明确的日期减少模型推算错误。4.3 把工具结果回传给模型拿到 tool_calls 后你的程序执行 search_flights然后把结果作为tool角色消息回传{ role: tool, tool_call_id: call_abc123, content: {\flights\:[{\flight\:\MU5101\,\depart\:\08:00\,\price\:1200}]} }再发一次请求模型会基于工具结果生成自然语言回复。到这里一次完整的 Function Calling 调用链就跑通了。4.4 MCP 侧的验证MCP 的验证方式不同它走 JSON-RPC。你可以手动发一条tools/list请求确认 Server 正常{jsonrpc:2.0,id:1,method:tools/list,params:{}}正常返回会列出 Server 暴露的所有工具。再发tools/call{jsonrpc:2.0,id:2,method:tools/call,params:{name:search_flights,arguments:{from_city:北京,to_city:上海,date:2025-01-16}}}返回结果里会有content数组。如果这两步都通说明 MCP Server 的 JSON-RPC 通信正常客户端可以正常发现和调用工具。实测下来最容易出问题的不是 schema 本身而是通道配置和模型选择。下一节把常见报错逐个拆开。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错给排查路径。每个报错都对应调用链上的一个具体环节。5.1 401 Unauthorized最常见。原因通常是 Key 没填、填错、或者 Base URL 和 Key 不匹配。检查顺序先确认Authorization: Bearer sk-...里的 Key 完整再确认 Base URL 是https://taotoken.net/api没有多余斜杠或路径最后去控制台确认这个 Key 还有效、没被删除。如果是在客户端里报 401检查配置文件里的字段名是否正确。比如 Claude Code 用的是ANTHROPIC_API_KEYCline 可能用apiKey字段名写错等于没填。5.2 local proxy failed这个报错通常出现在客户端尝试连接本地 MCP Server 时。原因可能是MCP Server 的command路径写错、args指向的文件不存在、或者 Server 启动就崩了。排查方法先在终端手动执行command args那串命令看 Server 能不能正常启动。如果手动启动报错先修 Server如果手动能启动但客户端报错检查客户端的 MCP 配置路径是不是绝对路径。还有一种情况是端口冲突。如果 MCP Server 用的是 HTTP/SSE 模式而不是 stdio端口被占用也会报这个错。换一个端口再试。5.3 reading choices 相关报错这个报错说明请求发出去了但返回体里没有choices字段。常见原因模型名写错比如写了一个不存在的 Model ID或者请求体格式不对比如messages字段缺失。检查 Model ID 是否在 TaoToken 支持的模型列表里检查请求体 JSON 是否合法。如果返回体里有error字段先看 error message。很多“reading choices”的根因其实是上游返回了错误客户端没处理好就直接去读 choices 了。5.4 OAuth 相关报错如果你用的是 Claude Code 这类带 OAuth 流程的客户端可能会遇到 OAuth 报错。这类报错通常和 Base URL 配置有关——客户端可能默认走官方 OAuth 流程而不是走你配置的 API 通道。检查配置里是否同时设置了ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY并且确认客户端版本支持自定义 Base URL。如果客户端强制走 OAuth考虑换用支持 API Key 直连的客户端或者查接入文档里对应客户端的配置方式。5.5 工具调用返回空模型返回了tool_calls但arguments是空对象或者模型直接返回文本而没有调用工具。原因通常是description写得太模糊模型判断“不需要调用工具”。把 description 改成明确的场景描述比如“当用户询问实时航班时使用”再试一次。另一个原因是模型本身工具调用能力弱。换一个工具调用能力强的 Model ID对比结果。排障时记住一个原则先验证通道再验证 schema最后验证模型。通道不通后面都是白搭。6. 把统一 Key 用起来从验证到长期编码走到这里你已经有了可复制的 Tools schema、MCP Server 配置片段、三件套写法也知道了常见报错怎么排查。接下来是怎么把这套东西用起来。如果你只是偶尔验证工具调用用模型对话入口发几条测试消息就够了不用配客户端。如果你要长期跑编码类 Agent或者工具数量会持续增加建议看 Coding Plan——高频工具调用场景下按量计费的波动会很大包月方案更可控。接入文档里有各客户端的完整配置示例遇到字段名不确定的时候先去那里对一遍比在报错里猜要快得多。API Keys 页面用来管理你的 Key建议给不同客户端生成不同的 Key这样某个客户端出问题时能快速定位是哪个 Key 的配置有问题。最后给一个实用技巧MCP Server 的tools/list返回的工具数量不要超过 15 个。前面提过工具从 10 个扩展到 200 个时模型选择准确率会从 95% 跌到 41%。如果你的 Server 暴露了太多工具考虑用 Skill 把多个工具的调用顺序打包成一个命名概念让模型选 Skill 而不是从几十个工具里挑。工具调用这件事配通一次之后就是复制粘贴。真正花时间的是排障——而排障效率取决于你的通道是否统一。统一 Key 和 Base URL 之后变量少了问题定位就快了。