使用 llama.cpp 运行 Qwen3.8-27B

太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处

我在 RTX 4090 24GB 上用 llama.cpp 跑了一遍 Qwen3.8-27B。模型使用 UD-Q4_K_M 量化,启动时同时加载 mmproj-F16.gguf。服务大约 11 秒完成初始化,两次文本请求都正常返回,生成速度在 49 tokens/s 左右。

这只是一次初步运行。我还没有测试长上下文、工具调用和持续运行的稳定性,所以这里只记录已经跑出来的结果。

下载模型

模型来自 ModelScope 上的 Qwen3.8-27B-GGUF。先按固定 ModelScope 缓存根目录设置 MODELSCOPE_CACHE,再下载 UD-Q4_K_M 权重和 F16 投影文件:

modelscope download unsloth/Qwen3.8-27B-GGUF \
  --include "*Q4_K_M*" "*mmproj-F16*"

下载完成后得到两个文件:

Qwen3.8-27B-UD-Q4_K_M.gguf  16464440224 bytes
mmproj-F16.gguf                927607488 bytes

新版 ModelScope 的缓存目录是:

$MODELSCOPE_CACHE/models/unsloth--Qwen3.8-27B-GGUF/snapshots/master/

启动前可以核对文件名和大小:

find "$MODELSCOPE_CACHE/models/unsloth--Qwen3.8-27B-GGUF/snapshots/master" \
  -maxdepth 1 -type f -printf '%f %s bytes\n'

主模型的实际文件名带有 UD,后面的启动命令也要使用完整名称 Qwen3.8-27B-UD-Q4_K_M.gguf

启动 llama-server

./build/bin/llama-server 是从 llama.cpp 源码编译得到的。源码获取、CUDA 构建参数和版本检查见“编译 llama.cpp”一节。编译完成后,进入 llama.cpp 仓库目录运行:

export LLAMA_API_KEY='在这里填入你的实际 key'

./build/bin/llama-server --metrics --alias Qwen3.8-27B \
  --model "$MODELSCOPE_CACHE/models/unsloth--Qwen3.8-27B-GGUF/snapshots/master/Qwen3.8-27B-UD-Q4_K_M.gguf" \
  --mmproj "$MODELSCOPE_CACHE/models/unsloth--Qwen3.8-27B-GGUF/snapshots/master/mmproj-F16.gguf" \
  --reasoning-preserve --jinja \
  --host 0.0.0.0 --port 30104 --ctx-size 262144 -ngl 100 --fit off \
  --cache-type-k q4_0 --cache-type-v q4_0 --parallel 1 \
  --batch-size 4096 --ubatch-size 1024 --flash-attn on \
  --load-mode mlock --threads $(nproc)

本地文件启动时,主模型和投影文件需要分别通过 --model--mmproj 指定。llama.cpp 的多模态说明介绍了两者的分工:主模型负责文本理解与生成,mmproj-F16.gguf 负责处理图片输入。这里保留 mmproj,但本次实测只发送了文本请求。

其余参数沿用之前运行 Qwen3.6-27B 的配置--jinja 用于聊天模板和工具调用解析,--cache-type-k q4_0--cache-type-v q4_0 用来压缩 KV Cache。--ctx-size 262144 能正常启动,不过我没有实际跑满 262K 上下文。

验证图片输入

加载 mmproj-F16.gguf 只说明图片投影器已经随服务启动,图片输入是否正常还要实际发一次请求。下面把本地图片编码成 Data URL,再交给 OpenAI 兼容的 /v1/chat/completions 接口:

IMAGE_DATA=$(base64 -w 0 ./test.png)

curl --noproxy '*' http://127.0.0.1:30104/v1/chat/completions \
  -H "Authorization: Bearer $LLAMA_API_KEY" \
  -H 'Content-Type: application/json' \
  --data-binary @- <<JSON
{
  "model": "Qwen3.8-27B",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "image_url",
          "image_url": {
            "url": "data:image/png;base64,$IMAGE_DATA"
          }
        },
        {
          "type": "text",
          "text": "请描述这张图片,并抄出其中清晰可见的文字。"
        }
      ]
    }
  ]
}
JSON

测试图片最好内容简单,并带有几处容易核对的文字。除了接口返回,还要看 llama-server 日志里是否出现图片编码过程。如果请求被当成纯文本,或者接口直接提示不支持图片,先检查 --mmproj 路径、投影文件是否与主模型配套,再确认当前 llama.cpp 构建是否包含多模态支持。这条图片请求还没有实际跑过,下面的性能数据来自两次文本请求。

运行结果

llama-server 大约 11 秒完成加载:

init: llama threadpool init, n_threads = 32
load_model: initializing, n_slots = 1, n_ctx_slot = 262144, kv_unified = 'false'
llama_server: model loaded
llama_server: listening on http://0.0.0.0:30104

第一次请求处理了 371 个输入 token,生成 85 个 token:

prompt eval time = 322.84 ms / 371 tokens (1149.19 tokens per second)
eval time = 1704.65 ms / 85 tokens (49.28 tokens per second)
total time = 2027.49 ms / 456 tokens
truncated = 0

第二次请求与已有上下文很接近,前缀相似度是 0.958。llama-server 复用了同一 slot 的缓存,只重新计算了 20 个输入 token。生成 804 个 token 时,速度是 49.38 tokens/s:

selected slot by LCP similarity, f_sim_best = 0.958
prompt eval time = 277.24 ms / 20 tokens (72.14 tokens per second)
eval time = 16262.84 ms / 804 tokens (49.38 tokens per second)
total time = 16540.09 ms / 824 tokens
truncated = 0

从这两次请求看,模型已经能够正常加载和生成文本,前缀缓存也在工作。至于回复质量、长上下文表现和 Agent 工具调用,还需要另外测试。

unused tensor 警告

加载模型时出现了几条 blk.64 警告:

model has unused tensor blk.64.attn_q.weight -- ignoring
model has unused tensor blk.64.ffn_gate.weight -- ignoring
model has unused tensor blk.64.nextn.eh_proj.weight -- ignoring
model has unused tensor blk.64.nextn.shared_head_norm.weight -- ignoring

其中既有 attention、FFN 权重,也有 nextn 权重。这与 llama.cpp 记录的 Qwen3.x MTP 块 unused tensor 问题一致。

这些警告没有影响本次普通文本生成,但也说明额外的 MTP/NextN 预测块没有参与这次推理。我没有测试 draft-mtp 推测解码,暂时保留这些警告,不对这部分能力下结论。