1. 为什么我要把 Qwen3 和 MCP 拼在一起做图像助手
Qwen3 是通义千问开源的新一代大模型系列,原生支持工具调用与 MCP(Model Context Protocol,模型上下文协议),能作为 Agent 的大脑去调度外部能力。MCP 则是一套让模型和外部工具"说同一种语言"的协议,图像生成、搜索、代码执行都能被封装成 MCP Server 挂上去。把两者组合起来,你就能得到一个"听得懂人话、还能自己动手画图"的 Qwen AI 图像生成助手,适合想低成本自建 AI 绘图工具、又不想被单一厂商绑死的开发者。
真正动手时,麻烦往往不在模型本身,而在"Key 和 Base URL 怎么统一"。本地 Ollama 跑 Qwen3 是一种玩法,但一旦你想换成云端 Qwen3、或者同时接多个模型做对比,每个服务一套 Key、一套地址,配置就会散得到处都是。我的做法是用 TaoToken 做统一入口:一个 Key、一个 Base URL,OpenAI 兼容格式,Qwen3 和后续要加的模型都走同一个口子,MCP 服务端只认这一份配置。这样切换模型时不用改代码,只改一个 model 字段。
这篇会按完整链路走一遍:先讲清楚问题场景和整体架构,再配置 TaoToken 前置(拿 Key、设 Base URL),然后给出可直接复制的 MCP 服务端配置和 Qwen3 接入参数,接着跑一次真实的图像生成请求验证返回结果,最后把几个高频报错(401、local proxy failed、reading choices、OAuth)逐个拆开排查。全程代码可复制,小白也能跟着做。
架构上分三层:最上面是 Streamlit 或任意客户端做 UI;中间是 Qwen Agent 框架,负责把用户指令转成工具调用;最下面是 MCP Server(图像生成)和模型服务(Qwen3)。TaoToken 的角色是模型服务这一层的统一网关,Base URL 指向https://taotoken.net/api,Key 用你在控制台生成的那把。理解这三层,后面配置就不会乱。
2. TaoToken 前置:统一 Key 与 Base URL 怎么设
先说清楚 TaoToken 在这里干什么。它是一个 OpenAI 兼容的模型接入入口,你注册后在控制台生成 API Key,然后把 Base URL 设成https://taotoken.net/api,就能用标准 OpenAI SDK 的写法去调 Qwen3 等模型。对 MCP 场景来说,好处是模型层只暴露一个地址和一把 Key,MCP Server 配置里不用再塞一堆不同厂商的凭证。
第一步,打开官网https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content=注册账号。注册流程很常规,邮箱加密码即可,这里不展开。登录后进控制台,地址是https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite,在 API Keys 页面点创建,复制生成的 Key。注意:Key 只在创建时完整显示一次,先存到本地环境变量里,别直接写死在代码里提交到 Git。
第二步,确认你要用的模型 ID。Qwen3 系列在 TaoToken 上的模型标识建议在模型对话页https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite里先试一下,输入框选模型、发一句话,能正常回就说明这个 Model ID 可用。把可用的 Model ID 记下来,比如qwen3-8b这类命名,后面配置里要用。
第三步,设置环境变量。Linux/macOS 在终端执行:
export TAOTOKEN_API_KEY="sk-你的Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"Windows PowerShell:
$env:TAOTOKEN_API_KEY="sk-你的Key" $env:TAOTOKEN_BASE_URL="https://taotoken.net/api"这里有个容易踩的坑:Base URL 到底带不带/v1。OpenAI 官方 SDK 默认会在 Base URL 后面拼/chat/completions,所以如果你用的是openaiPython 包,Base URL 填https://taotoken.net/api即可,SDK 会自己补路径。但如果你用的是某些要求完整路径的框架,就要按它的文档来。我实测下来,Qwen Agent 走 OpenAI 兼容模式时,model_server填https://taotoken.net/api是能通的。
第四步,验证 Key 是否有效。用 curl 发一个最小请求:
curl https://taotoken.net/api/chat/completions \ -H "Authorization: Bearer $TAOTOKEN_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-8b", "messages": [{"role": "user", "content": "你好"}] }'返回里如果有choices数组且message.content有内容,说明 Key 和 Base URL 都对了。如果返回 401,先检查 Key 有没有复制全、有没有多余空格;如果返回模型不存在,回模型对话页确认 Model ID 拼写。这一步过了,再往下接 MCP。
关于 Coding Plan:如果你打算长期跑 Agent、频繁调模型,可以看下https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite,它更适合持续编码和 Agent 场景,比按次调用更省心。接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite,遇到参数细节可以查。
3. 可复制配置:MCP 服务端 + Qwen3 接入参数
这一节给可直接复制的配置。先建 Python 环境,再写 MCP 服务端配置,最后把 Qwen3 接入参数填进去。环境用 conda 或 venv 都行:
conda create -n qwen_mcp python=3.11 -y conda activate qwen_mcp pip install -U "qwen-agent[gui,rag,code_interpreter,mcp]" pip install streamlit openaiMCP 服务端配置我用 JSON 形式管理,方便和别的客户端共用。新建mcp_config.json:
{ "mcpServers": { "pollinations": { "command": "npx", "args": ["-y", "@pollinations/model-context-protocol"], "env": { "TAOTOKEN_API_KEY": "sk-你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api" } } } }注意env里我把 TaoToken 的 Key 和 Base URL 透传给了 MCP 进程,这样 MCP Server 如果需要调模型也有凭证。实际图像生成走的是 Pollinations 自己的免费接口,不需要额外 Key,但保留这两个变量方便你以后换成别的 MCP Server。
接下来是 Qwen3 接入参数。如果你用本地 Ollama,model_server指向http://localhost:11434/v1;如果走 TaoToken 统一入口,就换成 TaoToken 的地址。两种写法我都给出来,你按需选:
# 方案 A:走 TaoToken 统一入口(推荐,一个 Key 管所有模型) llm_cfg = { "model": "qwen3-8b", "model_server": "https://taotoken.net/api", "api_key": "sk-你的Key", "generate_cfg": {"top_p": 0.8} } # 方案 B:走本地 Ollama # llm_cfg = { # "model": "qwen3:8b", # "model_server": "http://localhost:11434/v1", # "api_key": "EMPTY" # }如果你用 Cline 或 Claude Code 这类客户端接 MCP,配置里同样要写全三件套:Base URL、Key、Model ID。以 Cline 的 MCP 配置为例,在cline_mcp_settings.json里:
{ "mcpServers": { "pollinations": { "command": "npx", "args": ["-y", "@pollinations/model-context-protocol"], "env": { "OPENAI_BASE_URL": "https://taotoken.net/api", "OPENAI_API_KEY": "sk-你的Key", "OPENAI_MODEL": "qwen3-8b" } } } }Codex 用户如果走auth.json,结构类似,把base_url和api_key填成 TaoToken 的值,model填 Qwen3 的 Model ID。CC Switch 切换配置时,也是改这三个字段。记住:Base URL 不带/v1后缀(除非框架明确要求),Key 用控制台生成的那把,Model ID 用模型对话页验证过的那个。
把上面的llm_cfg和mcp_config.json合到主程序里,完整代码如下:
import json import streamlit as st from qwen_agent.agents import Assistant with open("mcp_config.json", "r", encoding="utf-8") as f: mcp_conf = json.load(f) llm_cfg = { "model": "qwen3-8b", "model_server": "https://taotoken.net/api", "api_key": "sk-你的Key", "generate_cfg": {"top_p": 0.8} } tools = [ {"mcpServers": mcp_conf["mcpServers"]}, "code_interpreter", ] bot = Assistant(llm=llm_cfg, function_list=tools) st.set_page_config(page_title="Qwen AI 图像助手", layout="wide") st.title("Qwen3 + MCP 图像生成助手") st.caption("由 Qwen3 驱动,通过 MCP 调用图像生成工具") if "messages" not in st.session_state: st.session_state["messages"] = [] for msg in st.session_state["messages"]: with st.chat_message(msg["role"]): st.markdown(msg["content"]) user_input = st.chat_input("试试:画一只戴宇航员头盔的猫") if user_input: st.session_state["messages"].append({"role": "user", "content": user_input}) with st.chat_message("user"): st.markdown(user_input) with st.chat_message("assistant"): placeholder = st.empty() try: responses = bot.run(messages=st.session_state["messages"]) final = None for chunk in responses: final = chunk if final: text = "\n".join( str(m["content"]) for m in final if m.get("role") == "assistant" and m.get("content") ) placeholder.markdown(text) st.session_state["messages"].extend( [m for m in final if m.get("role") == "assistant"] ) else: placeholder.markdown("没有拿到回复,检查 MCP 服务是否启动。") except Exception as e: st.error(f"请求出错:{e}")保存为qwen_mcp_app.py,运行streamlit run qwen_mcp_app.py。第一次跑时npx会去下载 Pollinations MCP 包,需要联网,耐心等几秒。
4. 验证请求:跑一次图像生成并检查返回
配置写完必须验证,不然你不知道是模型没通还是 MCP 没起。分两步:先单独验证模型,再验证 MCP 工具调用。
第一步,验证 Qwen3 模型通不通。写个最小脚本test_llm.py:
from openai import OpenAI client = OpenAI( base_url="https://taotoken.net/api", api_key="sk-你的Key" ) resp = client.chat.completions.create( model="qwen3-8b", messages=[{"role": "user", "content": "用一句话介绍你自己"}] ) print(resp.choices[0].message.content)运行python test_llm.py。如果打印出模型回复,说明 TaoToken 的 Key、Base URL、Model ID 三件套都对。如果报 401,看第 5 节排查。
第二步,验证 MCP 图像生成。启动 Streamlit 后,在输入框输入"画一只戴宇航员头盔的猫",回车。正常流程是:Qwen3 先理解指令,判断需要调用图像生成工具,然后通过 MCP 协议调用 Pollinations,最后返回图片链接或图片本身。返回结果里你会看到一段 Markdown 图片语法,类似,或者直接是图片 URL。
检查返回结果时看三点:一是choices里有没有tool_calls字段,有说明模型确实发起了工具调用;二是工具调用返回后,模型有没有把结果整合成自然语言回复;三是图片 URL 能不能在浏览器打开。如果只有文字没有图片,多半是 MCP Server 没启动成功,回终端看有没有npx报错。
我实测下来,第一次调用会慢一些,因为要下载 MCP 包和建立连接,后续就快了。如果返回里出现reading 'choices'这类报错,说明响应结构和你预期的不一样,通常是 Base URL 或路径拼错了,见第 5 节。
再给一个纯命令行的验证方式,不依赖 Streamlit,方便排查:
import asyncio from qwen_agent.agents import Assistant llm_cfg = { "model": "qwen3-8b", "model_server": "https://taotoken.net/api", "api_key": "sk-你的Key" } tools = [{ "mcpServers": { "pollinations": { "command": "npx", "args": ["-y", "@pollinations/model-context-protocol"] } } }] bot = Assistant(llm=llm_cfg, function_list=tools) messages = [{"role": "user", "content": "画一只戴宇航员头盔的猫"}] for resp in bot.run(messages=messages): for m in resp: if m.get("role") == "assistant": print(m.get("content"))这个脚本能跑通,说明整条链路没问题,Streamlit 只是外壳。跑不通就按报错信息对号入座。
5. 本篇常见错排查:401、local proxy failed、reading choices、OAuth
这一节把高频报错逐个拆开。每个都给你现象、原因、解法。
401 Unauthorized。现象:请求返回 401,提示 invalid api key 或 missing authorization。原因通常是 Key 没设对。检查顺序:一是 Key 有没有复制完整,控制台生成的 Key 很长,容易漏字符;二是环境变量有没有生效,echo $TAOTOKEN_API_KEY看输出;三是代码里是不是硬编码了旧 Key。解法:重新生成一把 Key,用环境变量注入,别写死在代码里。如果用的是 Cline 或 Claude Code,检查cline_mcp_settings.json或auth.json里的api_key字段。
local proxy failed。现象:连接超时或提示本地代理失败。原因一般是网络层配置问题,或者 Base URL 写成了本地地址但本地服务没起。检查:如果你走 TaoToken,Base URL 应该是https://taotoken.net/api,不是localhost;如果你走本地 Ollama,确认ollama serve在跑,端口 11434 没被占。解法:先 curl 一下 Base URL 看通不通,再检查防火墙。注意别在配置里留任何代理相关的环境变量,清掉HTTP_PROXY、HTTPS_PROXY再试。
reading 'choices'。现象:报错Cannot read properties of undefined (reading 'choices')或 Python 里KeyError: 'choices'。原因:响应结构里没有choices字段,通常是 Base URL 路径拼错,请求打到了错误端点,返回了 HTML 或错误 JSON。检查:Base URL 是不是多写了/v1或少写了/api;请求路径是不是/chat/completions。解法:用 curl 直接打一次,看返回的原始 JSON 长什么样。如果返回的是网页 HTML,说明地址错了。
OAuth 相关报错。现象:提示 OAuth token 失效或需要重新授权。原因:某些客户端(如 Claude Code)默认走 OAuth 流程,但你用的是 API Key 模式。解法:在客户端配置里显式指定 API Key 模式,把 Base URL 和 Key 填进对应字段。Claude Code 的配置在~/.claude/settings.json或项目级配置里,把apiKey和baseURL设成 TaoToken 的值。如果客户端强制走 OAuth,看它的文档有没有 API Key 开关。
MCP Server 启动失败。现象:终端报npx找不到命令,或 MCP 包下载失败。原因:没装 Node.js,或网络问题。解法:装 Node.js 18+,然后手动跑一次npx -y @pollinations/model-context-protocol看能不能启动。能启动再回到主程序。
模型不调用工具。现象:模型只回文字,不生成图片。原因:Qwen3 没识别出需要调工具,或者工具描述没传对。解法:在指令里明确说"用图像生成工具画",或者在generate_cfg里调低top_p让输出更确定。另外确认function_list里 MCP 配置的 key 名和mcp_config.json里一致。
排查时记住一个原则:先分层验证。模型层用 curl 或test_llm.py验证,MCP 层用命令行脚本验证,UI 层最后验证。哪层报错就查哪层,别一上来就改代码。
6. 把 Key 统一后,我的接入路径
走到这里,你应该已经跑通了一次完整的图像生成。回头看,最省事的一步其实是把 Key 和 Base URL 统一到 TaoToken:模型层只认一个地址、一把 Key,MCP 配置里不用再塞不同厂商的凭证,换模型只改model字段。这对后面要加搜索、代码执行等更多 MCP 工具的场景特别友好,配置不会越堆越乱。
如果你还想继续扩展,下一步可以试这几个方向:一是把 Pollinations 换成别的图像 MCP Server,对比生成质量;二是加一个搜索 MCP,让助手能先查资料再画图;三是把 Streamlit 换成你熟悉的 Web 框架,做成 API 服务。模型对话页https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite可以先试不同 Qwen3 尺寸,找到速度和质量的平衡点。API Keys 在https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite管理,接入文档在https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite。长期跑 Agent 的话,Coding Plan 在https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite。
最后留一个我踩过的坑:MCP 配置里的env字段,不同客户端支持程度不一样。有的客户端会把env透传给子进程,有的会忽略。如果你发现 MCP Server 拿不到 Key,别在env里绕,直接在启动命令前用 shell 导出环境变量,或者把 Key 写进 MCP Server 自己的配置文件。这个细节文档里往往不写,但排查时能省你半小时。