Atlas 800 A2 单机部署 GLM-5.3-Flash:六轮调优(附最优启动参数)

8 张昇腾 910B3,六轮参数迭代。
这篇记录了我们如何把一个连智能体调用都跑不起来的部署,调到冷启动 12.7 万 token 稳定服务的全过程。

网上 Atlas 800 A2 + vLLM-Ascend 的完整实测资料很少,多数停留在"能跑起来"。希望这篇能补上"跑得稳"这一块。


一、环境交代

项目配置
服务器Atlas 800 A2
NPU8 × 昇腾 910B3(64GB HBM/卡)
模型GLM-5.3-Flash-w8a8(昇腾量化版)
推理框架vLLM 0.23.0(vllm-ascend,TP8 + Expert Parallel)
互联HCCS 392GB/s

二、初次尝试,发现问题:单机部署KV 缓存池受限

选定部分常用参数启动相当顺利:8 张卡全部识别,权重正常加载,服务起来后用普通对话一问一答,响应飞快——看上去万事俱备。

直到把服务接入真实的智能体业务,问题才暴露出来:

场景表现
普通对话✅ 秒回,一切正常
智能体调用(带 tools)一直转圈,直到超时

而服务端日志显示的是成功:

POST /v1/chat/completions 200 OK

客户端却一个字都收不到,同时引擎侧毫无动静——GPU 利用率 0%,没有任何生成活动。

第一反应是工具调用解析器出了问题(GLM 系列的 parser 历史上确实有流式中断的 bug)。我们按这个方向查了一轮,把 parser、端点、tool_choice 各模式全测了一遍——全部正常。方向错了。

真正的突破口是 vLLM 的 Prometheus 指标:

curl -s http://<host>:8077/metrics | grep cache_config_info

返回里有这么一行:

num_gpu_blocks = 1146
block_size     = 4

1146 × 4 = 4584 tokens。

而我们的 --max-model-len 配置是多少?133120(声称支持 133K)。

也就是说:这个服务声称能处理 13 万 token 的上下文,但它的 KV 缓存池实际只能装 4584 个 token

再对照调度指标,真相彻底清晰:

vllm:num_requests_running  = 0
vllm:num_requests_waiting  = 1   (reason = "capacity")
vllm:kv_cache_usage_perc   = 0.0

请求被调度器以 capacity(容量不足) 为由拒绝进入 GPU,永远排在等待队列里。

为什么普通对话正常、智能体必挂?

  • 普通对话提示词:几十 token → 轻松进入 4584 的池子 ✅
  • 智能体系统提示词 + 工具定义:轻松破万 → 永远进不去 ❌

阈值实测精确落在 4584,与 KV 池容量完全吻合。

顺带发现:队头阻塞

更糟的是,我们把一个超限请求打进队列后,再发一个只有 2 个 token 的"你好"——也被堵死了 30 秒

一个超限请求,能毒化整个服务。

那句"200 OK"是怎么回事?

流式响应(SSE)的 HTTP 头是立即返回的,所以日志上看到 200。但引擎侧从未真正调度这个请求,自然一个字节都吐不出来。

经验一:vLLM 出现「200 OK 但引擎零活动」,先去 /metricsnum_requests_waitingreasoncache_config_info 的 KV 池大小,比查解析器快十倍。


三、六轮参数迭代(核心)

这是我们调试的完整轨迹。每一轮只改少数变量,用 KV 池大小、上下文悬崖位置、生成速度三个指标衡量。

轮次关键配置KV 池上下文悬崖速度
0 初始batched-tokens 32768, util 0.9, MTP 开45844.7K(智能体全挂)
1 优化batched-tokens 8192, util 0.95, 去 MTP9072消失(62.6K ✅)
2 加回 MTP+MTP, +cudagraph852445~48K28.6 tok/s
3 再去 MTP去 MTP,保留 cudagraph9280153~196K27.3 tok/s
5 手工调参+前缀缓存,MTP 又开回来5356跳档 +5K 即挂17.5 tok/s
6 优化落地删 MTP、len→131072、cudagraph[1…16]8956126.9K(至上限无悬崖)20.9 tok/s

第 4 轮是 KV 量化尝试(910B 无原生 FP8 计算单元,直接报错回退),未落地验证。

3.1 第一杠杆:–max-num-batched-tokens

这是最有效的一个参数,也是最容易被忽略的。

KV 池
327684584
81929072(翻倍)

原理:vLLM 启动时会用这个配置跑一次 profiling 前向,激活峰值决定了留给 KV 缓存的显存余量。32768 让激活峰值吃掉大半显存,KV 池就只剩 4584。降到 8192 后,让出的显存全部转化为 KV 池,同时 chunked prefill 生效,长提示词分块准入,挂死悬崖直接消失

代价是长提示词 prefill 分更多块、略慢一点——但"连跑都跑不起来"和"慢一点",不用犹豫。

3.2 最大的反直觉:MTP 投机解码是负资产

我们一开始理所当然地认为投机解码能提速。实测数据打脸了。

第 2 轮 vs 第 3 轮只差一个 MTP,其他参数完全相同:

有 MTP无 MTP
KV 池85249280(+8.9%)
上下文悬崖45~48K153~196K(3.4 倍)
生成速度28.6 tok/s27.3 tok/s

去掉 MTP,上下文悬崖提升 3.4 倍,代价只是 4.7% 的速度。

第 5 轮我们又(被动)验证了一次:MTP 开启时,草稿接受率只有 12.2%——1911 个草稿 token 里只接受了 234 个。等于白烧算力,还吃掉 KV 池。

经验二:在昇腾 NPU 上,MTP 投机解码的收益远不如 GPU。智能体场景下上下文是硬约束,用 4.7% 速度换 3.4 倍上下文余量,这笔交易非常划算。

3.3 最隐蔽的陷阱:前缀缓存让测试结果失真

第 5 轮我们测出"47K token 提示词通过",一度以为容量不错。后来发现这是假的

原因:测试用的填充文本是重复段落,开启前缀缓存后被大量命中(实测命中率 72.5%),测的是"缓存加持下的极限",不是真实能力。

我们改进了测试方法——在每段文本前插入唯一递增序号[0000123]),让前缀无法复用:

模式实测上限
热缓存(重复文本)49.4K
冷启动(唯一文本)~13.4K

差 3.8 倍。 真实业务里每次系统提示词 + 用户输入都不同,应该看冷启动的数据。

经验三:测长上下文上限,必须用唯一文本。否则前缀缓存会让结果虚高数倍。

3.4 最危险的行为:声明值远超实际容量

第 5 轮我们发现一个"状态依赖"的挂死:同一尺寸,跳档测就挂死、紧邻升序测就通过

场景增量结果
23.4K → 31.3K+6.9K❌ 挂死
43.9K → 45.9K → 47.4K+2.0K / +1.4K

一次挂死实测持续了 2171 秒(36 分钟) 零输出,期间所有请求全部无响应

根因:--max-model-len 声明了 262144,但真实能力只有十几 K。超限请求不会快速报错,而是无限挂死并毒化整个队列

经验四:宁可把 max-model-len 调小,让超限请求快速报 400,也不要让它挂死拖垮整个服务。

3.5 上下文阈值:131072 是怎么验证出来的

最终配置里的 --max-model-len 131072 不是拍脑袋定的,而是实测数据给出的"可行性较高"取值:

实测点结果
第 3 轮:152.9K✅ 通过(实测能力上界)
第 3 轮:196K❌ 挂死
第 6 轮:126,893✅ 冷启动唯一文本通过
第 6 轮:131,072(声明上限)扫描至上限无悬崖
跳档测试 +15.6K / +28K / +38K均正常,无状态依赖挂死

取 131072 的三条依据:

  1. 贴着实测上界、留足余量:131072 < 实测通过的 152.9K,留了约 15% 余量,保证声明值永远不会越过真实能力边界;
  2. 全区间验证无悬崖:最终配置下从 20K 一路扫到 131072,冷启动唯一文本全部通过,跳档挂死也已消除——声明范围内任何一个尺寸都是被实测覆盖过的
  3. 超限行为可控:即便业务真送来超长请求,也是 0.8 秒快速返回 400;120K 大提示词 prefill 期间,2 token 小请求仍 1.59 秒返回,大上下文不牺牲在线请求

对比第 5 轮的教训(声明 262144、真实能力只有十几 K,一个超限请求锁死整个服务 36 分钟),声明值宁可保守。131072 正是"够用"与"被验证"之间的那个平衡点:往下损的是业务空间,往上添的是挂死风险。


四、最终最优启动参数(可直接用)

#!/bin/bash
# GLM-5.3-Flash @ Atlas 800 A2 (8×Ascend 910B3) 实测最优配置

export OMP_PROC_BIND=false
export OMP_NUM_THREADS=128
export PYTORCH_NPU_ALLOC_CONF=expandable_segments:True
export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libjemalloc.so.2:$LD_PRELOAD
export HCCL_BUFFSIZE=1024
export HCCL_OP_EXPANSION_MODE="AIV"
export VLLM_ASCEND_ENABLE_FLASHCOMM1=1
export TASK_QUEUE_ENABLE=1
export VLLM_RPC_TIMEOUT=3600000
export VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=3000
export HCCL_EXEC_TIMEOUT=3600
export HCCL_CONNECT_TIMEOUT=1200
export ASCEND_RT_VISIBLE_DEVICES=0,1,2,3,4,5,6,7

vllm serve /root/.cache/GLM-5.3-Flash-w8a8 \
  --host 0.0.0.0 \
  --port 8077 \
  --max-model-len 131072 \
  --tensor-parallel-size 8 \
  --enable-expert-parallel \
  --seed 1024 \
  --served-model-name glm5.3-flash \
  --safetensors-load-strategy prefetch \
  --max-num-seqs 16 \
  --max-num-batched-tokens 8192 \
  --trust-remote-code \
  --tool-call-parser glm47 \
  --reasoning-parser glm45 \
  --enable-auto-tool-choice \
  --quantization ascend \
  --limit-mm-per-prompt '{"image":1,"video":0}' \
  --gpu-memory-utilization 0.95 \
  --enable-prefix-caching \
  --async-scheduling \
  --compilation-config '{"cudagraph_mode":"FULL_DECODE_ONLY","cudagraph_capture_sizes":[1,2,4,8,12,16]}' \
  --additional-config '{
    "ascend_compilation_config": {
      "enable_npugraph_ex": true,
      "enable_static_kernel": false
    },
    "enable_cpu_binding": true,
    "enable_flashcomm1": true,
    "multistream_overlap_shared_expert": true
  }' \
  --api-server-count 1

关键参数说明

参数为什么
--max-num-batched-tokens8192决定 KV 池大小的第一杠杆(32768 时 KV 池仅 4584)
--speculative-config不加MTP 接受率仅 12%,换 3.4 倍上下文更划算
--max-model-len131072略低于实测 152.9K,超限快速报 400 而非挂死(验证过程见 3.5)
cudagraph_capture_sizes[1,2,4,8,12,16]与 max-num-seqs=16 对齐,大于 16 的尺寸纯浪费显存
--enable-prefix-caching命中率 72.5%,TTFB 从 1.3s 降到 0.13~0.4s
--gpu-memory-utilization0.95多轮稳定,OOM 再退回 0.92
--max-num-seqs16智能体场景并发低,减小调度压力

实测效果

指标优化前优化后
KV 池53568956(+67%)
冷启动上下文~13.4K126,893 ✅
跳档挂死+5K 即挂消除
超限行为无限挂死锁死服务0.8s 返回 400
队头阻塞小请求 90s 零响应消除(120K prefill 中,小请求 1.59s 返回)
生成速度17.5 tok/s20.9 tok/s
智能体端到端挂死6.43s,正确返回 tool_calls

五、部署教程

5.1 下载模型

在宿主机准备目录,从 ModelScope 拉取昇腾 w8a8 量化版:

cd /mnt/sdb
mkdir GLM-5.3-Flash-w8a8
modelscope download --model Eco-Tech/GLM-5.3-Flash-w8a8 --local_dir ./

5.2 启动容器

用昇腾官方 vLLM-Ascend 镜像(已针对 GLM-5.3-Flash 适配),注意挂载全部 8 张卡:

docker run -d \
--name vllm-ascend-glm5.3 \
--net=host --shm-size=500g --privileged \
--device /dev/davinci0 --device /dev/davinci1 \
--device /dev/davinci2 --device /dev/davinci3 \
--device /dev/davinci4 --device /dev/davinci5 \
--device /dev/davinci6 --device /dev/davinci7 \
--device /dev/davinci_manager --device /dev/devmm_svm --device /dev/hisi_hdc \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
-v /usr/local/Ascend/firmware:/usr/local/Ascend/firmware \
-v /usr/local/sbin/npu-smi:/usr/local/sbin/npu-smi \
-v /usr/local/sbin:/usr/local/sbin \
-v /etc/hccn.conf:/etc/hccn.conf:ro \
-v /mnt/sdb:/root/.cache \
-it quay.io/ascend/vllm-ascend:glm-5.3-flash bash

docker exec -it vllm-ascend-glm5.3 /bin/bash

模型目录 /mnt/sdb 挂载到容器内 /root/.cache,即启动参数里的
/root/.cache/GLM-5.3-Flash-w8a8

5.3 环境检查

npu-smi info          # 应看到 8 张卡,Health = OK

驱动、固件、CANN、PyTorch-Ascend 的版本必须严格配套,请以昇腾官方《版本配套表》为准——
这是最容易踩坑的一步,版本不匹配会引发各种难以定位的算子异常。

5.4 启动服务

chmod +x vllm-serve-glm53-final.sh
bash vllm-serve-glm53-final.sh

首次启动含权重加载 + 图编译,耗时较长,建议把 VLLM_RPC_TIMEOUT 设大(脚本中已设 3600000ms)。

5.5 四步验证

第 1 步:确认 KV 池

curl -s http://<host>:8077/metrics | grep cache_config_info

关注 num_gpu_blocksblock_size,两者相乘即 KV 池 token 数。如果远小于你的 max-model-len,就一定会有挂死

第 2 步:确认调度健康

curl -s http://<host>:8077/metrics | grep -E "num_requests_running|num_requests_waiting|kv_cache_usage_perc"

正常空闲状态应全为 0。

第 3 步:工具调用链路

curl -s -X POST http://<host>:8077/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"glm5.3-flash","messages":[{"role":"user","content":"北京今天天气怎么样?"}],
       "tools":[{"type":"function","function":{"name":"get_weather","description":"获取城市天气",
       "parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}],
       "tool_choice":"auto","stream":false,"max_tokens":256}'

应正确返回 tool_callsfinish_reason=tool_calls

第 4 步:长上下文冷启动验证

这一步最容易被跳过,也最容易埋雷。务必用唯一文本(不要用重复段落,否则前缀缓存会让结果虚高 3.8 倍)。


本文所有数据均来自 Atlas 800 A2 实机实测,配置与测试方法可复现。欢迎在评论区交流你在昇腾部署上遇到的问题。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐