
1. Agent 调外部工具的标准动作从开发接口到读取 tool_use当我让 Claude“帮我画一只小猫”它只会道歉说没有绘画能力。要让大模型真正动手得走 Agent 的工具调用流程。这流程本身不复杂但要把 Key、Base URL、模型 ID 都对齐很麻烦。我的做法是先去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 拿一个 TaoToken 的 API Key再把 Agent 代码里的大模型地址换成统一的接口剩下的事情和官方流程一模一样。具体怎么做我们可以把 Agent 工具调用拆成六步。这个流程不限于某一个模型也不依赖某个特定的 SDK核心是让模型返回 tool_use然后由你的程序去执行真实工具。1.1 大模型缺少的不是智能而是工具入口大模型的训练目标决定了它只能预测下一个 token所以它天然不具备“执行”的能力。你说“帮我订一张机票”它最多给你一段订票步骤的文字不会真的去连航空公司系统。这时候需要我们在模型之外提供一个函数模型只需要输出“调用这个函数参数是这些”剩下的动作由外部代码完成。这就是 Agent 工具调用的雏形模型负责决策程序负责执行。理解这一点后你会发现 Agent 不是什么神秘技术它只是在模型返回里多了一个“我决定用某个工具”的信号。1.2 六步调用流程第一步开发一个画图接口。这个接口接收一个 prompt 参数内部调用 Midjourney、Stable Diffusion 或其他绘画 API最后返回一个图片地址。第二步把这个接口用 JSON 描述出来接口叫什么、接收什么参数、返回什么结构全部用自然语言写在 description 里。第三步在调用大模型时把这段 JSON 放进请求体的 tools 字段。第四步当你再让模型“画一只小猫”时模型不会直接回图而是返回一个 tool_use 结构里面带着它选中的工具名和参数。第五步你的程序解析这个结构调用真实的画图接口。第六步拿到图片后你可以直接把图片丢给用户也可以把结果再喂回大模型让它用自然语言补充一句“这是你的小猫”。整个链路里模型始终没有直接接触外部系统它只是输出“我想用什么工具、参数是什么”。真正的执行力在你自己写的代码里。这也是 Agent 和普通聊天最本质的区别聊天只调模型Agent 会在模型返回 tool_use 后继续执行工具然后把结果带回对话。注意这一步很关键。你不需要把工具的逻辑写进提示词而是要以结构化的 tools 形式传给模型。如果你只是把工具说明写在 system prompt 里模型可能理解但不会稳定地输出结构化参数。你需要的是让模型在返回内容里明确给出 tool_use程序才能安全地解析和调用。1.3 为什么“工具”和“智能体”都叫 Agent我第一次看这个概念时也被绕晕。原文里解释过Agent 有两个意思当它指代单个工具时就是“一个可供大模型调用的函数”当它指代一个应用时是“能根据模型大脑自主决策去执行多个工具、形成链式调用的智能体”。我们前面六步里说的 Agent更接近“工具”而你的整个脚本如果能在不同步骤自动选择不同工具就属于“智能体”。理解这个区别后再去看 MCP 就顺了MCP 其实由 MCP-Client调用端和 MCP-Server工具端组成。Cursor、Cline、Claude 客户端以及你写的 Agent harness都可以是 MCP-Client画图、天气、搜索这些工具则作为 MCP-Server 存在。2. MCP 是 Type-C但 Key 和 Base URL 还各自为政上面这套流程能跑通但有一个很现实的问题不同家大模型的 tools 字段格式略有差异。OpenAI 叫 functionsAnthropic 叫 tools参数结构也不完全一样。你为 Claude 写的工具描述搬到另一个模型上可能要改字段名。于是 MCPModel Context Protocol出现了它把这套工具定义统一成一份标准协议类似手机接口从 Lightning、Micro-USB 统一到 Type-C。2.1 MCP 把工具定义变成标准协议MCP 的价值在于“约定”。以前写一个天气工具你要为 OpenAI 写一份 description为 Claude 再写一份字段名可能还是 function_call 和 tool_use 的区别。现在只要你的工具按照 MCP 协议实现成 MCP-Server无论是哪家模型客户端都能用同样的方式发现工具、调用工具。工具本身彻底和模型解耦。这是 MCP 作为 Type-C 的真正含义协议统一了工具描述和传输格式不再为每个模型单独适配。2.2 MCP-Client 和 MCP-Server 各是什么具体拆开看MCP-Server 是提供能力的一方它把“画图”“搜索”“操作 Blender”封装成标准工具MCP-Client 是消费能力的一方它连接到 Server读取工具列表再把工具定义传给大模型。我们熟知的 Cursor、Cline、Claude 桌面客户端都内置了 MCP-Client。你自己也可以写一个不到 200 行的 MCP-Client官方文档在 modelcontextprotocol.io 上有示例。对于 Agent 开发者来说你甚至不需要自己实现协议很多语言都有现成的 MCP SDK几行代码就能拿到一个 MCP Server 暴露出的所有工具定义。2.3 接入层的碎片化一个模型一套 KeyMCP 统一了工具定义但模型接入的碎片化依然存在。你手上可能同时有 OpenAI、Claude、Gemini 的账号每个控制台申请的 Key 不一样Base URL 也不一样。Agent 脚本里往往写死了一个 provider 的地址想换模型就得改环境变量、换 Key甚至改 SDK 版本。长会话、多工具的场景下这种切换成本会被放大画图用一个模型搜索用另一个模型每个模型的 Key 和地址都不同编排代码里塞满各种模型名和密钥。这也是我引入 TaoToken 的原因。TaoToken 是一个统一的 API 兼容通道它把不同模型的接口收敛成同一个 Base URL 和同一个 Key。你不需要在每个控制台都申请一遍只需要在 Agent 代码里把大模型地址指向 TaoToken 的接口。模型 ID 按需切换Key 始终是同一个。这样做的好处是你的工具定义按 MCP 标准写模型通道由 TaoToken 统一承担换模型时不需要重新申请 Key也不用改 Base URL。3. 用 TaoToken 把 Agent 的 MCP 工具调用接进同一个通道当你理解了 Agent 的 tool_use 机制和 MCP 的统一工具定义之后剩下的工作就是把这套流程接到一个稳定的模型通道上。这里我把 TaoToken 作为统一入口配置一份可运行的 Agent 调用示例。3.1 准备材料Key 从官网拿接口地址填 api先打开 TaoToken 注册账号在控制台创建一个 API Key复制下来。注意官网只用来注册、创建 Key、查看模型广场和用量真正填进 Agent 代码的接口地址是https://taotoken.net/api末尾不要加/v1。模型 ID 不要凭记忆写去官网模型广场看当前可用的模型 ID再填到配置里。这一分离很值得强调官网落地页是给人点的接口地址是给程序用的。如果你不小心把https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end当成 Base URL 填进代码请求会失败反过来在浏览器里打开https://taotoken.net/api也看不到页面。很多第一次接入的朋友都栽在这里。3.2 最小 Agent 代码Base URL 指向 TaoToken下面用 Python 写一个最小的 Agent harness。它不做复杂编排只演示一件事把 TaoToken 作为大模型通道传一个工具定义然后读取返回里的 tool_use。import requests import json API_KEY YOUR_API_KEY # 从官网控制台创建 BASE_URL https://taotoken.net/api # 注意末尾不要加 /v1 MODEL_ID 你的模型ID # 以 TaoToken 模型广场为准 # 这个工具定义模仿 MCP-Server 暴露给客户端的能力 tools [ { type: function, function: { name: draw_cat, description: 根据一句话描述生成猫咪图片, parameters: { type: object, properties: { prompt: { type: string, description: 猫咪的画面描述例如一只橘猫坐在窗台上 } }, required: [prompt] } } } ] messages [ {role: user, content: 帮我画一只小猫} ] payload { model: MODEL_ID, messages: messages, tools: tools } headers { Authorization: fBearer {API_KEY}, Content-Type: application/json } response requests.post(f{BASE_URL}/chat/completions, jsonpayload, headersheaders) result response.json() # 解析 tool_use if choices in result: choice result[choices][0] message choice.get(message, {}) if tool_calls in message or tool_use in message: # 兼容不同命名 tool_call message.get(tool_calls) or message.get(tool_use) print(模型要调用工具, tool_call) else: print(模型直接回答, message.get(content)) else: print(请求失败, result)这里有两个需要注意的地方。第一BASE_URL是https://taotoken.net/api不是https://taotoken.net/api/v1也不是官网地址。第二MODEL_ID不能照抄我的占位符去官网模型广场找到你需要的模型 ID 再填。如果你本来就是在写 MCP-Client工具定义可以直接由 MCP Server 生成不需要手写上面的 JSONTaoToken 只负责模型请求这一层。为了让这段代码更贴近真实项目你可以把 API Key 放到环境变量里不要硬编码。上面的例子为了演示统一读写把变量写在了一起实际使用时建议用os.getenv(TAOTOKEN_API_KEY)。这样就不怕代码提交到仓库时泄露密钥。3.3 把 Tools 定义交给 MCP Server 自动生成上面手写 tools JSON 是为了让你看清结构真实项目里完全可以让 MCP Server 自动生成。比如你用某个 MCP SDK 连接一个天气服务SDK 会返回一个list_tools结果里面就是标准化的工具描述数组。你只需要把这个数组赋值给payload[tools]然后发送给大模型。这样做有两个好处一是工具定义不会写错二是新增工具时不用改 Agent 逻辑。TaoToken 在这一层扮演的是模型通道它不会限制你用什么工具协议只要是标准的 tools 定义都可以直接透传。如果你用的是现成的 OpenAI SDKbase_url 参数同样设置为https://taotoken.net/apiapi_key 设置为YOUR_API_KEY其余调用方式不变。Agent 的编排代码不需要为 TaoToken 做特殊修改。4. 跑一次真实调用验证 tool_use 触发配置完成后运行上面的脚本。如果一切正常你会看到输出里包含“模型要调用工具”的字样以及模型给出的工具名和参数。这说明大模型已经理解工具定义并且决定调用draw_cat。接下来你的程序只需要执行真实的画图接口然后把结果返回给用户。4.1 预期返回结构正常情况下message里会出现类似这样的结构{ message: { role: assistant, content: null, tool_calls: [ { id: call_xxx, type: function, function: { name: draw_cat, arguments: {\prompt\: \一只橘猫坐在窗台上\} } } ] } }拿到这个结构后你就知道模型选择了哪个工具、参数是什么。这是整个 Agent 调用链的转折点之前模型只是在聊天从这一步开始你的程序接过控制权去执行真实操作。执行完画图接口后你可以选择把图片直接展示也可以再把图片 URL 作为 assistant 的后续消息发给模型让它生成一句“画好了一只橘猫坐在窗台上给你”。4.2 把工具结果回传给模型如果选择第二种方式需要把工具执行结果包装成一条 role 为 tool 的消息追加到 messages 里再发起一次请求。这样模型能够看到工具执行的实际情况并给出更自然的回复。比如工具返回图片 URL模型就会说“已经画好了图片在这里”而不是机械地输出 JSON。这也是长会话 Agent 的常见做法对话、工具调用、工具结果、再对话形成一个循环。4.3 去官网控制台核对用量调用完成后可以回到 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 控制台查看这次请求的用量记录。确认请求成功、模型 ID 正确、token 消耗正常。这一步很重要能帮你第一时间发现 Key 配错或模型 ID 选错的问题。如果上面代码输出的是“请求失败”也可以先到控制台看是否有对应错误记录。5. 常见报错对照跑 Agent 时最容易遇到下面几个问题每个都对应不同的原因。出现报错先别急着改代码对照自己的请求体逐项排查。5.1 401 Unauthorized返回 401基本可以断定是 API Key 不对。检查两个地方Key 是否从 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建并完整复制Key 前后有没有多余空格。不要把官网地址当成接口地址官网是给人点的接口地址是https://taotoken.net/api。另外Authorization 头的格式必须是Bearer YOUR_API_KEY别漏掉 Bearer 前缀。5.2 404 Not Found / path not found请求路径写错时会看到这个。最常见的是把 Base URL 写成了https://taotoken.net/api/v1。TaoToken 的接口 Base URL 就是https://taotoken.net/api后面不要再加/v1也不要加https://taotoken.net/。如果你用的是 OpenAI SDK直接把 base_url 设为它SDK 会自己拼接/chat/completions。不要画蛇添足。5.3 模型 ID 不存在如果你填了一个猜测的模型名比如带日期后缀的版本号大概率会收到模型无效的报错。所有可用模型 ID 都列在官网模型广场去那里复制不要凭记忆输入。模型广场里可能同时有好几个模型选哪个取决于你对速度、质量、成本的要求。对于工具调用场景建议优先选工具调用能力稳定的模型不要只看参数大小。5.4 模型一直不返回 tool_use模型直接回了文字没有工具调用通常是 tools 参数没有正确传进去或者模型认为当前不需要工具。先打印你实际发出去的 payload确认 tools 在请求体里再检查工具描述是否清晰比如画图工具的 description 要写清楚“当用户要求画图时调用”同时用户消息里要包含明确的意图。如果模型仍然不调用可以换一个更强的模型试试模型能力会影响工具调用的稳定性。6. 下一步把你自己的 MCP Server 挂进来上面的例子手写了一个 tools JSON实际工程里你可能已经装了现成的 MCP Server比如查天气、搜网页、操作 Blender 的服务器。这些 MCP Server 都会对外暴露统一的工具列表你只需要用 MCP-Client 库读取到工具描述再把它合并进请求体的 tools 字段剩下的流程完全一样。TaoToken 在这一层不参与工具定义它只负责让不同模型都能接进同一个 Base URL 和 Key。我现在很多 Agent 脚本都改成了这种结构工具定义由 MCP Server 提供模型通道由 TaoToken 承担。长会话里切换模型时只需要改请求里的 model 字段Key 和地址始终不变。如果你的 Agent 还停留在“一个模型写死一套 Key”的阶段建议参考上面的配置改造一次。打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建完 Key填好接口地址再用一个小工具跑一次 tool_use你就能感受到“模型随便换工具照样调”的清爽。