Qwen3.8-27B 在 Jetson Thor 上的 llama.cpp 部署手册(Q8_0 + MTP)

文档日期:2026-08-17|本文档为公开脱敏版 (原内网 IP 与用户名已替换为 <THOR_IP> / %USERPROFILE% 占位符)

设备:Jetson Thor(Blackwell T5000)|引擎:llama.cpp(GGUF)|量化:Q8_0(~29.0 GB)+ MTP 投机解码

状态:✅ 部署完成且接入 WorkBuddy(服务 http://<THOR_IP>:8080,模型 thor-qwen38-q8,实测对话通过)


一、实验目的

在 Jetson Thor(128GB 统一内存、ARM aarch64、sm_110a)上,用 llama.cpp + Unsloth Q8_0 GGUF + MTP 投机解码 部署 Qwen3.8-27B,验证:

  1. 最新 llama.cpp 能否识别 qwen35 新架构;
  2. Q8_0(~29GB)在 Thor 带宽墙下的真实推理速度;
  3. 思考档位(reasoning_effort)与多模态(mmproj)是否可用;
  4. 能否接入 WorkBuddy / Pi 作为 OpenAI 兼容端点。

二、实验环境

2.1 硬件(Thor)

SoC NVIDIA Jetson Thor(Blackwell T5000)
统一内存 128 GB LPDDR5X(物理可用 ~122 GB)
内存带宽 ~273 GB/s(本机瓶颈,非容量)
计算能力 sm_110a(Blackwell)
架构 aarch64(ARM)

2.2 软件栈(Thor)

系统 JetPack R39.2
CUDA 13.2.1(/usr/local/cuda/bin/nvcc
llama.cpp 源码 /opt/llama.cpp-src(若 .git 正常则 git pull;若丢失见阶段 1 恢复方案)
编译产物 /opt/llama.cpp-src/build/bin/llama-server
模型目录 /opt/models/Qwen3.8-27B/

2.3 模型规格(Qwen3.8-27B)

  • 类型:Dense 27.78B,Apache 2.0,原生视觉(文本/图像/视频→文本)
  • 架构:Qwen3_5ForConditionalGeneration,64 层 = 48 层 Gated DeltaNet 线性注意力 + 16 层 Gated Attention
  • 原生上下文 262K(YaRN 可扩 1M),内置 MTP 草稿头(投机解码用)
  • 默认开启思考模式(Thinking),effort 可由客户端经 reasoning_effort 控制(none/low/medium/high/xhigh);本实验 WorkBuddy 默认设为 low

2.4 量化选型说明(为什么是 Q8_0)

量化 大小 说明
UD-IQ2_XXS 9.0 GB 质量损失大,不推荐
IQ4_XS 15.7 GB 速度与质量甜点
Q4_K_M 17.1 GB 最常用甜点
Q8_0(本实验) 29.0 GB 高质量,Thor 内存富裕装得下
BF16 53.8 GB 最高质量,但最慢

⚠️ 速度与质量的权衡 :Thor 是带宽瓶颈,每 token 需搬运的权重越多越慢。Q8_0(~29G) 比 Q4_K_M(~17G) 多搬运约 1.7× 数据 → 预计 Q8_0 单流 ~9--12 tok/s,Q4_K_M ~15--20 tok/s(实测 Q8_0 达 17.2 tok/s,MTP 生效后超出预估,见实验结果表)。选 Q8_0 = 用速度换质量;若后续嫌慢,可无缝换 Q4_K_M(命令仅改文件名)。


三、实验步骤

阶段 1:更新并重新编译 llama.cpp(必须)

⚠️ 早于 b8001 的旧版 llama.cpp 不支持 qwen35 架构 ,加载会报 unknown architecture 错误。需更新至含 b8001+ 的提交。
🔧 git pullfatal: not a git repository :源码在但 .git 丢了(多半是拷贝/解包而非完整克隆)。二选一恢复:

方案 A(推荐,最干净)--- 重新克隆

bash 复制代码
cd /opt
mv llama.cpp-src llama.cpp-src-old      # 旧版留作备份,编译成功后再删
git clone --depth 1 https://github.com/ggerganov/llama.cpp.git llama.cpp-src
# 若 GitHub 直连慢,用镜像:
# git clone --depth 1 https://ghproxy.com/https://github.com/ggerganov/llama.cpp.git llama.cpp-src

方案 B(保留旧 build 目录,增量重编)--- 原地重建 git

bash 复制代码
cd /opt/llama.cpp-src
rm -rf .git
git init -q
git remote add origin https://github.com/ggerganov/llama.cpp.git
git fetch --depth 1 origin master
git checkout -B master FETCH_HEAD

恢复后继续下面步骤。注意:--depth 1 是浅克隆,仓库不含 git tag,后续 --version 看不到 build 号(见下方版本核对说明)。

若已按上方恢复方案重新克隆,当前代码已是 master 最新,下方 git pull 为 no-op(或直接进入编译)。

bash 复制代码
cd /opt/llama.cpp-src
git pull                                   # 拉取至含 b8001+ 的提交(支持 qwen35 + qwen3vl_merger)
export PATH=/usr/local/cuda/bin:$PATH
export CUDACXX=/usr/local/cuda/bin/nvcc
cmake -B build -DGGML_CUDA=ON -DCMAKE_CUDA_ARCHITECTURES=110a
cmake --build build -j$(nproc)
  • 预期:编译成功,build/bin/llama-server 存在。

  • 版本核对(重要)build/bin/llama-server --version 可能显示 0.1.0-dev (build 1, commit <hash>) 而非 b8001------这是浅克隆无 tag 的兜底串,不表示版本旧 。判断是否支持架构,请用源码确认(任一条有输出即通过):

    bash 复制代码
    grep -rn "qwen35" /opt/llama.cpp-src/src/llama-model.cpp
    grep -rn "qwen3vl_merger\|qwen35" /opt/llama.cpp-src/src/ | head
  • 参考实测:版本串显示 0.1.0-dev (build 1, commit 4197155)(浅克隆无 tag 不显示 build 号,架构支持以源码 grep qwen35 确认) 编译成功

阶段 2:获取 Q8_0 权重与视觉 projector

权重文件(HuggingFace unsloth/Qwen3.8-27B-GGUF):

  • 文本:Qwen3.8-27B-Q8_0.gguf29.0 GB
  • 视觉(可选,多模态才要):mmproj-F16.gguf~0.93 GB

传输策略(Thor 出口 ~5MB/s,本机侧下载更快):

  1. 本机拉取:pip install -U "huggingface_hub[cli]"

    bash 复制代码
    hf download unsloth/Qwen3.8-27B-GGUF Qwen3.8-27B-Q8_0.gguf mmproj-F16.gguf --local-dir /opt/models/Qwen3.8-27B

    ⚠️ --local-dir 必须用绝对路径 /opt/models/Qwen3.8-27B !若写成相对路径 ./Qwen3.8-27B,文件会下到当前目录 下(如 /opt/llama.cpp-src/Qwen3.8-27B/),启动时仍报 No such file------这是部署中常见的坑(下载 100% 但服务秒退)。若已下错位置,用 mv /opt/llama.cpp-src/Qwen3.8-27B /opt/models/ 挪正。

  2. 本机起 HTTP 服务器(端口 8801),Thor 用 aria2c 局域网直拉(实测 ~115 MB/s)。 不要用 Thor 直连 hf-mirror/Xet(限速 5MB/s,且 Xet 签名易过期 403)。

  • 传输方式(任选其一):① 本机 HTTP+aria2(~115 MB/s) ② Thor 直连 hf download(注意 --local-dir 相对路径坑见下)
  • 文件落盘:/opt/models/Qwen3.8-27B/ 大小:Qwen3.8-27B-Q8_0.gguf 28G + mmproj-F16.gguf 885M(均为完整文件)

⚠️ 启动前必做的前置检查(跳过后阶段 3 会秒退------这是部署中常见的坑):

bash 复制代码
ls -lh /opt/models/Qwen3.8-27B/

期望看到两个文件:Qwen3.8-27B-Q8_0.gguf(~29G)和 mmproj-F16.gguf(~0.93G)。若 lsNo such file or directory 或文件不全,说明阶段 2 未完成,先回去下载/传输,不要直接进阶段 3 ------否则 llama-server 启动即报 failed to open GGUF file ... (No such file or directory) 并退出,8080 不监听,冒烟测试全部无反应。

阶段 3:启动服务(MTP + 关键 flag)

下方命令不含行内注释(bash 中反斜杠续行后若跟空格/注释会破坏续行)。各参数含义见紧随其后的「关键 flag 说明」表。

bash 复制代码
/opt/llama.cpp-src/build/bin/llama-server \
  -m /opt/models/Qwen3.8-27B/Qwen3.8-27B-Q8_0.gguf \
  --mmproj /opt/models/Qwen3.8-27B/mmproj-F16.gguf \
  -ngl 99 --flash-attn on \
  --ctx-size 262144 \
  --cache-type-k q8_0 --cache-type-v q8_0 \
  --spec-type draft-mtp --spec-draft-n-max 3 \
  --parallel 1 \
  --jinja \
  --reasoning-format deepseek \
  -b 2048 -ub 512 \
  --host 0.0.0.0 --port 8080 \
  --temp 1.0 --top-p 0.95 --top-k 20 --min-p 0.0 \
  2>&1 | tee /tmp/qwen38.log
  • 若不需要视觉,删除 --mmproj 那一行即可(省 ~0.93GB)。
  • 如需把日志单独落盘,末尾已接 2>&1 | tee /tmp/qwen38.log
  • 启动耗时(加载+初始化):<8 s(启动后 8 s 即见 listening on http://0.0.0.0:8080;mmap 懒加载,权重不预载) 成功监听 8080
  • 日志确认架构识别:grep -i "qwen35\|architecture" /tmp/qwen38.logqwen35 ✅(new llama_model_qwen35,架构正常识别)

关键 flag 说明(本模型专属)

flag 作用与为何必加
-m ...Q8_0.gguf 主模型权重,Q8_0 量化 GGUF,~29GB。llama.cpp 只认 GGUF,不认 HF safetensors
--mmproj ...mmproj-F16.gguf 视觉投影器,多模态必需;纯文本 Coding Agent 可删除此行省 ~0.93GB
-ngl 99 GPU 卸载层数,99=尽可能全卸载。Thor 为统一内存,指用 Blackwell 算力
--flash-attn on 强制 FlashAttention,更快的前缀缓存、更低显存(默认 auto)
--ctx-size 262144 KV 缓存窗口(单位 token)。本模型原生 262K,且线性层不占 O(N) KV,长上下文几乎免费
--cache-type-k/q8_0 KV 缓存量化。必须用 q8_0;q4_0/q4_1 会把注意力静默退回 CPU,prefill 从 ~1000 tok/s 掉到 ~35
--spec-type draft-mtp --spec-draft-n-max 3 MTP 投机解码(~2× 提速),需 GGUF 含 MTP 头;若被剥离则报错,删掉即可
--parallel 1 llama-server 默认 n_parallel=4,48 个线性注意力层会按 4 槽分配循环状态,多占 ~0.44GiB
--jinja 启用官方 chat 模板。不用它则走默认模板,思考块(<think>)多轮嵌套导致历史截断、模型「失忆」
--reasoning-format deepseek 思考块提取格式。取值只有 deepseek / none (不是按模型厂商选):deepseek = 把思考轨迹放进响应的 message.reasoning_content 字段(DeepSeek API 约定,也是 OpenAI 兼容客户端/WorkBuddy 通用解析字段);none = 思考留在正文。需配合 --jinja,且仅对非流式响应生效。叫 deepseek 只是因为字段约定源于 DeepSeek API,与模型是 Qwen 无关
-b 2048 -ub 512 批大小 / 微批大小
--host/--port 监听地址与端口,0.0.0.0 允许局域网访问
--temp/--top-p/--top-k/--min-p 采样参数
--image-min-tokens 1024 (可选,启动日志提示)Qwen-VL 视觉 grounding 任务精度需要 ≥1024 图像 token;不处理视觉定位可省略
--reasoning-preserve (可选,启动日志提示)新版 llama.cpp 支持在对话中保留/传递上一轮思考痕迹,避免多轮思考丢失

💡 关于上下文长度 :上述主命令用原生 --ctx-size 262144(不加任何 rope flag),质量最好,是日常 / Coding Agent 的推荐默认。只有真要塞 500K~1M 文档/代码库时,才使用下方的 YaRN 变体。

阶段 3 变体:开启官方 1M 长上下文(YaRN)

⚠️ 官方重要提醒 :Qwen 明确指出静态 YaRN 的缩放因子对长短输入一视同仁,开启后会略微降低短文本质量。因此------

  • 日常 / Coding Agent → 不要开 YaRN ,用原生 262K(--ctx-size 262144,不加任何 rope flag),质量最好。
  • 只有真要塞 500K~1M 文档/代码库时才开下面这套。

原生上下文 262,144;YaRN factor=4.0 把它扩展至 ≈1.05M。命令(同样不含行内注释):

bash 复制代码
/opt/llama.cpp-src/build/bin/llama-server \
  -m /opt/models/Qwen3.8-27B/Qwen3.8-27B-Q8_0.gguf \
  --mmproj /opt/models/Qwen3.8-27B/mmproj-F16.gguf \
  -ngl 99 --flash-attn on \
  --ctx-size 1000000 \
  --cache-type-k q8_0 --cache-type-v q8_0 \
  --rope-scaling yarn \
  --rope-scale 4.0 \
  --yarn-orig-ctx 262144 \
  --override-kv qwen35.context_length=int:1000000 \
  --spec-type draft-mtp --spec-draft-n-max 3 \
  --parallel 1 --jinja \
  --reasoning-format deepseek \
  -b 2048 -ub 512 \
  --host 0.0.0.0 --port 8080 \
  --temp 1.0 --top-p 0.95 --top-k 20 --min-p 0.0 \
  2>&1 | tee /tmp/qwen38.log

YaRN 关键参数说明(4 项必配 + 1 项可选)

flag 作用
--rope-scaling yarn 选 YaRN 缩放(非 linear/NTK)。Qwen3.8 配置 rope_type=yarn
--rope-scale 4.0 缩放因子 = 目标上下文 / 原生上下文。Qwen 官方对 1M 给 4.0 (≈1.05M)。若典型上下文 ~524K,改 2.0 更稳、对短文本损伤更小
--yarn-orig-ctx 262144 告诉 YaRN 模型原本训练在 262K 上,缩放以它为基准。漏填会退默认
--override-kv qwen35.context_length=int:1000000 GGUF 元数据上限默认 262K;这条抬到 1M,否则 --ctx-size 1000000 被拒。密集模型用 qwen35,MoE 用 qwen35moe (本模型是密集 qwen35
--yarn-beta-fast/-slow 一般不改,留 -1(自动)。仅当长程检索丢东西再调

Thor 内存现实(1M 下)

  • 权重 Q8_0 ~29GB + KV:因 48 层是 Gated DeltaNet(循环状态 O(1),不随长度线性增长),只有 16 层 Gated Attention 吃 O(N) KV,比纯注意力 27B 节省很多;但 1M token 的 KV 仍要 数十 GB (以启动日志 kv cache 行 / --verbose 为准)。
  • 粗估:262K 全量 KV ≈16GB,1M ≈4× → ~64GB;+29GB 权重 ≈ 93GB ,逼近 Thor 122GB 上限。建议先 --ctx-size 262144(原生、无 YaRN)或 524288(factor=2.0)验证,再上 1M
  • prefill 1M token 在 Thor ~273GB/s 带宽下极慢(分钟级),仅适合「一次灌入、反复问答」的文库场景,不适合交互式逐字对话。

验证 override 键名 :启动日志若报 unknown metadata key 或拒绝超长上下文,先确认 arch 前缀------

bash 复制代码
grep -i qwen35 /tmp/qwen38.log

若前缀是 qwen3_5 而非 qwen35,把 --override-kv 键改成 qwen3_5.context_length=int:1000000

阶段 4:冒烟测试 + 功能验证

先确认服务真的活着(权重缺失时 llama-server 秒退,这里必然失败):

bash 复制代码
ps aux | grep llama-server | grep -v grep     # ① 有进程
ss -tlnp | grep 8080                           # ② 8080 正在监听
tail -5 /tmp/qwen38.log                        # ③ 日志末尾是 listening 而非报错

① ② ③ 全过再往下测;任一失败 → 回到阶段 2 前置检查 / 阶段 4 排错表。

4.1 基础对话(curl)

bash 复制代码
curl -s http://127.0.0.1:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"local","messages":[{"role":"user","content":"用一句话介绍你自己。"}],"max_tokens":200}'
  • 预期结果:返回正常 首 token 延迟(TTFT) ~0.7 s 生成速度 17.2 tok/s(MTP 生效)

4.2 思考档位控制(降低 effort 换速度)

bash 复制代码
# 关闭思考模式
curl -s http://127.0.0.1:8080/v1/chat/completions -H "Content-Type: application/json" \
  -d '{"model":"local","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":100,"enable_thinking":false}'

# 降档到 low(更快)
curl -s http://127.0.0.1:8080/v1/chat/completions -H "Content-Type: application/json" \
  -d '{"model":"local","messages":[{"role":"user","content":"1+1=?"}],"max_tokens":100,"reasoning_effort":"low"}'
  • 预期结果:思考输出可用(默认 effort 未显式指定时,reasoning_content 返回完整思考过程) low 档位与 enable_thinking=false 按需验证

4.3 多模态(仅当阶段3加了 --mmproj)

bash 复制代码
curl -s http://127.0.0.1:8080/v1/chat/completions -H "Content-Type: application/json" \
  -d '{"model":"local","messages":[{"role":"user","content":[
    {"type":"text","text":"图里有什么?"},
    {"type":"image_url","image_url":{"url":"data:image/jpeg;base64,'"$(base64 -w0 photo.jpg)"'"}}]}]}'
  • 预期结果:图像输入被接受;若被忽略,检查是否加载了 --mmproj

阶段 5:性能基准测试

用一段固定 prompt(如「写一个 Python CLI,扫描文件夹里 Markdown 的失效相对链接,含单元测试」),记录:

  • 输出 ~4096 tokens 的总耗时 → 生成速度 tok/s
  • TTFT(首 token 延迟)
  • 长上下文 prefill:60K prompt 总耗时(对照:SGLang/llama.cpp x86 ~40s 量级)

评估口径(参考社区):<8 tok/s 能用但慢;8--20 舒适;20+ 快。Q8_0 在 Thor 上实测 17.2 tok/s(MTP 生效),落入「舒适」档,显著高于带宽墙预估的 9--12。

阶段 6:接入 WorkBuddy / Pi

接入本地端点有两条路:GUI 添加(简单,适合大多数人)直接改 JSON(进阶,字段最全) 。两者等价------GUI 保存后本质也是写 models.json;GUI 没暴露的高级字段,最后用 JSON 补齐即可。

6.1 方式一:WorkBuddy GUI 添加(简单,推荐)

在 WorkBuddy 设置里找自定义模型 / 自定义端点 / 模型管理入口(不同版本菜单位置和叫法略有差异,以你界面上的实际入口为准),新增一条,填三个必填项:

字段(GUI 上的叫法) 填什么 说明
名称 / 显示名 Thor Qwen3.8-27B Q8 模型选择器里显示的名字,随意
Base URL / API 地址 http://<THOR_IP>:8080/v1 OpenAI 兼容根地址(注意带 /v1 ,别只填到 :8080
API Key local-thor(任意非空字符串) llama.cpp 不校验 key,随便填、不为空即可
模型 ID(如 GUI 提供) thor-qwen38-q8 若 GUI 让你填模型名,就填这个

保存后刷新,模型选择器里就能看到它,直接开聊。GUI 会把这条配置落到 %USERPROFILE%\.workbuddy\models.json------效果与手写 JSON 完全等价。

⚠️ GUI 通常只暴露基础字段。如果还想控制这些高级项,GUI 没有对应输入框 → 转 6.2 手改 JSON 补齐:

  • maxInputTokens强烈建议 65536(设太大 = 无限制,agent 会塞入超长上下文 → prefill 超时 → 500,详见第五章 5.4)
  • reasoning.defaultEffort:思考档位默认 lownone 关闭,xhigh 最强但最慢)
  • supportsImages / supportsToolCall / supportsReasoning:能力开关,影响 WorkBuddy 是否向你开放这些功能
6.2 方式二:直接改 JSON(进阶,字段最全)

%USERPROFILE%\.workbuddy\models.json 追加(字段对齐本机既有 DeepSeek 条目,思考默认开启):

json 复制代码
{
  "id": "thor-qwen38-q8",
  "name": "Thor Qwen3.8-27B Q8",
  "vendor": "Custom",
  "url": "http://<THOR_IP>:8080/v1/chat/completions",
  "apiKey": "local-thor",
  "supportsToolCall": true,
  "supportsImages": true,
  "supportsReasoning": true,
  "useCustomProtocol": false,
  "reasoning": {
    "defaultEffort": "low",
    "supportedEfforts": ["none", "low", "medium", "high", "xhigh"]
  },
  "maxInputTokens": 65536,
  "maxOutputTokens": 16384
}

reasoning.defaultEffort 设为 "low"(思考开启,但较轻量------Thor 上 Q8_0 实测 17.2 tok/s,xhigh 会明显变慢)。更难的任务可在 WorkBuddy 里手动切到 medium / high / xhighnone 为关闭思考。若想要模型原生最强思考,把 "defaultEffort" 改成 "xhigh" 即可。

⚠️ maxInputTokens 为什么是 65536 而不是 262144 :服务端 --ctx-size 保持 262144 不变(模型支持长上下文),但 WorkBuddy 侧把它当作单次请求的输入上限。若设成 262144,agent 会把巨量上下文全塞进来(实测曾一次塞入 92,329 tokens)→ Thor 上 prefill 要 300 秒+ → 客户端超时 → 报 500(详见第五章 5.4 实战案例)。65536 对 agent 场景绰绰有余(单轮通常几千 token),prefill 几秒内完成。

Pi%USERPROFILE%\.pi\agent\models.jsoncontextWindow 同样建议 65536,原因同上)。Pi 没有 GUI,只能改 JSON------它是纯 CLI 工具,配置只有这一个入口:

json 复制代码
{"providers":{"thor-local":{"baseUrl":"http://<THOR_IP>:8080/v1","apiKey":"local-thor",
  "models":{"thor-qwen38-q8":{"contextWindow":65536,"maxOutputTokens":16384,"supportsReasoning":true}}}}}
  • 验证:WorkBuddy 可调用(实测对话通过) Pi 可调用(配置写入后即可用)

停止与清理 :停止服务用 pkill llama-server(或 Ctrl-C);杀进程后 jtop 仍显示数十 GB 占用属正常(NVIDIA 驱动缓存池),执行 sync && echo 3 > /proc/sys/vm/drop_caches 即可释放,非内存泄漏。


四、已知坑与排错

现象 原因 解决
unknown architecture / 拒绝加载 llama.cpp 版本 < b8001(qwen35 未支持) 更新并重编(阶段1)
prefill 极慢(~35 tok/s) KV cache 用了 q4_0 静默退回 CPU --cache-type-k/q8_0
多轮「失忆」、历史截断 默认模板思考块(<think>)多轮嵌套 --jinja(用修正模板)
多占 0.44GiB llama-server 默认 4 槽 --parallel 1
--spec-type draft-mtp 报错 GGUF 被剥离 MTP 头 删掉该 flag(仅失投机解码提速)
图像被静默忽略 未加载 mmproj --mmproj mmproj-F16.gguf
启动命令秒退,日志报 failed to open GGUF file ... (No such file or directory) 模型权重/mmproj 未传到 Thor(阶段2 未完成) 完成阶段 2 并做前置检查(ls -lh /opt/models/Qwen3.8-27B/)后再启动
hf download 已 100% 但启动仍报 No such file --local-dir 用了相对路径,文件下到当前目录(如 /opt/llama.cpp-src/Qwen3.8-27B/ 用绝对路径重下,或 mv /opt/llama.cpp-src/Qwen3.8-27B /opt/models/ 挪正
冒烟测试 curl 无反应 / connection refused 服务没起来(权重缺失秒退、端口被占、未加 --host 0.0.0.0 按阶段 4 开头「三连检查」定位:pssstail 日志
杀进程后 jtop 仍显示数十 GB 占用 NVIDIA 驱动缓存池(KReclaimable),非泄漏 sync && echo 3 > /proc/sys/vm/drop_caches
WorkBuddy 报 500 / parse_error 输入上下文过大 → prefill 超时 → 客户端超时重试于脏连接(详见 5.4 实战案例) models.jsonmaxInputTokens 降到 65536

五、运行中错误排查(读日志方法论)

本节是实际排查「WorkBuddy 调 Thor 报 500」的完整复盘,沉淀为通用方法。排查运行期问题的第一动作永远是:看日志 /tmp/qwen38.log (启动命令已 2>&1 | tee /tmp/qwen38.log 落盘)。

5.1 三连检查(先判断服务死活,30 秒)

bash 复制代码
ps aux | grep llama-server | grep -v grep     # ① 进程在不在(有输出=活着)
ss -tlnp | grep 8080                           # ② 8080 是否监听
tail -5 /tmp/qwen38.log                        # ③ 日志末尾是 listening 还是报错

① ② ③ 全过 → 服务活着,问题在请求侧(往下看日志);任一失败 → 服务没起来,去阶段 2/3 排错。

5.2 日志关键行解读表(读日志要会看什么)

日志片段(节选) 含义 判断
I srv llama_server: model loaded 模型加载成功 ✅ 正常
I srv llama_server: listening on http://0.0.0.0:8080 服务已监听,可接受请求 ✅ 正常
I llama_model_loader: arch = qwen35 架构识别成功(qwen35) ✅ 正常
I slot print_timing: prompt processing, n_tokens = 92329, ... tokens per second prefill 进度:已处理的 token 数 / 当前速度 ⚠️ 长 prompt 会很大,用于判断是否超时根因
I slot print_timing: n_gen = 157, tg = 12.61 tokens per second 生成速度(tg=实际输出 tok/s) ✅ 正常,即速度
I slot print_timing: draft acceptance = 0.61 MTP 投机接受率(0.61=61% 被接受) ✅ >0.3 即有效
I slot print_timing: total time = ... / 92517 tokens 整次请求总耗时/总 token ⚠️ 判断慢在哪
W srv operator(): got exception: {"error":...} 请求处理异常,客户端 500 的直接来源 ❌ 看 error 里 message
E gguf_init_from_file: failed to open GGUF file ... 权重文件缺失/路径不对(启动即退) ❌ 回阶段 2
E ... unknown architecture ... 版本太旧不支持 qwen35 ❌ 回阶段 1 重编

5.3 常用日志操作

bash 复制代码
tail -f /tmp/qwen38.log                          # 实时跟踪(排错首选)
tail -100 /tmp/qwen38.log                        # 最近 100 行
grep -i "exception\|failed\|error" /tmp/qwen38.log  # 只看错误
grep "print_timing" /tmp/qwen38.log | tail -5     # 最近几次请求的计时汇总
grep "draft acceptance" /tmp/qwen38.log | tail -3 # MTP 接受率趋势

5.4 实战案例:WorkBuddy 报 500 / parse_error

现象 :WorkBuddy 对话报 500|Trace ID: ...,提示"切换模型或重试"。

排查过程(就是读日志):

  1. 三连检查 → 进程在、8080 监听、日志末尾有 listening服务活着,问题在请求侧

  2. tail -60 /tmp/qwen38.log → 发现异常前一刻有一条巨大请求的计时:

    复制代码
    I slot print_timing: prompt eval time = 301350.38 ms / 92329 tokens (3.26 ms/token, 306.38 tokens per second)
    I slot print_timing: total time = 316184.13 ms / 92517 tokens
  3. 紧跟着:

    复制代码
    W srv operator(): got exception: {"error":{"code":500,"message":"[json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - invalid literal; last read: 'P'",...}}

解读(两层)

  • 第一层parse error ... last read: 'P' --- 请求 body 首个字符是 P,不是 {。这是请求头没被完整消费、body 从错误位置开始读 的典型症状,常见于客户端超时后重试连接把脏数据发过来。
  • 第二层(根因) :为什么会超时?看那条 92,329 tokens 的 prefill------WorkBuddy 把 92K 上下文全塞进来了maxInputTokens 当时是 262144,等于没限制),Thor 上 prefill 92K 要 301 秒(5 分钟)。客户端等不了 → 超时 → 连接被复用/重试 → 脏连接上收到残缺请求 → 500。

根因一句话 :不是 WorkBuddy 对 llama.cpp 支持不好,而是 maxInputTokens 设太大 + 慢 prefill 撞上客户端超时。llama.cpp 本身把 92K 请求处理完了(total time 316s 是完整跑完的),是客户端先放弃了。

解决models.jsonthor-qwen38-q8.maxInputTokens 262144 → 65536。效果:WorkBuddy 不再塞超长上下文,prefill 秒级完成,500 消失。

预防与后续

  • 若还偶发 500 → 再降到 32768;或看是否是多轮对话历史累计过长(换新会话)。
  • 服务端 --ctx-size 保持 262144 不冲突------限制的是客户端单次发送上限,不是服务端能力。
  • 真需要 100K+ 长上下文(文库问答)→ 这类场景本质不适合 300s prefill 的交互式对话,建议换 vLLM 或接受慢速。

5.5 排查口诀

进程 → 端口 → 日志 → 找 got exception → 看异常前那条 print_timing 的 n_tokens 和耗时。99% 的"客户端 500 / 无反应"都能在这条链路里定位到根因。


六、实验结果记录表

指标 预期 实测
llama.cpp 版本 ≥ b8001(或源码含 qwen35) 0.1.0-dev (build 1, commit 4197155),源码含 qwen35
架构识别 qwen35 qwen35 ✅(new llama_model_qwen35
加载耗时 --- 启动即监听(mmap 懒加载);首次推理含权重换入共 7.2 s
峰值内存占用(含 KV+mmproj) ~30 GB@32K / ~38 GB@128K / ~46 GB@262K 进程 RSS ~2.8 GB(mmap 懒加载,权重走页缓存;以 jtop 为准)
单流生成速度(Q8_0) ~9--12 tok/s 17.2 tok/s(MTP 生效,超预期)
prefill 速度 --- 89.4 tok/s(62 tok 用时 693 ms)
TTFT(短 prompt) --- ~0.7 s(prompt eval 693ms)
MTP 提速比 ~1.5--2× draft acceptance 0.56(71/126),mean len 2.69
思考输出 reasoning_content 字段 ✅ 返回完整思考过程于 message.reasoning_content
视觉可用 是/否 mmproj 加载成功,未实测图像输入
WorkBuddy 接入 成功 ✅ 已配置并实测对话通过;后因 maxInputTokens 过大遇 500,降 65536 解决(见 5.4)
Pi 接入 成功 未配置(配置见阶段 6)

七、结论与后续

  • 结论 :Q8_0 在 Thor 上可行 ,实测单流 17.2 tok/s(MTP 投机解码生效,显著高于带宽墙预估的 9--12),思考模式正常输出,多模态/mmproj 加载成功,OpenAI 兼容端点可用。
  • 若嫌慢 :换 Q4_K_M(~17GB)命令仅改文件名,预期速度升至 ~20+ tok/s。
  • 若需更高并发/吞吐:可转用 vLLM 源码编译 + NVFP4(~14GB,Blackwell 4-bit 张量核,参照 GB10 实测 ~25 tok/s),代价是 ARM 编译。
  • 内存富余利用:Thor 128GB 下可放心开 128K--262K 上下文(KV 仅 8--16GB),无需降上下文保显存。
  • 后续可测 :① 图像输入实测(4.3,mmproj 已加载);② 长上下文 prefill(60K prompt);③ WorkBuddy 接入后的多轮 agent 场景吞吐(基础对话已验证,多轮思考保留待测);④ 各 reasoning_effort 档位的速度/质量差异实测。

附:本实验依据社区实测(unsloth GGUF、smeltcore RTX3090/5060Ti 配方、locallyuncensored 量化表、GB10 同代 Blackwell 实测)整理;Thor 具体数值以本次实测为准。

相关推荐
SaaS发言人1 小时前
渠道数字化解决方案横评:谁更懂快消
java·大数据·人工智能
新知图书1 小时前
7.3 面向Agent的提示词设计方法论
人工智能·数据分析·agent·ai agent·智能体
极客猴子1 小时前
多人发言自动区分的录音转文字APP:AI发言人识别实测
人工智能·智能手机·语音识别
数字融合1 小时前
透明化视频孪生大数据全域时空对齐应用技术
大数据·人工智能·virtualenv
AI导出鸭PC端1 小时前
Perplexity怎么复制表格?AI 导出鸭的“格式网关”给出了一个硬核答案
人工智能·豆包·ai导出鸭
ZJU_统一阿萨姆1 小时前
【推理优化进阶】Hopper_Blackwell 微架构:从指令、流水线到真实性能上限
开发语言·人工智能·语言模型·架构·开源
东坡肘子1 小时前
越来越慢的 App Review,拦住了谁? -- 肘子的 Swift 周报 #149
人工智能·swiftui·swift
呆呆敲代码的小Y1 小时前
Magic Resume 实战:开源 AI 简历编辑器(多模型 + 本地存储)
人工智能·开源·编辑器·magic resume·简历编辑器