上周三智谱把 GLM-4 开源版放出来,我当天晚上就拉了权重往本地部署。心想云端 GLM-4 的 API 我已经跑通了,开源版应该改个端口就完事——结果接了两遍,踩了三个坑,折腾到凌晨两点才把 chat completions 跑通。

核心问题就一句话:GLM-4 开源版本地部署的 base_url 路径格式、鉴权机制、model name 字段,和云端版完全是两套逻辑,但官方文档混在一起写,第一次接必踩。这篇把三处差异拆开讲清楚,附完整代码和报错排查。

这篇适合谁

  • 已经在用云端 GLM-4 API,准备本地部署开源版的同学
  • 用 vLLM / TGI 跑 GLM-4 开源权重,OpenAI 兼容层死活 401 的同学
  • 想在 Cursor / Cline 里接自部署 GLM-4 但 model name 不知道怎么填的同学
  • 团队有私有化部署需求,需要搞清楚鉴权差异的后端工程师

整体流程

  1. 搞清楚云端版 vs 开源版的三处核心差异(base_url / 鉴权 / model name)
  2. 本地部署开源版,启动 inference endpoint
  3. 用 OpenAI SDK 接入,配置正确的参数
  4. 验证 streaming 和 function calling 是否正常
  5. 踩坑排查(附真实报错)

先说结论

对比项 云端版(open.bigmodel.cn) 开源版(本地部署)
base_url https://open.bigmodel.cn/api/paas/v4/ http://localhost:8000/v1/(取决于你的端口)
鉴权方式 Bearer Token(平台生成的 API Key,据 SDK 源码推断内部走 JWT 刷新,Key 格式为 xxxxxxxx.yyyyyyyy 无鉴权 / 自定义 token(vLLM 启动参数指定)
model name glm-4 / glm-4-air 等固定字符串 本地权重路径名,如 THUDM/glm-4-9b-chat
流式输出 stream=True 直接用 同,SSE 数据块中 delta.content 为空时需跳过,否则会拿到 None
Function Calling 原生支持 取决于推理框架版本,建议使用较新稳定版 vLLM 并在启动时加 --enable-auto-tool-choice

第一处坑:base_url 路径格式

云端版的 base_url 大家都熟:

base_url = "https://open.bigmodel.cn/api/paas/v4/"

注意末尾有斜杠,路径是 /api/paas/v4/

开源版用 vLLM 启动后,endpoint 是标准的 OpenAI 兼容格式:

base_url = "http://localhost:8000/v1"

这里没有 /api/paas/ 这层路径。我第一次接的时候下意识写了 http://localhost:8000/api/paas/v4/,直接 404。说实话这个错误挺蠢的,但文档里两种写法混在同一页,扫一眼真的会搞混。

第二处坑:鉴权方式完全不同

云端版的 API Key 看起来是个普通字符串,但底层其实走的是 JWT 机制——你拿到的 Key 格式是 xxxxxxxx.yyyyyyyy,据 SDK 源码推断前半段是 ID、后半段是 Secret,SDK 内部会用它生成带过期时间的 JWT token(官方文档未公开此字段拆分含义,以 SDK 源码为准)。

开源版本地部署?默认完全不需要鉴权。vLLM 启动时如果没加 --api-key 参数,随便传什么都行:

client = OpenAI(
    api_key="whatever",  # 本地部署随便填
    base_url="http://localhost:8000/v1"
)

我第一次把云端的 Key 原封不动贴过来,报了这个错:

openai.AuthenticationError: Error code: 401 - {'error': {'message': 'Incorrect API key provided'}}

当时人傻了——本地服务还能 401?后来发现是我 vLLM 启动时加了 --api-key mytoken123 但忘了改客户端代码里的 key。如果你启动时指定了 api-key,客户端必须传完全一致的字符串,不是云端那套 JWT。

# vLLM 启动命令(指定自定义 token)
# 注意:vllm serve 子命令需要 vLLM 0.4.1+;更早版本请改用:
# python -m vllm.entrypoints.openai.api_server --model THUDM/glm-4-9b-chat --api-key mytoken123
vllm serve THUDM/glm-4-9b-chat --api-key mytoken123

第三处坑:model name 字段格式

这个最坑。云端版 model name 是智谱定义的产品名:

model = "glm-4"  # 云端版

开源版的 model name 必须和你加载的权重路径一致:

model = "THUDM/glm-4-9b-chat"  # 开源版

如果你写 glm-4,vLLM 会返回:

{"error": {"message": "The model `glm-4` does not exist."}}

报错信息倒是很明确,但问题是——你怎么知道该填什么?答案是看 vLLM 启动日志里的 model_name 字段,或者直接调 /v1/models 接口:

curl http://localhost:8000/v1/models

返回的 JSON 里 id 字段就是你该填的 model name。

graph TD
    A[确定部署方式] --> B{云端 or 开源?}
    B -->|云端| C[base_url: open.bigmodel.cn/api/paas/v4/]
    B -->|开源本地| D[base_url: localhost:8000/v1]
    C --> E[鉴权: 平台 API Key - JWT 机制]
    D --> F[鉴权: 无 / 自定义 --api-key]
    E --> G[model: glm-4]
    F --> H[model: THUDM/glm-4-9b-chat]
    G --> I[调用成功 ✅]
    H --> I

完整的开源版接入代码

把三处差异对齐后,完整代码其实很短:

from openai import OpenAI

client = OpenAI(
    api_key="mytoken123",
    base_url="http://localhost:8000/v1"
)

然后正常调用:

response = client.chat.completions.create(
    model="THUDM/glm-4-9b-chat",
    messages=[{"role": "user", "content": "你好"}]
)

流式输出也是标准写法:

stream = client.chat.completions.create(
    model="THUDM/glm-4-9b-chat",
    messages=[{"role": "user", "content": "写一段快排"}],
    stream=True
)

逐块读取:

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")

不同场景怎么选

个人开发者 / 快速验证想法:直接用云端 API。glm-4 的云端版注册就能用,免费额度够你跑通原型。本地部署 9B 模型至少要一张 24G 显存的卡,折腾环境的时间够你把功能写完了。

团队生产环境 / 数据不能出网:开源版本地部署。走 vLLM 起服务,前面套一层 Nginx 做负载均衡,鉴权用 --api-key 参数配合内网网关。

多模型混用 / 想同时调 GLM-4 + Claude + GPT:这种场景建议走聚合 API 网关。比如 OpenRouter、ofox.io 这类平台,改一个 base_url 就能切不同模型,不用每个模型单独维护一套鉴权逻辑。对团队来说省的是运维成本不是那几毛钱 token 费。使用前建议在对应平台确认所需 model ID 的实际名称,各平台命名规范不尽相同。

接入 Cursor / Cline 等编辑器:如果是本地部署的开源版,在编辑器设置里填 http://localhost:8000/v1 作为 base_url,model name 填 THUDM/glm-4-9b-chat,API Key 随便填或者填你启动时设的那个。

踩坑记录 / 常见问题 FAQ

Q: 开源版用 zhipuai SDK 能调吗?

不能。zhipuai 这个包是专门对接云端平台的,内部写死了 JWT 签名逻辑和 open.bigmodel.cn 的域名。本地部署只能用 openai SDK 或者裸 HTTP 请求。

Q: vLLM 启动后调 /v1/chat/completions 返回 404?

先确认 vLLM 版本,建议使用较新的稳定版本。另外确认你启动时没加 --disable-frontend-multiprocessing 之类影响路由的参数。

Q: 本地部署的 GLM-4 能用 Function Calling 吗?

能,但有前提:需要使用支持该功能的较新稳定版 vLLM,且启动时加 --enable-auto-tool-choice(该参数在 vLLM 0.4.x 即已引入,具体最低版本要求建议查阅 vLLM 官方 changelog)。我测下来 9B 版本的 tool use 准确率比云端 GLM-4 差一截,毕竟参数量摆在那。复杂的多步 tool calling 建议还是走云端大参数版。

Q: model name 填错了会报什么错?

返回 HTTP 404,body 是 {"error": {"message": "The model 'xxx' does not exist."}}。不是 401 也不是 500,别往鉴权方向排查。

Q: 云端版的 API Key 格式是什么样的?怎么区分?

云端 Key 格式是 xxxxxxxxxxxxxxxx.yyyyyyyyyyyyyyyy,中间有个点分隔。如果你的 Key 里没有点,那大概率是你自己设的本地 token,别往云端传。

小结

GLM-4 开源版接入本身不难,难的是文档把两套体系混在一起写。记住三个差异点:base_url 没有 /api/paas/ 这层、鉴权不走 JWT 而是纯字符串匹配、model name 要用 HuggingFace 风格的路径名。搞清楚这三点后,接入本身不复杂。

我目前的做法是本地开发用开源 9B 版本快速迭代(响应快、不花钱),上线前再切到云端大参数版本做质量验证。两套代码唯一的区别就是一个 config 文件里的三个字段,切换成本几乎为零。

更多推荐