跳到主要内容
AI教程

使用 TensorFold 在两台 DGX Spark 上运行 GLM-5.3-Flash

了解如何在两台 NVIDIA DGX Spark 上安装、配置、启动、测试和调优 GLM-5.3-Flash EXL3。本教程涵盖 RoCE 连接、自动化设置、兼容 OpenAI 的请求、图像和视频输入、推理控制、共享 KV 缓存行为、监控以及高级配置。

使用 TensorFold 在两台 DGX Spark 上运行 GLM-5.3-Flash

此项目的功能

此项目使用 TensorFold v0.5.0,在两台 NVIDIA DGX Spark 系统上提供 GLM-5.3-Flash 服务。每台 Spark 运行一个分布式 rank,同时第一台 Spark 还托管兼容 OpenAI 的 HTTP API。

默认配置支持四个并发请求、每个请求最大 1,048,576 个 token 的上下文窗口、图像和视频输入、工具调用、结构化输出、分词接口以及 Prometheus 指标。两台系统之间通过直连的 ConnectX-7 RoCE 链路通信。

该部署使用 Mia-AiLab GLM-5.3-Flash EXL3 检查点。路由专家以每个权重约 4 bit 的精度存储,而检查点中的其他权重使用 BF16。完整检查点约为 176 GB。

主要功能

  • 兼容 OpenAI 的 API:使用聊天补全、补全、响应和模型列表接口。
  • 长上下文:在默认 FP8 KV 配置下,单个请求最多支持 1,048,576 个提示词与回复 token。
  • 并发推理:使用默认的 DFlash2 草稿模型同时解码最多四个请求。
  • 共享缓存池:并发对话共享一个 FP8 KV 池,其中包含约 210 万至 270 万个 token,具体取决于可用内存。
  • 多模态请求:提交 JPEG、PNG、WebP、MP4、WebM、MOV、MKV 以及其他可由 FFmpeg 解码的视频输入。
  • 推理控制:选择低、高或最高推理力度,也可以针对某个请求禁用思考。
  • 智能体功能:通过 xgrammar 使用工具调用和受 schema 约束的结构化输出。
  • 运维接口:检查健康状态、对输入进行分词或反分词,并收集 Prometheus 指标。
  • 自动化部署:一条命令即可准备两台机器、启动两个 rank、执行冒烟测试并报告 API 地址。

硬件和软件要求

你需要两台 DGX Spark,或两台各配备 128 GB 统一内存的 GB10 系统。服务器启动时,每个 GPU 需要约 110 GiB 可用空间,因此请先停止其他占用大量 GPU 资源的任务。

网络要求

使用 QSFP 线缆连接 ConnectX-7 端口,并为每个端点分配同一私有子网中的 IPv4 地址。例如,head 可以使用 192.0.2.1/24,worker 可以使用 192.0.2.2/24。这些地址必须能够通过 ping 访问,并且必须提供 RoCE v2 GID。

如果两个 CX7 端口都已连接并配置地址,项目可以使用两条 rail。根据 README 中的测量结果,使用两条 rail 时,提示词块的 all-gather 速度约快 1.8 倍。

SSH 和系统要求

  • head Spark 能够通过基于密钥的 SSH 访问 worker。
  • 两台系统都安装 Docker 和 NVIDIA 容器运行时。
  • 两台系统上的账户都属于 docker 组。
  • 两台系统都安装 rsync。
  • 每台 Spark 约有 205 GB 磁盘空间。
  • 可选:在 head 上安装 Hugging Face hf CLI 并配置 Hugging Face token。

如有必要,请创建 SSH 密钥并将其复制到 worker,然后验证非交互式访问:

ssh-keygen -t ed25519
ssh-copy-id user@192.0.2.2
ssh -o BatchMode=yes user@192.0.2.2 true

DFlash2 草稿模型采用 CC BY-NC-ND 4.0 许可,仅限非商业用途。如果需要避免下载或使用 DFlash2,请在首次设置前选择检查点的 MTP 草稿模型。

安装和配置项目

1. 在 head 上克隆仓库

git clone https://github.com/MiaAI-Lab/GLM-5.3-Flash-EXL3-2x-DGX-Sparks-TensorFold.git
cd GLM-5.3-Flash-EXL3-2x-DGX-Sparks-TensorFold
cp scripts/local.sh.example scripts/local.sh

worker 不需要单独检出仓库。设置脚本会从 head 传输所需的容器镜像和模型文件。

2. 设置 worker 地址

编辑 scripts/local.sh,将 WORKER 设置为 worker 的 SSH 目标:

WORKER=user@192.0.2.2

分布式 rank 会通过访问 worker 所使用的路由进行通信。如果 WORKER 指向另一个网络中的地址,请将 FABRIC_PEER 设置为 worker 的私有 CX7 地址:

WORKER=user@worker-hostname
FABRIC_PEER=192.0.2.2

3. 必要时选择草稿模型

默认值为 DRAFTER=dflash2,支持最多四个并发请求,并带来约 5% 至 10% 的实测解码提升。如需改用检查点的 MTP head,请在首次启动前进行配置:

DRAFTER=mtp

MTP 无需使用 DFlash2 的许可和下载内容,但默认一次只能处理一个请求。

4. 启动两个 rank

./start.sh

首次运行时,脚本会检查两台系统,获取或构建约 25 GB 的容器镜像,在 head 上下载检查点和草稿模型,将它们复制到 worker,并将 CUDA 内核编译到 ~/.cache/tensorfold-glm53。

后续启动通常需要两到六分钟,将约 80 GiB 的权重加载到每台 Spark。启动器会跟踪 rank 0 的日志,报告加载进度,运行分布式冒烟测试,并在 8888 端口输出实时端点。

如需准备两台系统但不启动 API,请运行:

scripts/prepare.sh

如需强制重新构建镜像,请使用:

scripts/prepare.sh --rebuild

发起首个 API 请求

默认模型标识符为 GLM-5.3-Flash-EXL3,基础 URL 为 http://<head-address>:8888/v1。

列出可用模型

curl -s http://<head-address>:8888/v1/models

发送聊天补全请求

curl -s http://<head-address>:8888/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "GLM-5.3-Flash-EXL3",
    "messages": [
      {"role": "user", "content": "Write a Python fibonacci function."}
    ],
    "max_tokens": 2000
  }'

模型默认会在回答前进行思考。推理过程会返回在 reasoning_content 中,最终答案则出现在 content 中。请为这两个阶段分配足够的输出 token。如果 content 为空,模型可能是在推理阶段耗尽了输出额度。

控制推理和采样

将 reasoning_effort 设置为 low、high 或 max。使用 none 可请求模型直接回答而不进行思考:

curl -s http://<head-address>:8888/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "GLM-5.3-Flash-EXL3",
    "messages": [{"role": "user", "content": "Summarize TCP in two sentences."}],
    "reasoning_effort": "low",
    "temperature": 0,
    "max_tokens": 1000
  }'

API 还接受 temperature、top_p、top_k、min_p 和 seed。如果未明确指定 seed,采样器会根据提示词生成其密钥,因此相同的请求会产生相同的回复。

发送图像和视频

默认情况下,GLM 视觉塔运行在 rank 0 上。图像和视频以 OpenAI 风格的内容部分表示,并放在用户消息中。

分析本地图像

IMG=$(base64 -w0 photo.jpg)
curl -s http://<head-address>:8888/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "GLM-5.3-Flash-EXL3",
    "messages": [{
      "role": "user",
      "content": [
        {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,'"$IMG"'"}},
        {"type": "text", "text": "What is in this picture?"}
      ]
    }],
    "max_tokens": 2000
  }'

默认情况下,服务器通过数据 URL 接受媒体。将 VISION_URLS=1 设置为启用对公共 HTTPS URL 的获取。

支持的媒体限制

  • 每个请求最多 50 张图像;每张图像限制为 10 MB,图像数据总量限制为 64 MB。
  • 每个请求最多 4 个视频;每个视频限制为 64 MB,视频数据总量限制为 96 MB。
  • 每个视频最多可包含一小时的素材。
  • 请求体最大为 96 MiB,为 Base64 编码的媒体留下约 70 MB 空间。
  • 每张图片最多使用 2,048 个 token,而单个请求中的所有图片共享默认的 16,384 token 预算。
  • 视频采样默认为每秒两帧,整个片段最多采样 128 帧。

对于视频,请将图像内容部分替换为包含数据 URL 的 video_url 部分,例如 data:video/mp4;base64,...。

如果只需要文本,请设置 VISION=0。这样可以避免加载视觉塔,并为 rank 0 的缓存使用额外释放约 1.8 GiB 内存。

运行和监控服务器

重启和停止

./start.sh restart
./stop.sh

更改持久化设置后必须重启。停止服务会释放两台机器上的 GPU 内存。

查看日志

docker logs -f glm53-flash-tf
ssh <worker> docker logs -f glm53-flash-tf

第一条命令跟踪 head 上的 rank 0。第二条命令跟踪 rank 1,尤其适合在 worker 意外退出时使用。

检查健康状态和指标

curl -s http://<head-address>:8888/health
curl -s http://<head-address>:8888/metrics

健康状态响应会报告繁忙状态、活跃流数量和共享池容量,包括 pool_tokens 和 pool_free_tokens。指标端点会提供 Prometheus 请求、延迟、首 token 时间和健康状态数据。

了解上下文和共享 KV 内存

默认配置使用 FP8 KV 缓存,并为每个请求提供最大 1,048,576 token 的窗口。四个请求共享一个容量为 2,684,928 token 的池,这是某次记录的启动数据;实际容量取决于可用内存。

单个请求可以使用完整窗口,但所有活跃及保留的会话都必须适配公共池。例如,两个会话可以分别接近一百万 token,而较短的并发会话则可以共享剩余容量。暂时无法放入池中的请求会等待其他请求完成。

使用 CONTEXT=0 可计算能够容纳的最大窗口,但请注意,这样不会为保留其他会话的提示状态留下内存。切换为精确的 BF16 KV 存储会显著缩小可用窗口。

统一内存耗尽可能导致 Spark 卡死,而不是产生明确的分配失败。除非你了解其中的内存权衡,否则请保留默认预留值;当其他工作负载共享这些机器时,请提高 MEMORY_RESERVE_GIB。

高级配置

可以通过环境变量、scripts/local.sh 或 start.sh 旁边的 .env 文件提供设置。优先级顺序为环境变量、scripts/local.sh、.env,最后是仓库默认值。

为单次运行更改并发数

PARALLEL=2 ./start.sh restart

启用 DFlash2 后,PARALLEL 可设置为 1 到 4。MTP 默认一次处理一个请求。

更改上下文窗口

CONTEXT=524288 ./start.sh restart

当不需要完整的百万 token 窗口时,较小的上下文可以减轻缓存压力。

增加默认输出上限

./start.sh restart --max-tokens 16384

其他命令行参数会在已配置的默认参数之后传递给 tensorfold serve,因此会覆盖这些默认值。运行 ./start.sh --help 可查看支持的参数。

强制执行或跳过准备过程

PREPARE=1 ./start.sh restart
PREPARE=0 ./start.sh restart

第一个命令会强制执行设置检查和准备过程。第二个命令会跳过该过程。

有效利用提示复用

共享前缀复用默认通过 SHARED_PREFIX=1 启用。重复的系统提示和相同的保留提示可以从缓存状态恢复,而无需再次预取。在文档记录的测量中,重复的 64K token 提示耗时从 34 秒降至 0.07 秒以内,而复用 7.9K token 的系统提示则将启动时间从 4.24 秒缩短至 0.13 秒。

选择缓存和权重精度

  • KV=fp8 为默认值,并支持最大的上下文窗口。
  • KV=bf16 使用精确的缓存存储,但会大幅缩短可用窗口。
  • DENSE=q4 是稠密检查点组件的默认设置。
  • DENSE=fp8 和 DENSE=bf16 是可用的替代选项,对内存的影响不同。

其他 API 功能

除聊天补全外,服务器还提供 /v1/completions、/v1/responses、/v1/models、/tokenize、/detokenize、/health 和 /metrics。分词路由也可通过 /v1/ 使用。服务器不提供兼容 Anthropic 的 /v1/messages 端点。

工具调用支持 tools 和流式 tool_calls,参数类型依据所提供的架构定义。结构化生成支持 json_object、json_schema、引导式 JSON、正则表达式、选项、语法以及其他结构化输出字段;推理完成后,这些字段通过 xgrammar 强制执行。

如果提示与 max_tokens 的总长度超过配置的窗口,API 会返回 HTTP 400,并使用 OpenAI 风格的错误代码 context_length_exceeded。

常见启动问题排查

  • 内存不足:如果启动时报告可用 GiB 数量有限,请检查 docker ps,并停止两台 Spark 上的其他 GPU 工作负载。
  • 没有 RoCE 设备或路由:使用 ip -4 addr 确认 CX7 接口具有 IPv4 地址。如果 SSH 使用其他网络,请将 FABRIC_PEER 配置为工作节点的 CX7 地址。
  • 工作节点缓存不可写:如果之前的容器将其所有者设置为 root,请修正工作节点 Hugging Face 缓存的所有权。
  • 一个 rank 退出:检查两台机器上的日志。任一端失败时,启动器通常会停止剩余的 rank。
  • 设置已更改:修改补丁或更改 TensorFold、基础镜像或相关构建输入后,运行 scripts/prepare.sh --rebuild。

性能预期

在 README 默认的两台 Spark 测量中,文本解码性能从单个请求的每秒 60.4 个 token,到四个并发请求的每秒总计 108.8 个 token。结构化生成性能从每秒总计 114.7 个 token 到 227.9 个 token 不等。对于 8,219 token 的提示词,预填充速度约为每秒 1,952 个 token;对于 981,841 token 的大海捞针提示词,速度为每秒 1,015 个 token,且成功找到了目标内容。

这些数据是在四个流、FP8 KV 缓存、四位稠密权重、启用 DFlash2 和复制草稿、启用视觉功能以及将 GPU 时钟限制为 2,200 MHz 的条件下测得的。应将其视为该配置下的参考结果,而不是所有环境的保证值。

结论

此部署将复杂的双节点 TensorFold 配置封装在实用的启动流程和兼容 OpenAI 的 API 后面。CX7 链路、SSH 访问、Docker 运行时、存储和工作节点地址准备就绪后,./start.sh 会处理准备、分布式启动、验证和服务运行。之后,你可以使用标准 API 客户端,并针对工作负载调整并发数、上下文、推理、多模态限制和内存行为。