[控场AI]
· 6 分钟阅读· 3,254 字

Claude Code 对接自托管模型的三个坑

Claude Code 对接自托管模型的三个坑

Claude Code 接自托管 Qwen 模型的三个坑与修复:工具解析器、模型名统一、base URL 与鉴权。

作者用 Claude Code 对接 vLLM 部署的 4-bit 量化 Qwen3 Coder Next(80B MoE),跑通后整理出一份失败清单。三个主要坑分别是:普通 vllm serve 不接受工具定义导致 400,需加 --enable-auto-tool-choice 与匹配模型家族的 --tool-call-parser;Claude Code 默认发送 claude-* 模型名并静默调用后台小模型,所有模型角色必须指向同一自托管名;base URL 要停在 workload 层,鉴权方式二选一。文章还解释了 4-bit AWQ 把权重压到约 40GB、MoE 让每 token 只算约 3B,因而单张 A100 80GB 可跑,并坦承实验只验证了管道连通性,未测质量、吞吐与长上下文真实负载。

Claude Code 可以连上任何支持 Anthropic Messages API 的端点,听起来像改一行配置的事。实际把端点指向自托管模型后,会以几种可预测的方式失败,而且每一种都像是模型坏了。

有人做了一次完整实验:用 Claude Code 驱动 Qwen3 Coder Next——一个 80B 的混合专家(MoE)编程模型,由 vLLM 部署在一张租来的 A100 80GB 上。这篇文章只讲失败清单:哪里断了、为什么断、怎么修。

Claude Code 对接自托管模型实验

坑一:工具调用直接返回 400

Claude Code 是个 agent,几乎每个请求都带工具定义:读文件、执行命令、编辑组件。普通的 vllm serve 不接受这些定义,带工具的请求会被 vLLM 以 HTTP 400 拒绝。从 Claude Code 那边看,就像模型坏了。

修复方式是在服务端加两个参数,模型卡也是这么建议的:

--enable-auto-tool-choice --tool-call-parser qwen3_coder

解析器必须和模型家族匹配。加上之后,整个会话跑通了:进入 plan 模式、写文件、装依赖、跑 npm run build,然后继续编辑,工具调用全程正常。

Anthropic Messages API 与 Claude Code 的工具协议

Claude Code 之所以能驱动任意兼容端点,是因为它把所有 agent 能力都编码成了标准化的工具调用(tool use):每一次「读文件」「跑命令」「改代码」在 API 层都是一个带 tools 数组的 /v1/messages 请求,模型需要返回结构化的 tool_use 块,客户端执行后再以 tool_result 回灌。这意味着端点不仅要能对话,还要能理解并生成这套 JSON schema。vLLM 默认的 chat 补全路径只负责普通文本生成,不会把工具定义翻译成模型的 prompt 模板,也不会把模型输出解析回工具调用块,所以带工具的请求会直接被拒。--enable-auto-tool-choice 负责开启这条路径,--tool-call-parser 则指定用哪个解析器把模型的原始文本按特定格式拆成工具调用——不同模型家族(Qwen、Llama、Mistral 等)的输出格式不一样,选错解析器同样会失败。

坑二:模型名对不上

这个坑在第一条 prompt 就会触发。Claude Code 默认发送 Claude 的模型名,而且它还会静默调用一个更小的模型做后台工作。只服务一个开源模型的端点根本不知道 claude-something 是什么。

在那台端点上,管理器会检查请求的模型名,只接受 workload slug、它自己的模型名、default,或者干脆不带模型名。其他任何值都会拿到 400,报错为 "Model '…' is not available on this endpoint"。所以 Claude Code 知道的每个模型角色都得指向同一个名字:主模型、后台小模型,还有 sonnet 和 opus 别名——不然 /model 选择器和 opusplan 依然会发 claude-* 名字。

也就是说,需要四个环境变量,外加 base URL 和 token。

坑三:base URL 与鉴权头

模型名旁边还藏着两个小陷阱。

base URL 要停在 workload 这一层。Claude Code 自己会追加 /v1/messages,如果你把完整路由粘进去,路径里就会出现两次 /v1/messages。

关于 key:ANTHROPIC_AUTH_TOKEN 走 Authorization: Bearer;ANTHROPIC_API_KEY 走 x-api-key,也能用,但它会覆盖订阅登录,交互式 Claude Code 会要求你确认一次自定义 key。如果两个都设了且值不同,管理器会以 401 拒绝请求。选一个就好。

为什么模型能塞进一张卡

有两个独立的概念,分开看更清楚。权重是 cyankiwi 发布在 Hugging Face 上的 4-bit AWQ 量化,把 80B 参数压到约 40GB,剩下约 40GB 留给 KV cache。MoE 架构是速度的来源:全部 80B 权重常驻显存,但每个 token 只经过其中约 3B。

服务端按模型的原生上下文 262,144 token 配置,KV cache 用 FP8 存储以便和权重一起塞下,最多同时处理两个序列。最后这个数字很诚实:这是单人开发环境。

AWQ 量化与 MoE 的显存经济学

AWQ(Activation-aware Weight Quantization)是一种训练后量化方法,它不只看权重本身的数值分布,还会参考激活值来判断哪些通道更重要,从而在压到 4-bit 时尽量保住精度。80B 参数按 4-bit 存储约等于每参数 0.5 字节,加上量化缩放因子等开销后落在 40GB 量级,正好给 KV cache 留出空间。KV cache 是推理时的历史键值缓存,随上下文长度和并发序列数线性增长,所以作者用 FP8 存储并把并发压到两条。MoE(Mixture of Experts)则把前馈层拆成多个专家,每个 token 只激活其中一小部分——这里约 3B。要注意的是,MoE 省的是计算量而非显存:全部 80B 权重仍需常驻,这也是为什么量化是这张卡能跑起来的前提。

这次实验没能证明什么

跑通只证明管道通了,不证明质量。一个落地页不等于大型遗留系统重构;这是 4-bit 量化模型,质量损失没有测量;也没有和相同任务上的托管版 Claude 做对比。256K 上下文是配置上了,但没被真正用满——会话中途只用了 31.7K token。在 A100 上只做了冒烟测试,所以没有吞吐数据。而且这是 Claude Code 驱动 Qwen,不是 Claude,agent 行为取决于模型对 Claude Code 工具协议的遵循程度。

成本也有同样的保留。按 GPU 小时付费(当天 A100 是 $1.79/h)只有在多个开发者或 agent 把卡跑满时,才比按 token 付费划算。

冒烟测试的边界

冒烟测试(smoke test)指的是只验证系统能启动并跑通一条最小路径,不评估性能与正确性。它回答的是「管道通不通」,而不是「好不好用」。要判断一套自托管方案是否可替代托管 API,通常还需要:固定任务集上的质量对比(如代码通过率、指令遵循度)、不同并发下的吞吐与首 token 延迟、长上下文真实负载下的显存与衰减曲线,以及量化前后的精度落差测量。作者明确列出这些缺口,是因为单个落地页任务的成功极易被误读成通用能力——agent 类任务对工具协议遵循度极为敏感,任务越复杂,模型格式偏差被放大的概率越高。

值得复制的部分

模型来自 Qwen 团队的 Qwen3 Coder Next,4-bit 版是 cyankiwi 的,vLLM 负责服务,Anthropic 让 Claude Code 兼容自定义端点。把这几样接起来就是这次实验的全部。

如果你的目标是复现,核心就是三件事:开启工具调用解析器、把所有模型角色指向同一个名字、把 base URL 停在 workload 层并只选一种鉴权方式。剩下的都是围绕这个模型和这张卡的调优细节。

#Claude Code#自托管模型#vLLM#Qwen3 Coder#Anthropic Messages API#工具调用#MoE 量化模型
分享:

相关推荐