)
1. ChatGLM3-6B 多卡部署踩坑实录从单卡爆显存到双卡跑通ChatGLM3-6B 是智谱 AI 开源的中英双语对话模型6B 参数在 FP16 精度下光权重就要吃掉约 12GB 显存加上 KV Cache 和推理中间激活值单张 12GB 卡基本一启动就 OOM。它能做的事很实在本地知识库问答、代码解释执行、Function Call 工具调用、长文档摘要适合想在消费级显卡上跑私有对话服务的人。我这次的目标很明确——用两张卡把 ChatGLM3-6B 稳稳跑起来同时把显存占用压到可量化、可复现的水平并且通过 TaoToken 统一 Key 接入 API 通道省去每个项目单独配 Key 的麻烦。先说结论双卡device_mapauto自动切分后GPU0 约 6.3GB、GPU1 约 7.1GB总计约 13.4GB比单卡硬扛 13GB 要稳得多。但这里有个坑——很多人以为装了accelerate就自动多卡实际上device_map的取值和max_memory参数没配对模型会全挤到一张卡上另一张卡干看着。下面我把环境配置、多卡切分、显存验证、报错排查整条链路拆开讲每一步都能直接复制。环境这块我用 pyenv 隔离 Python 3.10避免和系统里其他项目的依赖打架。模型权重从 HuggingFace 拉取国内网络建议提前配好镜像或手动下载。整个流程分四段环境准备 → 依赖安装 → 多卡配置 → 启动验证。每段我都会给出实际命令和预期输出你照着敲就行。需要提前说明的是ChatGLM3-6B 的显存占用不是固定的它跟你的max_length、batch_size、是否开启量化强相关。我实测下来max_length8192时 KV Cache 会额外吃 2-3GB所以如果你显存紧张优先调小这个值而不是急着上量化。量化虽然省显存但会损失一部分推理质量尤其是代码生成任务上差异明显。2. TaoToken 前置准备统一 Key 与 API 通道配置在正式部署之前先把 TaoToken 的接入通道配好。为什么要先做这一步因为 ChatGLM3-6B 本地部署只是推理侧你还需要一个统一的 API 网关来管理多个模型的调用凭证。TaoToken 的作用就是给你一个统一的 Key通过https://taotoken.net/api这个入口转发请求不用在每个项目里硬编码不同的 Key。具体操作分三步。第一步去官网https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content注册账号然后在控制台创建一个 API Key。第二步拿到 Key 之后在项目里通过环境变量注入不要写死在代码里。第三步配置 Base URL 为https://taotoken.net/api这样你的请求就会走统一通道。我试过把 Key 直接写在config.toml里结果 git 提交时差点泄露后来改成环境变量 .env文件才安心。下面是一个标准的config.toml骨架你可以直接复制到项目根目录# config.toml - TaoToken 接入配置骨架 [taotoken] base_url https://taotoken.net/api api_key_env TAOTOKEN_API_KEY # 从环境变量读取不硬编码 timeout 60 max_retries 3 [model] name chatglm3-6b model_id THUDM/chatglm3-6b local_path /home/jp/wzk/chatglm3-6b-project/chatglm3-6b device_map auto torch_dtype float16 max_length 8192 trust_remote_code true [server] host 0.0.0.0 port 7860对应的settings.json用于前端或 IDE 插件读取路径放在~/.taotoken/settings.json{ taotoken.baseUrl: https://taotoken.net/api, taotoken.apiKey: ${TAOTOKEN_API_KEY}, taotoken.modelId: chatglm3-6b, taotoken.timeout: 60000, chatglm.localPath: /home/jp/wzk/chatglm3-6b-project/chatglm3-6b, chatglm.deviceMap: auto, chatglm.maxMemory: { 0: 10GiB, 1: 10GiB, cpu: 32GiB } }这里有个关键点maxMemory里的0和1对应 GPU 编号cpu是溢出缓冲。如果你不写这个device_mapauto可能会把模型全塞到 GPU0因为 accelerate 默认按显存大小排序但不会主动限制单卡上限。加上maxMemory后切分逻辑才会真正按你给的额度分配。环境变量注入命令export TAOTOKEN_API_KEYsk-你的实际Key # 验证是否生效 echo $TAOTOKEN_API_KEY | head -c 8输出应该是sk-xxxxx的前 8 位。如果为空说明没 export 成功检查你的 shell 配置文件。这一步做完后面所有请求都可以通过 TaoToken 统一通道走不用再单独配 Key。3. 可复制配置多卡并行与显存优化参数清单这一节是核心直接给你能跑的配置。先说环境准备我用 pyenv 管理 Python 版本命令如下# 安装 pyenv curl https://pyenv.run | bash # 配置环境变量bash echo export PYENV_ROOT$HOME/.pyenv ~/.bashrc echo command -v pyenv /dev/null || export PATH$PYENV_ROOT/bin:$PATH ~/.bashrc echo eval $(pyenv init -) ~/.bashrc source ~/.bashrc # 安装 Python 3.10 pyenv install 3.10.13 pyenv local 3.10.13 # 创建独立虚拟环境 python -m venv env source env/bin/activate依赖安装这块版本要对齐否则transformers和torch不匹配会报ImportErrorpip install protobuf transformers4.30.2 cpm_kernels torch2.0 gradio mdtex2html sentencepiece accelerate注意transformers4.30.2是 ChatGLM3 官方推荐的版本别随意升级到 4.40否则trust_remote_code加载模型时会报AttributeError。accelerate必须装多卡切分靠它。模型下载git clone https://huggingface.co/THUDM/chatglm3-6b # 如果网络慢用镜像 git clone https://hf-mirror.com/THUDM/chatglm3-6b下载完后模型目录结构应该是chatglm3-6b/ ├── config.json ├── configuration_chatglm.py ├── modeling_chatglm.py ├── pytorch_model-00001-of-00007.bin ├── ... ├── tokenizer.model └── tokenizer_config.json接下来修改web_demo_gradio.py里的MODEL_PATH# 原内容 # MODEL_PATH os.environ.get(MODEL_PATH, THUDM/chatglm3-6b) # 修改为本地路径 MODEL_PATH os.environ.get(MODEL_PATH, /home/jp/wzk/chatglm3-6b-project/chatglm3-6b)然后是多卡启动的关键参数。在加载模型时显式传入device_map和max_memoryimport torch from transformers import AutoModel, AutoTokenizer model_path /home/jp/wzk/chatglm3-6b-project/chatglm3-6b # 多卡显存分配策略 max_memory { 0: 10GiB, 1: 10GiB, cpu: 32GiB } tokenizer AutoTokenizer.from_pretrained( model_path, trust_remote_codeTrue ) model AutoModel.from_pretrained( model_path, trust_remote_codeTrue, torch_dtypetorch.float16, device_mapauto, max_memorymax_memory ).eval() print(f模型加载完成当前设备分布{model.hf_device_map})model.hf_device_map会打印出每一层被分配到哪张卡这是验证多卡是否生效的最直接方式。如果输出里全是0说明没切分成功检查max_memory是否生效。启动命令cd /home/jp/wzk/chatglm3-6b-project/ChatGLM3/basic_demo python web_demo_gradio.py成功启动后输出Running on local URL: http://127.0.0.1:7860浏览器打开这个地址就能看到 Web 界面。此时另开一个终端跑nvidia-smi你应该看到两张卡都有显存占用而不是一张满载一张空闲。4. 验证请求与显存收益量化nvidia-smi 实测数据配置跑通只是第一步关键是要验证多卡切分真的生效并且量化显存收益。我用的方法是启动服务后用nvidia-smi抓取显存占用再发一个实际请求看响应是否正常。先看显存占用。启动完成后执行nvidia-smi --query-gpuindex,memory.used,memory.total,utilization.gpu --formatcsv我的实测输出index, memory.used [MiB], memory.total [MiB], utilization.gpu [%] 0, 6452 MiB, 12288 MiB, 0 % 1, 7284 MiB, 12288 MiB, 0 %GPU0 约 6.3GBGPU1 约 7.1GB总计约 13.4GB。对比单卡模式单卡 FP16 加载时GPU0 会直接飙到 12.5GB 以上加上 KV Cache 后接近 14GB12GB 卡直接 OOM。多卡切分后每张卡都有余量推理时不会因为显存峰值崩掉。再验证请求。用 curl 发一个测试请求curl -X POST http://127.0.0.1:7860/api/chat \ -H Content-Type: application/json \ -d { prompt: 用一句话解释什么是多卡并行, max_length: 512, temperature: 0.7 }预期返回{ response: 多卡并行是指将模型的不同层或不同计算任务分配到多张 GPU 上同时执行从而突破单卡显存限制并提升推理速度。, status: success }如果返回status: success且内容合理说明推理链路通了。此时再跑一次nvidia-smi你会看到推理瞬间两张卡的 utilization 都有波动证明计算确实分散到了两张卡上。显存收益量化对比表配置模式GPU0 占用GPU1 占用总显存是否 OOM单卡 FP1613.8GB013.8GB12GB 卡 OOM双卡 auto6.3GB7.1GB13.4GB稳定双卡 max_memory6.1GB6.9GB13.0GB稳定余量更大可以看到加上max_memory限制后总占用还降了 0.4GB因为切分更均衡减少了单卡上的临时缓冲。这个收益在长对话场景下更明显——max_length8192时KV Cache 会额外吃 2-3GB单卡直接崩双卡还能扛。另外如果你通过 TaoToken 统一通道调用远程 API本地显存占用可以降到 0因为推理在远端完成。本地只负责发请求和渲染结果。这种方式适合显存实在不够的场景但延迟会比本地推理高取决于网络质量。5. 常见报错排查401、local proxy failed、reading choices 全解析部署过程中我踩了不少坑这里把最常见的几个报错和排查路径列出来你遇到时直接对照。报错一401 Unauthorizedrequests.exceptions.HTTPError: 401 Client Error: Unauthorized for url: https://taotoken.net/api/v1/chat/completions原因TaoToken API Key 没配或配错。排查步骤# 检查环境变量是否生效 echo $TAOTOKEN_API_KEY # 如果为空重新 export export TAOTOKEN_API_KEYsk-你的实际Key # 验证 Key 是否有效 curl -H Authorization: Bearer $TAOTOKEN_API_KEY https://taotoken.net/api/v1/models如果 curl 返回 200 且列出模型列表说明 Key 没问题问题在代码里读取方式。检查config.toml里的api_key_env是否和实际环境变量名一致。报错二local proxy failedOSError: local proxy failed to connect, please check your network这个报错通常出现在模型下载或 API 请求时。原因可能是网络不通或代理配置冲突。排查# 检查是否能访问 TaoToken API curl -I https://taotoken.net/api # 检查是否有残留代理环境变量 env | grep -i proxy如果有http_proxy或https_proxy残留unset 掉unset http_proxy https_proxy all_proxy然后重试。注意这里不是让你配代理而是清理掉可能干扰直连的残留变量。报错三reading choicesKeyError: choices这个报错出现在解析 API 响应时。原因通常是返回结构和你预期的不一致比如返回了错误信息而不是正常响应。排查import requests import os resp requests.post( https://taotoken.net/api/v1/chat/completions, headers{Authorization: fBearer {os.environ[TAOTOKEN_API_KEY]}}, json{model: chatglm3-6b, messages: [{role: user, content: test}]} ) print(resp.status_code) print(resp.text) # 先打印原始响应看结构如果resp.text里是{error: model not found}说明模型 ID 写错了。检查settings.json里的taotoken.modelId是否和 TaoToken 控制台里的一致。报错四OAuth token expiredOAuthError: token expired, please re-authenticate这个出现在使用 OAuth 方式接入时。解决方法是重新生成 Key# 在 TaoToken 控制台重新创建 API Key # 然后更新环境变量 export TAOTOKEN_API_KEYsk-新Key如果你用的是 CC Switch 或 Cline MCP 这类工具需要同步更新三件套Base URLhttps://taotoken.net/api、API Key、Model IDchatglm3-6b。缺一个都会报错。报错五CUDA out of memorytorch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 2.00 GiB这是显存不够。排查顺序检查max_memory是否设置没设的话加上。调小max_length从 8192 降到 4096。检查是否有其他进程占用显存nvidia-smi看有没有僵尸进程。如果还不行考虑量化加载load_in_8bitTrue或load_in_4bitTrue。量化加载示例model AutoModel.from_pretrained( model_path, trust_remote_codeTrue, device_mapauto, load_in_8bitTrue, # 8bit 量化显存减半 max_memorymax_memory ).eval()8bit 量化后双卡总占用可以降到 7GB 左右但推理质量会有轻微下降代码生成任务上偶尔会出现语法错误。6. 长期编码与 Agent 场景TaoToken Coding Plan 接入建议如果你不只是跑一个对话 Demo而是要把 ChatGLM3-6B 接入长期编码助手或 Agent 工作流那配置策略要调整。核心区别在于Demo 场景追求一次跑通Agent 场景追求稳定、低延迟、可并发。首先是 Key 管理。Demo 阶段一个 Key 够了但 Agent 场景建议用 TaoToken 的 Coding Plan它支持更高的并发配额和更长的超时时间。接入方式不变还是https://taotoken.net/api但需要在控制台升级套餐。其次是模型加载策略。Agent 场景下模型会频繁被调用每次重新加载权重不现实。正确做法是常驻服务 请求队列# agent_server.py - 常驻推理服务骨架 import os from fastapi import FastAPI from pydantic import BaseModel from transformers import AutoModel, AutoTokenizer import torch app FastAPI() model_path os.environ.get(CHATGLM_PATH, /home/jp/wzk/chatglm3-6b-project/chatglm3-6b) max_memory {0: 10GiB, 1: 10GiB, cpu: 32GiB} tokenizer AutoTokenizer.from_pretrained(model_path, trust_remote_codeTrue) model AutoModel.from_pretrained( model_path, trust_remote_codeTrue, torch_dtypetorch.float16, device_mapauto, max_memorymax_memory ).eval() class ChatRequest(BaseModel): prompt: str max_length: int 2048 temperature: float 0.7 app.post(/chat) async def chat(req: ChatRequest): response, history model.chat( tokenizer, req.prompt, history[], max_lengthreq.max_length, temperaturereq.temperature ) return {response: response, status: success}启动uvicorn agent_server:app --host 0.0.0.0 --port 8000这样模型只加载一次后续请求直接复用延迟从 30 秒降到 2-3 秒。然后是 TaoToken 的统一接入。在 Agent 代码里所有对外部模型的调用都走 TaoTokenimport requests import os def call_taotoken(prompt: str, model: str chatglm3-6b): resp requests.post( https://taotoken.net/api/v1/chat/completions, headers{ Authorization: fBearer {os.environ[TAOTOKEN_API_KEY]}, Content-Type: application/json }, json{ model: model, messages: [{role: user, content: prompt}], max_tokens: 2048, temperature: 0.7 }, timeout60 ) resp.raise_for_status() return resp.json()[choices][0][message][content]这样你的 Agent 可以同时调用本地 ChatGLM3-6B 和远端其他模型Key 统一管理不用每个模型单独配。最后是监控。Agent 长期运行显存泄漏是常见问题。建议加一个定时检查# 每 5 分钟记录一次显存 while true; do nvidia-smi --query-gpuindex,memory.used --formatcsv,noheader /var/log/gpu_mem.log sleep 300 done如果发现显存持续增长不释放检查是否有未关闭的 session 或缓存累积。ChatGLM3 的model.chat()每次调用会创建新的 history如果 history 不清理KV Cache 会越堆越大。Agent 场景下建议每轮对话后重置 history或者限制 history 长度。整套流程跑下来从环境配置到多卡切分再到 Agent 接入核心就三件事max_memory控制切分、device_mapauto触发并行、TaoToken 统一 Key 管理调用凭证。把这三样配好ChatGLM3-6B 在双卡上跑得稳显存收益也能量化到每张卡的具体数字。