FastMCP 示例里 LLMClient 的 base_url 改到 TaoToken 后,qwen 问答照常跑通 📅 发布时间:2026/9/18 12:04:57 👁 浏览次数: 1. FastMCP 数学问答示例里LLMClient 那两行写死的东西开始碍事FastMCP 数学问答示例里LLMClient的api_key和base_url直接写死了 DashScope这是很多人跑通之后第一个想改的地方。把这段接到 TaoToken 上之后原本调qwen-plus-latest的问答请求走统一通道客户端输入一道乘法题照样能触发multiply而 FastMCP 服务端和 SSE 传输逻辑一行都不用动。这个示例的好处是结构短短到你一眼能看完本地起一个 MCP Server注册几个数学工具再写一个 LLMClient把工具描述交给模型模型决定什么时候调工具最后通过 SSE 把两端连起来。坏处也是因为短——所有配置都挤在一个类里供应商、模型名、密钥全写死在构造函数换任何一样都得翻源码。所以这篇不讲 FastMCP 是什么也不重新教一遍 MCP 协议。就沿着示例本身的路径走先看清数据流再打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 拿 Key然后把LLMClient里那两行换掉最后验证一次完整的数学问答顺带把可能撞上的报错对一遍。1.1 示例的数据流客户端提问 → 模型决策 → multiply 执行先把链路说清楚不然后面改配置容易改错位置。客户端把一个自然语言问题发进来比如「13 乘 27 等于多少」。这句话先到 LLMClient由它连同工具清单一起发给大模型。模型读完工具描述返回一个tool_call参数里带着a13、b27但注意模型本身不执行计算。真正做乘法的是本地那个 Python 进程里的multiply函数。LLMClient 拿到 tool_call 后在本地调用对应函数把返回值再塞回对话让模型生成最终那句自然语言回答。这条链里只有「模型决策」这一跳需要外部 API其他全在本地。因此换供应商要改的只是 LLMClient 里发请求的那一段。工具注册、参数校验、函数执行、结果回填全都跟供应商无关。1.2 换供应商要改源码根子在 api_key 和 base_url 硬编码示例里的写法大致是这样OpenAI(api_key..., base_url...)其中 key 从环境变量或直接写字面量取base_url 指向 DashScope 的兼容模式端点模型名写qwen-plus-latest。想试试别的模型就得回来改这三处改完还要重新读一遍常量在哪。更麻烦的是协作场景同事拉下代码发现 key 没配还得去翻注释找是哪家的 key、去哪申请。base_url和模型名分散在不同文件时问题会成倍放大。改造思路不复杂——把这三件事统一收到一处指向一个兼容 OpenAI 协议的通道。这样以后换模型只改模型 ID换供应商这件事本身就不存在了。2. 准备 Key 与模型 IDTaoToken 控制台这一趟必须走在动代码之前有两样东西要先拿到手一把 API Key一个可用的模型 ID。这两样都在 TaoToken 后台。整个准备过程大概三分钟比改代码本身还快但顺序别颠倒——先有 Key再去填配置否则改完代码还得回头补。这里要提醒一句注册和创建 Key 走的是官网落地页不是接口地址两者别混。后面填进代码里的 Base URL 是另一回事第 3 章会单独讲。2.1 注册并创建 API Key浏览器打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册登录后进入控制台在 API Keys 页面新建一把 Key。Key 只在创建时完整显示一次复制下来先存到本地密码管理器或者临时文件里别直接粘进代码。如果你之前用别的方式管过密钥习惯上可能会想「先写死在代码里跑通再说」。这个示例里可以破例一次但第 3.3 节会给出.env的收口写法跑通后建议立刻切过去。Key 在代码里留痕、又被推到远端仓库是很常见的翻车方式。本文所有示例里的 Key 都写成占位符YOUR_API_KEY你替换成自己那把即可。不要把真实 Key 贴进任何提问、截图或者 issue。2.2 在模型广场挑一个能跑 function calling 的模型第二样东西是模型 ID。打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的模型广场看当前列表里有哪些模型挑一个明确支持工具调用function calling / tool use的。这一步不能省也不能凭记忆填。示例原来的qwen-plus-latest是 DashScope 侧的命名换通道之后不一定同名可用具体写什么以模型广场当时的列表为准。模型广场里通常会标注模型的能力标签挑带工具调用能力的那一档即可。选好之后把模型 ID 记下来下一步要填进 LLMClient。如果只是先验证链路挑一个响应快的就够不必一上来就选最贵的。3. 改 LLMClient把 base_url 指向 https://taotoken.net/api终于到动代码这一步。改动量很小只涉及 LLMClient 构造函数的两个参数和一个模型名常量其余文件不动。为了让你对照着改先把改造前的样子贴出来再贴改造后的。有一点要反复强调填进代码的 Base URL 是https://taotoken.net/api末尾不带/v1。官网落地页那个带 UTM 的地址是给人点的不要填进base_url两者用途完全不同混用会直接 404。3.1 改造前的 DashScope 写法示例里 LLMClient 大致长这样供应商端点、模型名全在类里import os from openai import OpenAI class LLMClient: def __init__(self): self.client OpenAI( api_keyos.getenv(DASHSCOPE_API_KEY), base_urlhttps://dashscope.aliyuncs.com/compatible-mode/v1, ) self.model qwen-plus-latest这段代码没毛病只是把「用哪家」这件事焊死在了类内部。想换通道就得回来改base_url想换模型就得改self.model两件事还会互相牵连因为有些模型只在特定通道上架。另外注意DASHSCOPE_API_KEY这个环境变量名——它本身就带着供应商信息。换通道之后变量名也该跟着改否则下一个读代码的人还得猜。3.2 改造后的 TaoToken 写法改完之后是这样两处替换一处新增环境变量名import os from openai import OpenAI class LLMClient: def __init__(self): self.client OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY, YOUR_API_KEY), base_urlhttps://taotoken.net/api, ) self.model os.getenv(TAOTOKEN_MODEL_ID)api_key换成你的YOUR_API_KEY或从环境变量读base_url固定写https://taotoken.net/api模型 ID 从环境变量取值以模型广场当时列表为准。不要在base_url后面补/v1。OpenAI SDK 自己会在请求路径上拼接/chat/completions你多写一段就等于把路径凑成/api/v1/v1/chat/completions服务端找不到报 404。工具清单、tool_choice、消息数组这些参数一个都不用改它们本来就是标准 OpenAI 协议字段通道换了不影响。3.3 用 .env 收口避免 Key 进版本库跑通之后把 Key 从代码里挪出去。最省事的方式是.env加python-dotenv# .env TAOTOKEN_API_KEYYOUR_API_KEY TAOTOKEN_MODEL_ID以模型广场当时列表为准配一个.gitignore把.env排除掉代码里保留os.getenv的读法。这样本地和服务器用不同的 Key 也不用改代码。如果团队里多人共用建议再往上一层把模型 ID 也做成可配项而不是写在某个模块常量里。模型广场会更新硬编码的模型名迟早会过期。4. FastMCP 服务端与 SSE 传输逻辑保持原样前面的改动只碰了 LLMClient。这一章要说明的是服务端那半边真的一行都不用改这也是这次改造最省心的地方。MCP 工具的定义和传输方式跟模型走哪条通道没有耦合。如果你原来的示例里工具注册和服务启动写在一个文件里那就更简单——整个文件原样保留只改客户端侧。4.1 工具注册照抄一个字符都不用改服务端核心是这样multiply和add两个工具from fastmcp import FastMCP mcp FastMCP(math-qa) mcp.tool() def multiply(a: float, b: float) - float: 返回 a 与 b 的乘积。 return a * b mcp.tool() def add(a: float, b: float) - float: 返回 a 与 b 的和。 return a b工具的描述字符串docstring很关键模型就是靠它判断什么时候该调这个工具。写清楚「返回两数乘积」比写「计算」有效得多。这部分跟通道无关原来的写法不用动。要加新工具就再写一个mcp.tool()函数然后同步更新客户端那份工具 schema。两份描述保持一致模型才不会调错。4.2 SSE 启动参数与客户端连接地址启动 SSE 服务还是老样子if __name__ __main__: mcp.run(transportsse, host127.0.0.1, port8000)这里只改端口或主机时要注意客户端的连接地址要跟着改别一边起在 8001另一边的客户端还在连 8000。有个容易混的点这里的host、port是 FastMCP 本地 SSE 服务的监听地址跟https://taotoken.net/api完全是两码事。前者是你自己机器上的端口后者是模型 API 的入口。两者出现在同一份配置里时务必分清哪一行是给谁用的。5. 验证一次完整的数学问答配置改完最该做的是跑一次端到端而不是先看代码有没有语法错误。因为这条链路的关键不在代码能跑而在「模型有没有正确决定调工具」和「请求有没有真的走通道」。验证分两步本地看日志后台看用量。两边对上才算真的通。5.1 客户端输入与工具触发日志客户端侧发一条最简单的请求走完整的 tool calling 循环import json, os from openai import OpenAI client OpenAI( api_keyos.getenv(TAOTOKEN_API_KEY, YOUR_API_KEY), base_urlhttps://taotoken.net/api, ) TOOLS [{ type: function, function: { name: multiply, description: 计算两个数的乘积, parameters: { type: object, properties: {a: {type: number}, b: {type: number}}, required: [a, b], }, }, }] def ask(question: str) - str: model os.getenv(TAOTOKEN_MODEL_ID) messages [{role: user, content: question}] first client.chat.completions.create( modelmodel, messagesmessages, toolsTOOLS, tool_choiceauto ) msg first.choices[0].message if not msg.tool_calls: return msg.content messages.append(msg) for call in msg.tool_calls: args json.loads(call.function.arguments) result multiply(args[a], args[b]) # 本地执行不走 API messages.append({ role: tool, tool_call_id: call.id, content: str(result), }) final client.chat.completions.create(modelmodel, messagesmessages) return final.choices[0].message.content输入「13 乘 27 等于多少」日志里应该能看到一次tool_calls返回参数是{a: 13, b: 27}随后是本地multiply的执行痕迹最后一次请求拿到自然语言结果。这个顺序对了说明链路是通的。注意multiply是在你本地进程里被调用的不是模型远程执行的。模型只负责判断和给参数这一点别搞混。5.2 回 TaoToken 控制台核对 Token 消耗本地日志只能证明程序跑通了不能证明请求真的走了你配的通道。所以第二件事是打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 进控制台看用量记录。正常情况下刚才那一轮问答会对应两次请求一次是带工具的决策请求一次是拿到工具结果后的收尾请求Token 消耗分别记着。看到记录说明base_url确实生效了没有偷偷走别的端点。顺便对比一下两笔消耗的量级。带工具的那次请求因为要传工具 schema输入 Token 会明显多一些如果两次记录的量级完全一样或者一次都没有那就要回头查配置了。6. 报错对照401、404、工具不触发分别查哪里改了base_url和api_key之后最容易撞上的就三类问题。这里按「先看什么、再看什么」的顺序列一下省得漫无目的地翻代码。6.1 401 先看环境变量有没有加载401 一般不是 Key 本身错了而是代码没读到。最常见的情况是.env写了但启动脚本里没调load_dotenv()或者环境变量名对不上代码读TAOTOKEN_API_KEY.env里写的是TAOTOKEN_KEY。排查方式很土但有效在OpenAI(...)前面打一行print(bool(os.getenv(TAOTOKEN_API_KEY)))看是不是False。如果是问题在加载环节不在通道。另一种可能是 Key 复制时带了空格或换行尤其是从终端复制的场景。重新从控制台复制一次注意别带上首尾空白。6.2 404 多半是 base_url 尾巴多了 /v1base_url写成https://taotoken.net/api/v1请求就会拼成/api/v1/v1/chat/completions服务端找不到这个路径。改回https://taotoken.net/api即可末尾不要带斜杠也不要带版本号。还有一个容易忽略的情况把官网落地页那个带查询参数的地址填进了base_url。带?utm_source...的地址是给人看的不能当接口地址用。两件事分开。如果 404 的报错信息里能看到完整请求路径直接对比一下是不是多了段/v1一般一眼就能认出来。6.3 模型不回 tool_call 的三种原因链路通了但模型直接回答「13 乘 27 是 351」而不调工具这不算报错但说明工具没生效。常见原因有三个。一是选的模型不支持工具调用或者当前通道下没开这个能力。回模型广场确认一下能力标签换一个支持 tool use 的。二是tools参数没传或者结构写错了比如parameters的 schema 少了一层。三是提示词太模糊模型觉得没必要调工具这种情况可以在 system 消息里明确要求「涉及算术必须调用工具」。三种原因的判断顺序建议是先看请求体里tools有没有发出去打日志最直接再看模型能力最后才调提示词。反过来查会绕远路。7. 跑通之后把这条链路接回日常到这里FastMCP 数学问答示例已经能完整跑一遍而且换模型不再需要改源码。剩下的事有两件都跟后续使用有关。第一件是验证这把 Key 在别的场景也能用。打开 TaoToken 模型对话 页面用同一把 Key 发一条消息确认模型 ID 和 Base URL 填法一致。这个页面相当于一个最小验证环境接口有问题在这里能最快暴露。第二件是长期成本。如果这套问答后面要跑批量任务或者你打算拿同一个 Base URL 去接日常写代码的助手可以看看 Coding Plan 的套餐是否合适。Key 的管理和再创建在 控制台 API Keys 页面。最后留个提醒https://taotoken.net/api这个 Base URL 可以复用到任何兼容 OpenAI 协议的工具里包括你平时用的编码助手。但无论接哪个工具填的都是接口地址不是官网落地页——这两个地址混用是这类改动里最高频的低级错误。