① 硬件与选型 ② 模型与量化 ③ 编译与启动 ④ 参数逐项 ⑤ 显存标定 ⑥ ctx 调优史 ⑦ 休眠机制

① 硬件基线与栈选型

硬件规格
GPURTX 5090 32GB(Blackwell 架构,整卡 32,607 MB)
CUDA13.4(驱动 616.92,必须 ≥ 12.x 才能用 Blackwell NVFP4 加速)
CPU / RAMAMD Ryzen 9 9950X3D 16C · 61.4 GB

为什么选 llama-server 而不是 Ollama / LM Studio:

方案优点缺点
llama-server轻量无依赖 · 原生 GGUF · NVFP4 tensor core 加速 · OpenAI 兼容 API要自己编译或下二进制
Ollama装完就能用 · 模型管理方便fork 版本滞后 · offload 不彻底 · 强占 11434 端口且自恢复极强杀不掉
LM StudioGUI 友好 · 自动下载闭源 · 功能受限 · 启动慢

核心理由:能把 Blackwell 的 tensor core 吃满,没有中间层损耗。Ollama 只是退回 11434 自己玩,主栈改用 11435(llama-server)/ 11436(proxy)。

② 模型与量化选型(两次演进)

第一代 NVFP4 量化模型 → 第二代三值量化底座,选型逻辑完全不同。

第一代:qwen3.8-27b-nvfp4(9/13 部署)——先决定参数量:

候选参数量量化大小结论
qwen3.8-7b7BNVFP4~5 GB太小,Agent 多工具调用推理质量不够
qwen3.8-14b14BNVFP4~10 GB浪费一半显存
qwen3.8-27b27BNVFP419.6 GB32GB 卡刚好填满又不爆
qwen3.8-57b57BNVFP4~40 GB显存不够

NVFP4 是 Blackwell 原生 4-bit 浮点格式:GGUF 里 tensor 直接以 NVFP4 dtype 存储,加载后走 tcgen05 张量核心加速;换 Q4_K_M 等整数格式只能 F16 模拟,慢一倍左右。模型直接下 ModelScope 预量化版,省掉 54GB 原始权重 + 3 小时量化时间。

第二代:Ternary-Bonsai-2-27B-PQ2_0(9/22 切换)——1.71-bit 三值量化,同尺寸显存下上下文翻倍(ctx 上限 → 262,144)。换模型不是下载就完:量化格式变了,KV 量化档位、chat-template 补丁锚点、MTP 层有无全部跟着变(详见性能页与排障页)。

KV cache 量化三档演进(都是实测):

  1. f16 原生:66.3 KiB/token,26 万 ctx 根本放不下;
  2. q8_0 / q5_1(9/21):降到 30.0 KiB/token,质量损失极小。K 必须保 q8_0——K 进注意力分数,量化误差会被放大到每个 token;V 只是加权求和,误差会平均掉;
  3. q8_0 / q4_0 是本机构建的坑档:日志报 no FlashAttention vector kernel compiled for K/V types q8_0-q4_0,每次注意力现场转 f16,省显存赔速度,不要这么配;
  4. q4_0 / q4_0(10/4,前提是 -fa on + 换 Bonsai 底座):质量 7/7 零退化、速度 +13.6%、显存 −4.35 GB——nvfp4 时代"别用 q4_0"的注释前提已变。

③ 编译与启动

2026-09-12 用 VS2026 Developer Command Prompt(vcvarsall x64)编译,确保找到 CUDA SDK。

REM 1. 拉源码(带 CUDA 后端)
git clone --recursive https://github.com/ggml-org/llama.cpp.git D:\_Qwen3.8-27b\_llama_src

REM 2. CMake configure —— 指定 CUDA 后端
cd D:\_Qwen3.8-27b\_llama_src
cmake -B build -G Ninja -DGGML_CUDA=ON ^
  -DGGML_CUDA_CUDA_PATH="C:/Program Files/NVIDIA GPU Computing Toolkit/CUDA/v13.4"

REM 3. 编译 + 产物复制
cmake --build build --config Release --parallel
copy build\bin\Release\*.exe ..\llama_cpp\
copy build\bin\Release\*.dll ..\llama_cpp\

关键产物:llama-server-impl.dll(8.9 MB 核心实现)+ ggml-cuda.dll(144.9 MB CUDA 加速后端)。首次加载 65 秒——mmap 要把 19.6GB GGUF 分页进显存。后来换用上游预编译包 prism-b10754(自带 MTP Hadamard 修复 PR #205 与 prefill 优化),但旧目录原封保留作回滚点。

④ 启动参数逐项解释

当前生效配置(2026-10-04)。每个参数都有"为什么",改之前先看这张表。

参数为什么
-c 26214426 万 ctx。显存占用随 KV 量化下降而可控(见 ⑤),同时给 ZCode 的估算偏差留绝对缓冲
-fa onFlash Attention——KV 量化的前提,也更快
-ctk q4_0 -ctv q4_0KV 双 q4_0:质量 7/7 零退化 + 显存 −4.35 GB。前提是 -fa on;q8_0/q4_0 混合档是坑,不要配
-ngl 999全量 GPU offload
-np 1Agent 是串行请求,多槽只是白占 KV
-b 2048 -ub 512batch / ubatch。cont-batching 时代定下的值,prefill 快 70%+ 且省 4GB
--no-reasoning-preserve模板默认把历史里的 thinking 全留下,会持续吃上下文
--reasoning-budget 1000压掉过度思考:思考 −76%,正文 0 → 4,820 字,准确度无下降(见性能页)
--mmproj ...gguf视觉投影器(0.6 GB):真实截图逐字读出。脚本里带 if exist 守卫,删文件自动退回纯文本
--sleep-idle-seconds 1803 分钟不用把显存还给 UE;端口保持监听,7.6 秒唤醒(见 ⑦)
--chat-template-file ...打补丁的模板:修 reasoning_effort 500 + 对话中间 system 消息两个 500 根因
-a "local,qwen3.8:27b,...offload"别名列表:客户端 model 名随便填都能命中
--metrics --log-file ...指标 + 落盘日志:排障与速度归因全靠它

调参旋钮(环境变量临时覆盖,仅影响这一次启动):

set LLM_CTX=131072      & start_local_llm.bat   REM 降 ctx(同步把 contextWindow 改成 ~×0.75)
set LLM_SLEEP=60        & start_local_llm.bat   REM 1 分钟就还显存;-1 永不归还
set LLM_THINK=0         & start_local_llm.bat   REM 关 thinking:约快一倍,推理变浅
set LLM_RBUDGET=500     & start_local_llm.bat   REM 收紧思考预算

⑤ 显存标定(实测公式)

显存标尺必须在"开着 UE"的状态下测——引擎没开时测出来的基础占用(2,086 MB)不能拿来算预算。

第一代模型(nvfp4,q8_0 KV 时代)的标定:

KV 单 token = 16 层 × 4 头 × (256+256) 维 × 1.0625 B(q8_0) ≈ 35.2 KiB / token
总占用 ≈ 16.5 GiB(权重 + compute buffer + CUDA 上下文) + ctx × 35.2 KiB
ctxKV 类型实占(UE 同开)整卡剩余判断
65536q8_0/q8_0~18,900 MB~6,200 MB保守,但 ZCode 容易冲过去
81920q8_0/q8_0~19,200 MB~5,300 MB实测被冲过去过
114688q8_0/q8_0~20,608 MB~4,753 MB稳定运行过
131072q8_0/q5_1~20,594 MB~4,750 MB同显存、上下文 +14%

第二代模型(Bonsai 2)的标尺:净占用 = 整卡唤醒 − 睡眠 = 20,702 − 2,828 ≈ 17.5 GiB;KV4 后整卡峰值 20,964 MiB。模型预算(用户定 ~21 GB)始终按"UE 开着"核算:整卡 32,607 − UE 桌面+引擎 7,489 ≈ 25 GB 可用,再留 UE 后续增长余量。

⑥ ctx 调优历史(9 个阶段)

一次又一次改 -c 的完整轨迹——每一步都是被某个 400/性能问题逼出来的。

32768

9/13 首启。能跑,但 Agent 带 tools 不够用

8192

想首字更快改小 → 400:46,911 > 8,192

131072

改大解决 400 → 显存 30.7 GB 顶格,prefill 慢

65536

9/16 砍半 + cont-batching:prefill 3.8s → 1.1s,显存省 4GB

81920→98304

9/17 ZCode 时代:按"估算偏差 + 压缩开销"配对上调

114688

9/18 再上调:触发点 81,920 在 1.26 系数下安全

262144

9/22 Bonsai 2 换底座:三值量化下显存放得下 26 万

这 9 步里真正可迁移的规律只有一条:服务端 ctx 不是跟显存上限走,是跟客户端估算偏差走——每档提升都发生在"又冲过去一次"之后(见性能页 · token 估算偏差)。

⑦ 休眠机制:为什么"清除"必须是休眠

"打开就用、不用就还显存"的唯一正确实现 = 进程常驻 + 空闲休眠。

做法端口显存客户端用的时候
退出进程不在监听0连接被拒 → "重新连接中 N/10"
休眠(sleep)仍在监听~2.4 GB请求到达 → 7.6 秒唤醒 → 正常回答

实测(--sleep-idle-seconds 180):使用中 27,430 MB(含 UE)→ 空闲 180 秒 ~2,400 MB(日志 handle_sleep: server is entering sleeping state)→ 再来请求 7.2 秒返回 200。早期用 proxy 级 kill 实现释放,但有计时 bug 且重启会退回旧参数,全部废弃——llama.cpp 原生支持,一层就够。