本文档记录本机硬件环境、Qwen3.8-27B 模型的安装与推理服务部署,以及在 DeepSeek Harness(dsh)中的接入配置。支持文本 + 图片(多模态)。
1. 本机硬件环境
| 组件 | 配置 |
|---|---|
| CPU | Intel Xeon W-2150B @ 3.00GHz(10 核 20 线程) |
| 内存 | 251 GiB |
| GPU | 2 × NVIDIA GeForce RTX 2080 Ti(每卡 22 GiB,共 44 GiB) |
| 驱动 / CUDA | NVIDIA Driver 595.84 / CUDA 13.2 |
| 操作系统 | Ubuntu 24.04.4 LTS(x86_64) |
注意:RTX 2080 Ti 是 Turing 架构(SM 75),llama.cpp 编译时需确认 CUDA backend 已启用(见下文编译说明)。
2. 模型文件
Qwen3.8-27B 是支持多模态(图文)的 27B 参数模型,本机使用 GGUF 量化格式,Q8_0 量化。
| 文件 | 大小 | 用途 |
|---|---|---|
Qwen3.8-27B-Q8_0.gguf |
27G | 主模型权重(Q8_0) |
mmproj-Qwen3.8-27B-BF16.gguf |
889M | 视觉投影器(vision encoder,BF16 未量化) |
mtp-Qwen3.8-27B-Q8_0.gguf |
3.0G | 多 token 预测(MTP)权重 |
Qwen3.8-27B-DFlash2-Q4_K_M.gguf |
1.1G | DFlash2 投机解码 drafter(draft 小模型,来源 incoai/Qwen3.8-27B-DFlash2-GGUF) |
3. 编译 llama.cpp
推理服务基于 llama.cpp。
bash
cd ~/llama.cpp-upstream
cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=75
cmake --build build -j --config Release
# 产物:build/bin/llama-server
关键点:
GGML_CUDA=ON必须开启,否则模型掉回 CPU(日志会出现failed to initialize CUDA: no CUDA-capable device is detected)。- 2080 Ti 的 CUDA 架构码是 75 ,
CMAKE_CUDA_ARCHITECTURES=75能让编译针对本卡优化。不指定也能编译,但会用默认架构列表。
当前实际使用的二进制不是这个 :因为 DFlash2 投机解码需要未合并的 PR #27342,服务现在指向
~/llama.cpp-dflash2/build/bin/llama-server(见第 8 章)。上面只是基础编译方法;如需 DFlash2 请按第 8.2 节构建。
4. 部署推理服务(systemd 用户服务)
用 systemd 用户服务托管 llama-server,开机/登录自启、崩溃自动拉起。
4.1 服务文件
~/.config/systemd/user/qwen38-27b-q8.service:
ini
[Unit]
Description=llama.cpp server - Qwen3.8-27B Q8_0 (dual-GPU hybrid attention)
After=network.target
[Service]
Type=simple
ExecStart=/home/XXXXX/llama.cpp-dflash2/build/bin/llama-server \
--model /home/XXXXX/models/Qwen3.8-27B-GGUF/Qwen3.8-27B-Q8_0.gguf \
--mmproj /home/XXXXX/models/Qwen3.8-27B-GGUF/mmproj-Qwen3.8-27B-BF16.gguf \
--alias qwen3.8-27b-q8 \
--host 127.0.0.1 \
--port 8097 \
--ctx-size 131072 \
--n-gpu-layers 99 \
--split-mode layer \
--jinja \
--flash-attn on \
--cache-type-k q8_0 \
--cache-type-v q8_0 \
--spec-type draft-dflash \
--spec-draft-model /home/XXXXX/models/Qwen3.8-27B-GGUF/Qwen3.8-27B-DFlash2-Q4_K_M.gguf \
--spec-draft-ngl 99 \
--spec-draft-n-max 5 \
--no-webui
Restart=on-failure
RestartSec=10
[Install]
WantedBy=default.target
4.2 参数说明
| 参数 | 含义 |
|---|---|
--model |
主模型权重路径 |
--mmproj |
视觉投影器路径,加载它才支持图片输入 |
--alias |
模型别名,/v1/models 里返回的名字 |
--ctx-size 131072 |
上下文窗口 128K |
--n-gpu-layers 99 |
把 99 层全部卸载到 GPU |
--split-mode layer |
按层拆分到双卡(layer 模式) |
--flash-attn on |
开启 Flash Attention |
--cache-type-k/v q8_0 |
KV cache 量化,节省显存以容纳 128K 上下文 |
--spec-type draft-dflash |
开启 DFlash2 投机解码(draft 小模型先猜,大模型后验) |
--spec-draft-model |
DFlash2 drafter 权重路径 |
--spec-draft-ngl 99 |
drafter 全部卸载到 GPU |
--spec-draft-n-max 5 |
drafter 每次猜测的 token 上限(编程场景最优值,见第 9 章) |
--no-webui |
关闭内置 Web UI,仅开 API |
4.3 启用并启动
bash
systemctl --user daemon-reload
systemctl --user enable --now qwen38-27b-q8.service
systemctl --user status qwen38-27b-q8.service
4.4 验证
bash
# 模型列表
curl -s http://127.0.0.1:8097/v1/models
# 文本推理
curl -s http://127.0.0.1:8097/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model":"qwen3.8-27b-q8","messages":[{"role":"user","content":"用一句话解释递归"}]}'
# 图片推理(base64 图片)
curl -s http://127.0.0.1:8097/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model":"qwen3.8-27b-q8",
"messages":[{"role":"user","content":[
{"type":"text","text":"图里有哪些颜色?"},
{"type":"image_url","image_url":{"url":"data:image/png;base64,<BASE64>"}}
]}]
}'
5. 在 DeepSeek Harness 中配置
DeepSeek Harness(dsh)通过 llm-pi-ai 插件接入任意 OpenAI 兼容端点。配置写在 ~/.dsh/settings.yaml。
5.1 settings.yaml 配置片段
yaml
agent-default-model:
provider: qwen38-local
model: qwen3.8-27b-q8
llm-pi-ai:
providers:
qwen38-local:
displayName: Qwen3.8-27B Local (llama.cpp Q8 dual-GPU)
api: openai-completions
baseURL: http://127.0.0.1:8097/v1
headers:
Authorization: Bearer local-noauth # llama-server 无鉴权,占位值即可
models:
- id: qwen3.8-27b-q8
name: Qwen3.8-27B Q8_K (128K ctx)
contextWindow: 131072
maxTokens: 8192
input: [ text, image ] # 关键:声明支持图片
defaultInput:
- text
- image
5.2 关键点:图片支持怎么开启
Harness 的 llm-pi-ai 适配器在发请求前会检查 model.input 是否包含 image,不含就直接抛 UNSUPPORTED_CONTENT 拒绝图片。因此:
- 模型条目必须写
input: [ text, image ]------这是让一个手写声明的视觉模型可用的唯一方式。 defaultInput: [ text, image ]是 provider 级兜底,供未在 catalog 里的模型继承。- 两者都写上最稳妥(模型级
input优先于defaultInput)。
对比纯文本模型(如 Qwen2.5-Coder-32B)只需 input: [ text ] 或不写(默认 [text])。
5.3 重启生效
settings.yaml 修改后需重启 dsh:
bash
# 停掉当前 dsh web
pkill -f "dsh web"
# 重新启动
cd ~ && nohup dsh web > /tmp/dsh-web.log 2>&1 &
# 或前台运行
dsh web
Web UI 地址:http://127.0.0.1:3080
6. 显存规划与多模型共存
两块 2080 Ti 合计 44 GiB。Qwen3.8-27B Q8_0 本体约 26 GiB,128K 上下文的 KV cache(q8_0 量化后)仍需额外数 GiB,合计约 34 GiB。
因此同时常驻多个大模型会显存不足,导致 offload 失败、掉回 CPU(表现为 CPU 占用高、推理超时)。本机最终方案:
- 常驻 :仅 Qwen3.8-27B(
qwen38-27b-q8.service,enabled)。 - 停用 :GLM-4.6V-Flash(
llama-server.service,8099)与 Qwen2.5-Coder-32B(qwen32b.service,8098),已disable,需要时再手动systemctl --user start。
排查口诀:nvidia-smi --query-compute-apps=pid,used_memory --format=csv 看进程是否真的上了 GPU;若不在列表,查服务日志里是否出现 no usable GPU found。
7. 已知问题与备注
- 图片识别偏慢 :视觉投影器是 BF16 未量化,且 128K 上下文 KV cache 占用较多显存,图片请求整体比纯文本慢(实测约 9~10 秒返回)。若高频做视觉任务,可考虑降低
--ctx-size释放显存,或接受现状。 - 并发幽灵请求 :曾观测到外部客户端向 8097 发送超长 prompt(77K+ token)拖垮响应。若推理突然变慢,用
ss -tpn定位连接来源。 - Harness 侧另有
dsh-vision-tools插件 (日志中注册了vision_understand工具、/api/vision-paste路由)。这与「模型原生支持图片」是两套机制:本方案让 qwen3.8 模型自身吃图片;dsh-vision-tools是另一条视觉增强旁路,二者可独立存在。
8. DFlash2 投机解码加速
本机已为 Qwen3.8-27B 启用 DFlash2 投机解码(speculative decoding),编程场景实测约 2.8× 加速。
8.1 原理
投机解码 = 「小模型先猜,大模型后验」:
- drafter (DFlash2,仅 1.1GB 的轻量 draft 模型)跑得快,一次并行预测 N 个候选 token;
- 大模型 (27B Q8)一次 forward 验证这 N 个 token,保留连续的、正确的,丢弃第一个猜错之后的所有;
- 猜对 k 个,就相当于大模型只跑 1 次却产出 k 个 token → 加速。
关键 :投机解码不改变输出内容 ------最终 token 都由大模型验证过,与无投机时逐字一致,只是更快。且加速发生在 llama.cpp 服务端,对任何 OpenAI 兼容客户端(dsh、curl 等)完全透明,客户端无需任何配置。
8.2 为什么用独立构建
DFlash2 是 llama.cpp PR #27342 引入的(含 local-convolution + candidate-selector 两个新模块),尚未合并进上游 master 。master 自带的 draft-dflash 是 DFlash v1,读不了 81-tensor 的 DFlash2 drafter(会报 wrong number of tensors; expected 81, got 58)。
因此本机在独立 worktree 里构建:
bash
git worktree add ~/llama.cpp-dflash2 pr-27342 # 基于 PR #27342
cd ~/llama.cpp-dflash2
cmake -B build -DGGML_CUDA=ON -DGGML_CUDA_FA=ON -DGGML_NATIVE=ON -DGGML_OPENMP=ON
cmake --build build --target llama-server -j
- 产物:
~/llama.cpp-dflash2/build/bin/llama-server(服务已指向它) - 原
~/llama.cpp-upstream(master)保持不动,上游合并后可切回重编译。
8.3 n_max 调优(实测数据)
--spec-draft-n-max(drafter 每次猜测的 token 上限)是核心性能旋钮,并非越大越好 :太小加速有限,太大则接受率下降、且 drafter 的 CUDA graph 显存暴涨(本卡 n_max=15 直接 failed to allocate compute buffers 溢出)。
| 任务 | 可预测性 | 最优 n_max | 实测解码速度 | vs 无投机 |
|---|---|---|---|---|
| 编程 / 结构化 | 高(代码语法固定,接受率 66--79%) | 5 | 43 tok/s | ≈2.8× |
| 创意写作 / 散文 | 低(发散,接受率 ~33%) | 3 | 22.7 tok/s | ≈1.5× |
无投机基线约 15.3 tok/s。本机当前 n_max=5(用户主要用编程)。
若高频做创意写作,可把
--spec-draft-n-max从 5 降到 3,systemctl --user restart qwen38-27b-q8.service生效。
8.4 验证投机解码确实在跑
请求 /v1/chat/completions,响应 timings 字段里有投机指标:
json
"timings": {
"predicted_per_second": 43.0,
"draft_n": 295, // drafter 共猜了多少 token
"draft_n_accepted": 196 // 大模型接受了多少
}
draft_n / draft_n_accepted 存在即说明投机解码已激活;两者之比为接受率。
9. 快速参考
| 项 | 值 |
|---|---|
| 推理服务 | llama-server,127.0.0.1:8097 |
| 服务二进制 | ~/llama.cpp-dflash2/build/bin/llama-server(DFlash2 分支) |
| 投机解码 | DFlash2(draft-dflash),drafter Qwen3.8-27B-DFlash2-Q4_K_M.gguf,n_max=5 |
| OpenAI 兼容端点 | http://127.0.0.1:8097/v1 |
| 模型别名 | qwen3.8-27b-q8 |
| systemd 服务 | qwen38-27b-q8.service(user 级) |
| Harness 配置 | ~/.dsh/settings.yaml → llm-pi-ai.providers.qwen38-local |
| Harness Web UI | http://127.0.0.1:3080 |
| dsh 版本 | 0.1.0-rc.7(Node v24.16.0) |