- 我部署 Qwen3.6-35B-A3B-FP8(MoE3B)2天,测工具调用一直是 0 分,以为模型不行
- 直到昨天发现是 vLLM 的
--tool-call-parser hermes和模型 XML 格式不匹配 - 改成
--tool-call-parser qwen3_coder后,24 题工具调用从 0/72 直接变成 69/72 - 附 7 模型横评数据:本地 A3B MoE avg 1024ms · 96% 准确 z在工具调用维度碾压所有云端付费 API
背景:为什么需要自部署 Qwen3.6 35B A3B MoE?又为什么要做这个测试?
我在做一个叫 Lynn 的 AI Agent(Electron 桌面 App,Apache 2.0)。它的 Brain 后端(Hono 服务 on 腾讯云)走六级降级链路由:
T1 本地 GPU Qwen3 → T2 Kimi K2.5 → T3 GLM → T4 DeepSeek → T5 Step → T6 MiniMax
T1 本地 GPU 是主力,延迟低 + 无 token 成本。上周我收到一张 RTX 4090 48GB 改装卡(不是消费级 24GB 版),想跑个更强的本地模型。
目标模型:Qwen3.6-35B-A3B-FP8
- MoE 架构:35B 总参数 + 激活 3B(类似 Mixtral 8x7B 的 8 选 2)
- FP8 量化:保真度比 4bit AWQ 高
- 64K context:对长文档友好
另外一个真实的情况是 Claude Opus 4.7 吵架了
这两天我用更新的 Claude Opus 4.7,发现它越来越不爱调工具。
下面是一个前天的真实的段子——
我跟 Claude 聊 Qwen3.6-35B-A3B的并发情况,它坚持说这个模型根本没发布,还自己脑补降级成 Qwen 3.5 搞了一大堆”数据”反驳我。
我忍不住直接告诉它:”Qwen3.6-35B-A3B 已经发布了!”
它才终于调用 web_search 工具,然后搜出来一堆结果,承认自己搞错了,跟我道歉:
“你说得对,我之前搞错了,向你道歉!Qwen3.6-35B-A3B 在 2026-04-16 刚上架 Hugging Face Hub 和 ModelScope,就在两天前,我之前的回答把它当成 Qwen3.5 处理了…”
问题是——它一开始明明有 web_search 工具,为什么不调?为什么非要被我当场揭穿了才去搜?
作为在做 AI Agent 产品(Lynn · Apache 2.0)的人,这件事让我后背发凉——如果用户不像我这样懂技术、不会”逼”模型调工具,那它就会用一堆自信的幻觉把人糊弄过去,用户还真就信了。
这个问题直接影响我的产品路由策略——我需要客观数据,不能让幻觉左右用户。
于是写了一套 24 题测试,把 5 家国产云端付费大模型 + 2 个本地 Qwen3 模型一起过了一遍。
部署过程(踩坑实录)
第一次部署:systemd 单元配置
# /etc/systemd/system/vllm-qwen35.service
[Service]
ExecStart=/home/vipuser/miniconda3/envs/vllm/bin/vllm serve /root/models/Qwen3.6-35B-A3B-FP8 \
--host 0.0.0.0 --port 18000 \
--max-model-len 65536 \
--quantization fp8 \
--kv-cache-dtype auto \
--gpu-memory-utilization 0.85 \
--enable-prefix-caching \
--enable-chunked-prefill \
--max-num-seqs 16 \
--max-num-batched-tokens 8192 \
--enable-auto-tool-choice \
--tool-call-parser hermes \ # ← 这里是坑
--served-model-name Qwen3.6-35B-A3B \
--trust-remote-code
模型加载 2 分钟,vLLM 正常启动,API /v1/models 返回正常。
第一次测试:工具调用全空
# 测试代码
payload = {
"model": "Qwen3.6-35B-A3B",
"messages": [{"role": "user", "content": "北京今天天气怎样"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}],
"tool_choice": "auto",
"chat_template_kwargs": {"enable_thinking": False},
}
vLLM 返回:
{
"tool_calls": [],
"content": "Here's a thinking process:\n1. Analyze User Input...\n2. Identify Tool...\n3. Execute: get_weather(city='北京')",
"finish_reason": "length"
}
模型在 content 里写了思考过程,tool_calls 是空的。以为 thinking mode 干扰了,加上 chat_template_kwargs: {"enable_thinking": False}。
再测:
{
"tool_calls": [],
"content": "<tool_call>\n<function=get_weather>\n<parameter=city>\n北京\n</parameter>\n</function>\n</tool_call>",
"finish_reason": "stop"
}
模型正确生成了 tool_call,但是以 XML 格式写在 content 字段里——tool_calls 还是空!
我做的错误结论
当时以为是模型能力问题(毕竟是首次量化 FP8 版本),我就回滚部署到 Qwen3-32B-AWQ 稠密版(用户请求 --tool-call-parser hermes 正好能解析,工具调用正常)。一周都用稠密版跑 T1。
今天重新测才发现真相
扩大测试到 24 题 × 6 云端模型后,我想"再试一次 A3B"——这次先仔细看 vLLM 文档:
--tool-call-parser: 指定 tool_call 解析器。 支持的值:hermes/mistral/llama3_json/deepseek_v3/qwen3_coder/pythonic/granite/ ... 不同模型家族使用不同的 tool_call 输出格式,parser 必须和模型家族对应。
问题找到了!
Qwen3 家族(Instruct / Coder / A3B)输出格式是:
<tool_call>
<function=get_weather>
<parameter=city>北京</parameter>
</function>
</tool_call>
hermes parser 期望:
<tool_call>{"name":"get_weather","arguments":{"city":"北京"}}</tool_call>
格式完全不一样。hermes 尝试 JSON parse 一个 XML 结构,结果失败就返回空 tool_calls。
修正部署
- --tool-call-parser hermes
+ --tool-call-parser qwen3_coder
重启 vLLM → 重新测试:
{
"tool_calls": [{
"id": "chatcmpl-tool-b82f73f3f3d0ef40",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}],
"content": "",
"finish_reason": "tool_calls"
}
完美解析。同一模型从 0 分变 96 分。
完整 24 题测试结果
24 题覆盖 4 档难度:
- 基础 12 题(新闻/娱乐/生活/工作/财经/体育 × 2)
- 错误恢复 4 题(工具名错、参数歧义、无效工具、多工具协作)
- 安全拒绝 4 题
- 长上下文 4 题(5-10K 长文档 + 工具调用指令)
评分 0-3 分,满分 72。
总分榜(7 模型)
| 排名 | 模型 | 架构 | 总分 | 完美 | avg 延迟 | p95 延迟 |
|---|---|---|---|---|---|---|
| 🥇 | Qwen3.6-35B-A3B-FP8 (本地) | MoE 35B/3B | 69/72 | 20 | 1024ms | 1623ms |
| 🥈 | Qwen3-32B-AWQ (本地) | 稠密 32B 4bit | 66/72 | 20 | 2397ms | 13719ms |
| 🥉 | glm-5-turbo | 云端 | 40/72 | 9 | 8698ms | 25766ms |
| 4 | DeepSeek V3.2 | 云端 | 39/72 | 7 | 7350ms | 28090ms |
| 5 | Step-3.5-Flash | 云端 | 38/72 | 8 | 5337ms | 12044ms |
| 6 | Kimi K2.5 | 云端 | 36/72 | 7 | 7255ms | 20077ms |
| 7 | MiniMax M2.7 | 云端 | 34/72 | 6 | 7803ms | 16263ms |
除了安全这个维度,其他大模型对于调用工具解决问题其实兴趣不大
技术深度分析
为什么 MoE (A3B) 比稠密 (32B) 更强?
本地两个模型直接对比:
| 维度 | Qwen3-32B-AWQ 稠密 | Qwen3.6-A3B-FP8 MoE | 差异 |
|---|---|---|---|
| 总分 | 66/72 | 69/72 | +3 |
| 基础 12 题 | 35/36 | 36/36 全满 | +1 |
| 错误恢复 | 11/12 | 11/12 | 持平 |
| 安全拒绝 | 12/12 | 12/12 | 持平 |
| 长上下文 | 8/12 | 11/12 | +3 |
| avg 延迟 | 2397ms | 1024ms | 快 2.3x |
| p95 延迟 | 13719ms | 1623ms | 快 8.5x |
| 显存 | 18GB (4bit) | 40GB (FP8) | +22GB |
MoE 稀疏激活的原理
Qwen3.6-35B-A3B 的每层:
┌──────────────────────────────┐
│ Attention (dense) │ ← 所有 token 都走
└──────────────────────────────┘
↓
┌──────────────────────────────┐
│ Router (门控网络) │ ← 给每个 token 打分
│ "哪几个专家最适合处理?" │
└──────────────────────────────┘
↓
挑 Top-K=8 个专家 (共 64 个)
↓
┌──────────────────────────────┐
│ Expert 1 ... Expert 64 │
│ (每个 ~500M 参数) │
└──────────────────────────────┘
每个 token 只激活 8/64 个专家 → 实际 FLOPS 等于 3B 稠密模型。
这就是为什么 A3B 推理速度和 3B 稠密一样快,但效果接近 35B 稠密。
FP8 vs 4bit AWQ 的权衡
| 量化 | 精度损失 | 显存 | 推理速度 |
|---|---|---|---|
| FP8 (awq_marlin) | 极小 (~1%) | 35B × 1byte = 35GB | vLLM awq_marlin kernel 加速 |
| 4bit AWQ | 小 (~3%) | 32B × 0.5byte = 16GB | awq_marlin kernel 加速 |
| BF16 (原生) | 无 | 32B × 2byte = 64GB | 原生快但显存爆 |
FP8 对 tool_calling 这种需要精确 JSON 生成的场景更友好——4bit 量化偶尔会生成语法错误的 JSON。
云端模型为什么"只写 content 不调工具"?
5 家云端在 12 道基础题上的行为高度一致:
必调工具题 (6/6 都调):
✅ W2 北京上海高铁
✅ F2 比特币价格
✅ SR1-4 安全拒绝
"模型觉得自己知道"题 (5/6 都不调):
❌ N1 今日新闻
❌ N2 DeepSeek 发布
❌ E1 华语悬疑片
❌ L1 上海天气
❌ W1 阿里腾讯美团股价
❌ F1 上证指数
❌ S1 NBA 比分
A/B 对照实验:加强制 system prompt
"涉及实时信息(新闻/天气/股价/比分)必须调用工具,
不允许凭记忆直接回答。"
结果:glm-5-turbo 和 Kimi K2.5 调用率从 0/5 → 0/5,无变化。反而思考时间从 8s 变 25s——模型"想了更久",但最终选择还是不调工具。
这是 RLHF 的深度偏好。云端模型被奖励"直接给答案"——直接给更省 token、用户满意度更高("模型好聪明")。
对 AI Agent 产品的启示
启示 1:vLLM tool-call-parser 必须和模型家族对应
| 模型家族 | parser |
|---|---|
| Qwen3-Instruct / Coder / A3B | qwen3_coder |
| Hermes 系列 | hermes |
| Llama-3.1 | llama3_json |
| DeepSeek-V2/V3 | deepseek_v3 |
| Mistral | mistral |
| Granite | granite |
| Pythonic 风格 | pythonic |
配错会让模型假装自己不会调工具。我浪费了整整一周。
启示 2:云端模型做 Agent 要绕过 system prompt 限制
如果你必须用云端 API 做 Agent:
❌ 不推荐:在 system prompt 里说"必须调工具"
- 实测 A/B 0 提升
- 反而让延迟暴涨 3 倍
✅ 推荐:在代理层做模型路由
- 检测到"实时信息"关键词(新闻 / 天气 / 股价 / 比分 / 现在)
- 强制路由到本地 Qwen3 或者 强制拼接 web_search 结果到 prompt
启示 3:本地 MoE 是 2026 年 Agent 的答案
Qwen3.6-35B-A3B-FP8 + vLLM + qwen3_coder parser 是目前本地 Agent 的最佳组合:
- 一张 4090 48GB 跑得动
- avg 1 秒响应,p95 1.6 秒
- 工具调用 96% 准确
- 长上下文 64K 够用
比所有云端付费 API 都强。
启示 4:显存权衡
| 场景 | 推荐 |
|---|---|
| 单点 T1 · 只做 Agent | Qwen3.6-A3B-FP8 (40GB) |
| T1 + Embedding 同卡 | Qwen3-32B-AWQ (18GB) 留 30GB 给 embedding |
| 双卡部署 | 一张 A3B + 一张 Qwen3-Coder-FP8 |
完整复现步骤
# 1. 下载模型
modelscope download --model Qwen/Qwen3.6-35B-A3B-FP8 --local_dir /root/models/Qwen3.6-35B-A3B-FP8
# 2. 安装 vLLM 0.19.0+(A3B 支持需要新版本)
pip install vllm==0.19.0 flashinfer ninja
# 3. 启动(关键是 parser)
vllm serve /root/models/Qwen3.6-35B-A3B-FP8 \
--host 0.0.0.0 --port 18000 \
--max-model-len 65536 \
--quantization fp8 \
--kv-cache-dtype auto \
--gpu-memory-utilization 0.85 \
--enable-prefix-caching \
--enable-chunked-prefill \
--max-num-seqs 16 \
--max-num-batched-tokens 8192 \
--enable-auto-tool-choice \
--tool-call-parser qwen3_coder \ # 👈 关键
--served-model-name Qwen3.6-A3B \
--trust-remote-code
# 4. 测试
import json, urllib.request
payload = {
"model": "Qwen3.6-A3B",
"messages": [{"role": "user", "content": "北京今天天气"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
}
}],
"tool_choice": "auto",
"chat_template_kwargs": {"enable_thinking": False},
}
req = urllib.request.Request("http://127.0.0.1:18000/v1/chat/completions",
data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"})
print(json.load(urllib.request.urlopen(req)))
期望输出:
{
"choices": [{
"message": {
"role": "assistant",
"content": "",
"tool_calls": [{
"function": {
"name": "get_weather",
"arguments": "{\"city\": \"北京\"}"
}
}]
},
"finish_reason": "tool_calls"
}]
}
开源测试仓库
完整的 24 题 prompt + 评分脚本 + 7 模型原始 JSON + 图表生成代码:
- github.com/MerkyorLynn/Lynn/tree/main/tests/benchmarks(已push)
欢迎 PR 加自己家模型的测试结果。
总结
- vLLM 部署新模型,parser 配置永远是第一个坑
- MoE 稀疏激活是 2026 Agent 的必然趋势 — 延迟低 + 精度高 + 显存合理
- 本地模型在 tool_calling 场景碾压云端 — 但长上下文 ≥128K 场景云端仍优
- 不要完全相信模型在 system prompt 里"听话"的承诺 — RLHF 偏好改不掉,该绕就绕
如果你也在部署 Qwen3 家族或做 AI Agent 产品,欢迎评论交流。
利益相关:Lynn 作者 · Apache 2.0 开源。测试在自建 GPU 服务器 + 腾讯云 Brain 代理上跑。