1. Agent 调外部工具的标准动作:从开发接口到读取 tool_use
当我让 Claude“帮我画一只小猫”,它只会道歉说没有绘画能力。要让大模型真正动手,得走 Agent 的工具调用流程。这流程本身不复杂,但要把 Key、Base URL、模型 ID 都对齐,很麻烦。我的做法是:先去 https://taotoken.net/?utm_source=taotoken_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 叫 functions,Anthropic 叫 tools,参数结构也不完全一样。你为 Claude 写的工具描述,搬到另一个模型上可能要改字段名。于是 MCP(Model 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 接入层的碎片化:一个模型一套 Key
MCP 统一了工具定义,但模型接入的碎片化依然存在。你手上可能同时有 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_source=taotoken_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": f"Bearer {API_KEY}", "Content-Type": "application/json" } response = requests.post(f"{BASE_URL}/chat/completions", json=payload, headers=headers) 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 生成,不需要手写上面的 JSON;TaoToken 只负责模型请求这一层。
为了让这段代码更贴近真实项目,你可以把 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 SDK,base_url 参数同样设置为https://taotoken.net/api,api_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_source=taotoken_aicg_blog_end 控制台查看这次请求的用量记录。确认请求成功、模型 ID 正确、token 消耗正常。这一步很重要,能帮你第一时间发现 Key 配错或模型 ID 选错的问题。如果上面代码输出的是“请求失败”,也可以先到控制台看是否有对应错误记录。
5. 常见报错对照
跑 Agent 时最容易遇到下面几个问题,每个都对应不同的原因。出现报错先别急着改代码,对照自己的请求体逐项排查。
5.1 401 Unauthorized
返回 401,基本可以断定是 API Key 不对。检查两个地方:Key 是否从 https://taotoken.net/?utm_source=taotoken_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_source=taotoken_aicg_blog_end 创建完 Key,填好接口地址,再用一个小工具跑一次 tool_use,你就能感受到“模型随便换,工具照样调”的清爽。