
1. 从一次工具调用失败说起大模型上下文工程到底难在哪大模型上下文工程Context Engineering这两年从提示词工程里独立出来核心原因就一个模型本身再强它也不知道你公司数据库里有什么、今天天气几度、订单状态到哪一步了。工具使用Tool Use和 MCPModel Context Protocol就是给模型接上外部能力的两种主流方式。我见过太多人卡在同一个地方本地写好了工具函数Schema 也定义了但一到真实请求就报错——要么是tools字段格式不对要么是模型返回了tool_calls但自己不知道怎么把结果塞回去要么是 MCP Server 起来了但客户端连不上。这些问题不是模型能力问题是上下文工程没做对。这篇面向需要为模型接入外部能力的开发者把 Function Calling 和 MCP 两条链路串起来讲。你会看到可复制的 MCP 服务配置片段、工具 Schema 定义示例以及用 TaoToken 统一 Key/API 通道完成一次完整 Function Calling 请求的验证步骤。跑通之后从工具注册到模型调用的整条链路你就清楚了。适合谁看已经会调大模型 API、想给模型加外部能力的后端或全栈开发者正在评估 MCP 要不要接入自己系统的技术负责人以及被各家模型工具调用格式差异折磨过的同学。先说结论Function Calling 是模型厂商各自实现的工具调用协议MCP 是 Anthropic 推动的开放标准两者本质都是把工具描述塞进上下文让模型决定调不调、调哪个、传什么参数。区别在于 MCP 把工具的注册、发现、执行做成了独立服务客户端通过统一协议通信不用为每个模型写一套适配。2. TaoToken 前置准备统一 Key 与 API 通道在动手写工具调用之前先把 API 通道理顺。我试过同时对接多家模型做工具调用最烦的就是每家 Base URL、鉴权头、请求体格式都不一样调试成本极高。TaoToken 的价值在于提供一个统一的 API 入口Key 和 Base URL 一套配置模型 ID 切换即可工具调用的请求结构保持一致。你需要先拿到 API Key。访问 https://taotoken.net/api-keys 创建注意这个页面是控制台里的密钥管理入口。创建后复制保存后面所有请求都用它。Base URL 统一用https://taotoken.net/api注意这个地址不带任何查询参数。模型 ID 按你实际要用的填比如gpt-4o、claude-sonnet-4-20250514这类具体以控制台模型列表为准。如果你用的是 Claude Code 这类编码 Agent配置方式略有不同。Claude Code 通过环境变量读取 Base URL 和 Key你可以在 shell 配置里设置export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEY你的TaoToken Key然后启动 Claude Code 即可。它内部走的是 Anthropic 的 Messages API 格式TaoToken 做了协议兼容工具调用Tool Use的tools字段和tool_use/tool_result消息块都能正常透传。对于 Cline、Cursor 这类支持 MCP 的编辑器配置在各自的 settings 里。以 Cline 为例它的 MCP 配置走cline_mcp_settings.json模型 API 配置走单独的 Provider 设置。这里要区分清楚MCP 配置管的是工具服务怎么连模型 API 配置管的是模型怎么调两者是独立的。一个常见的坑是把 MCP Server 的地址和模型 API 的 Base URL 搞混。MCP Server 是你自己起的本地或远程服务模型 API 是 TaoToken 的地址别填反了。配置完成后建议先用一个最简单的对话请求验证通道是否通curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [{role: user, content: ping}] }返回里有choices[0].message.content就说明通道正常。这一步别跳过后面工具调用报错时你能快速判断是通道问题还是 Schema 问题。3. 可复制配置MCP 服务与工具 Schema 定义这一节给可直接复制的配置。先看 MCP Server 的配置片段以 Claude Code 的.mcp.json为例这个文件放在项目根目录{ mcpServers: { demo-stdio: { command: python, args: [/Users/yourname/projects/mcp-demo/server.py], env: { DEMO_API_KEY: stdio-key-12345, DEMO_ENV: development } }, demo-http: { type: http, url: http://127.0.0.1:8002/mcp, headers: { X-API-Key: http-key-abcde, X-Environment: production } } } }这里有两个 Serverdemo-stdio通过命令拉起走标准输入输出通信demo-http走 Streamable HTTP适合远程或容器化部署。注意type字段stdio 类型不需要写http 和 sse 类型必须显式声明。对应的 MCP Server 代码用 FastMCP 写工具定义如下from fastmcp import FastMCP mcp FastMCP(Demo Server) mcp.tool() def get_weather(city: str) - dict: 查询指定城市的当前天气。 Args: city: 城市名称例如 Beijing Returns: 包含温度和天气状况的字典 return {city: city, temp: 22, condition: sunny} mcp.tool() def add_numbers(a: float, b: float) - float: 计算两个数字之和。 Args: a: 第一个数字 b: 第二个数字 Returns: 两数之和 return a b if __name__ __main__: mcp.run(transporthttp, host127.0.0.1, port8002, path/mcp)启动后MCP Client 会通过tools/list拿到工具列表格式是 JSON-RPC。你可以用 curl 手动验证curl --location http://127.0.0.1:8002/mcp \ --header Accept: application/json, text/event-stream \ --header Content-Type: application/json \ --data { method: initialize, params: { protocolVersion: 2024-11-05, capabilities: {}, clientInfo: {name: curl-test, version: 1.0} }, jsonrpc: 2.0, id: 0 } -i响应头里会有mcp-session-id后续请求都要带上它。这一步是 MCP 握手少了 session id 后面全报错。再看 Function Calling 的工具 Schema这是直接塞进模型请求tools字段的{ type: function, function: { name: get_weather, description: 查询指定城市的当前天气, parameters: { type: object, properties: { city: { type: string, description: 城市名称例如 Beijing } }, required: [city], additionalProperties: false } } }注意additionalProperties: false和required这两个字段它们能显著降低模型传错参数的概率。MCP 的工具定义和这个结构高度对应只是 MCP 走的是inputSchema字段名Function Calling 走的是parameters这是最容易混淆的地方。4. 验证请求用 TaoToken 跑通一次 Function Calling现在把工具 Schema 和 TaoToken 通道结合起来发一次完整的 Function Calling 请求。请求体如下curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [ {role: user, content: 北京今天天气怎么样} ], tools: [ { type: function, function: { name: get_weather, description: 查询指定城市的当前天气, parameters: { type: object, properties: { city: {type: string, description: 城市名称} }, required: [city], additionalProperties: false } } } ], tool_choice: auto }成功返回的关键标志是choices[0].message.tool_calls数组非空里面包含function.name和function.arguments。arguments 是 JSON 字符串需要你自己解析。比如返回{ tool_calls: [ { id: call_abc123, type: function, function: { name: get_weather, arguments: {\city\:\Beijing\} } } ] }拿到之后你在本地执行get_weather(Beijing)得到结果{city: Beijing, temp: 22, condition: sunny}。然后把这个结果作为tool角色的消息追加到上下文再发一次请求curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o, messages: [ {role: user, content: 北京今天天气怎么样}, { role: assistant, tool_calls: [ { id: call_abc123, type: function, function: { name: get_weather, arguments: {\city\:\Beijing\} } } ] }, { role: tool, tool_call_id: call_abc123, content: {\city\: \Beijing\, \temp\: 22, \condition\: \sunny\} } ], tools: [ { type: function, function: { name: get_weather, description: 查询指定城市的当前天气, parameters: { type: object, properties: { city: {type: string, description: 城市名称} }, required: [city], additionalProperties: false } } } ] }这次返回的choices[0].message.content就是模型根据工具结果生成的最终回答类似北京今天晴气温 22 度。到这里从工具注册到模型调用的完整链路就跑通了。如果你用 Python SDK逻辑一样只是用client.chat.completions.create封装。关键点在于tool_call_id必须和上一轮 assistant 消息里的 id 严格对应否则模型会报上下文不一致。5. 常见报错排查401、local proxy failed、reading choices这一节对照真实报错给排查路径。401 Unauthorized最常见。先检查Authorization头是不是Bearer开头Key 有没有多余空格。如果 Key 是从控制台复制的注意别把换行符带进去。还有一种情况是 Key 权限不足去 https://taotoken.net/api-keys 确认 Key 状态是启用。local proxy failed / connection refused这个报错通常出现在 MCP Client 连本地 Server 时。检查 MCP Server 是否真的起来了端口有没有被占用。stdio 类型的 Server 如果启动命令路径写错Client 会直接报拉起失败。http 类型的话用curl http://127.0.0.1:8002/mcp确认端口通不通。另外注意MCP Server 的地址和模型 API 的 Base URL 是两个东西别把https://taotoken.net/api填到 MCP 配置里。reading choices 相关报错一般是响应体解析失败。可能原因有三个一是模型返回了tool_calls但你的代码还在读content导致content为 null 时报错二是流式响应没处理完整choices数组为空三是请求体里tools字段格式不对模型直接返回了错误信息而不是正常结构。建议先关掉流式用非流式请求确认结构再开流式。OAuth / 鉴权失败如果你用的是 Claude Code 或 Codex 这类工具它们可能走 OAuth 流程。检查环境变量ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY是否都设置了且 Base URL 没有多余路径。Codex 的auth.json里如果残留了旧的 token也会导致鉴权冲突清掉重新登录。工具调用返回空 arguments模型知道要调工具但没传参数。检查 Schema 里required是否声明了必填字段description是否足够清晰。描述太模糊时模型可能选择不传参。MCP session id 丢失Streamable HTTP 模式下initialize之后的所有请求都要带Mcp-Session-Id头。漏了会返回 400 或直接断开。建议在客户端封装里统一管理 session id。排查顺序建议先确认模型 API 通道通用最简单的对话请求再确认 MCP Server 单独能通用 curl 打 tools/list最后再串起来。分层排查能省很多时间。6. 继续深入把工具调用接进你的系统跑通单次 Function Calling 只是起点。真实系统里你需要考虑工具的数量管理、执行结果的上下文卸载、以及多模型切换时的兼容性。工具数量一多上下文会被工具定义撑爆。这时候要么做工具分组按需加载要么用 MCP 的 Server 聚合能力把多个工具服务绑到一个入口下。执行结果太长时别原样塞回上下文做摘要或只保留关键字段这是上下文工程里结果卸载的核心动作。多模型场景下MCP 的价值更明显工具定义一次Claude、GPT、Gemini 都能通过各自的 MCP Client 调用不用为每家写一套 Schema 适配。Function Calling 则更适合单模型快速集成、对延迟敏感的场景。如果你要长期做编码 Agent 或自动化流程建议把模型通道固定下来用 TaoToken 的 Coding Plan 统一管理额度和模型切换省得每个项目单独配 Key。接入文档在 https://taotoken.net/doc 有完整的参数说明和示例。想先验证模型对工具调用的支持情况可以直接在模型对话页面试https://taotoken.net/chat 。最后给一个实用技巧调试工具调用时把tool_choice设成required强制模型必须调工具能快速验证 Schema 是否正确。等 Schema 稳定了再改回auto。这个开关在排查模型不调工具的问题时特别有用。