① 模型规格(从 GGUF 头解析,不是猜的)
general.architecture = qwen35 (Qwen3.5 混合注意力) total parameters = 27.32B (密集模型,非 MoE) block_count = 65 (blk.64 是 nextn/MTP 层,被 llama.cpp 忽略) attention.head_count = 24 attention.head_count_kv = 4 (KV 只有 4 头) key_length/value_length = 256 (每头 256 维) full_attention_interval = 4 (每 4 层才有 1 层全注意力) ssm.* (其余 ~49 层是线性注意力 GatedDeltaNet) 文件大小 = 19,653,896,608 字节(18.30 GiB,nvfp4 时代)
关键推论:只有 16 层参与 KV cache((i+1)%4==0 的层),另外 49 层是 SSM 线性注意力——状态固定大小、不随 token 增长。这就是同尺寸传统 Transformer 爆显存的地方,这个模型能开 26 万上下文的原因。prefill 也因此极快:6.4 万 token 提示词 33.5 秒(1,905 t/s),prompt cache 命中率 99.7%。
② 生成速度随上下文长度下降(实测,不是故障)
2026-09-21 直接读 llama_server.log 的 eval time。每生成一个 token 都要对全部已缓存 KV 做注意力——上下文越长越慢,这是算术。
同时确认没有溢出:共享显存仅 505 MB(预算内)、专用显存与标定一致。系统内存 86% 是另一回事——模型 21 GB 以 mmap 驻留,内存紧张只影响加载快慢,不影响生成速度(KV 全在显存)。
嫌慢的三个抓手(按性价比):① LLM_THINK=0 关思考链约快 2 倍(或用更优的 --reasoning-budget,见 ⑤);② 缩短上下文(代价:压缩更频繁);③ 减少工具底噪,让同样长度装更多有效内容。自查:findstr /C:"eval time" llama_server.log。
③ 27 t/s 之谜 = UE 编辑器抢 GPU
速度在 26~28 与 71 t/s 两档间翻转(12h 日志:1780 次快 / 74 次慢),任务中途也会跳变。
| 排查动作 | 结论 |
|---|---|
| 同参数同模型,快档 71 t/s 存在 | 不是量化/模型退化——能力还在 |
Get-Counter '\GPU Engine(*)' 按进程定位 | UE 视口实时渲染常驻占 42~65% GPU;灯控程序 MythCool 再吃 9~10% |
| UE 安静时探测请求 | 70 t/s;UE 渲染时 35.79 t/s |
| 显存检查 | 无溢出——不是显存问题,是算力被分走 |
对策(等模型生成时):UE 最小化 / Ctrl+R 关视口实时渲染 / 控制台 t.MaxFPS 30 / 编辑器偏好勾选 "Use Less CPU in Background"。
④ ZCode 的 token 估算不能信(三次实测,偏差不稳定)
| 场景 | ZCode 以为 | 实际发出 | 低估 |
|---|---|---|---|
| 场景一 | ≤61,440 | 64,824 | 5.5% |
| 场景二 | ≤65,536 | 75,208 | 15% |
| 场景三 | ≤65,536 | 82,324 | 26% |
第三次直接把服务端打 400:request (82324 tokens) exceeds the available context size (81920 tokens)。所以:llama-server 的 --context-shift 默认关闭,超了就硬报错,不会静默截断;ZCode 的 contextWindow 不是保护,只是"什么时候开始压缩"的触发点,压不住它自己去冲。服务端 ctx 必须比客户端上限高出一大截做绝对缓冲(配对公式见接入页)。
⑤ 过度思考的根治:--reasoning-budget 1000
背景:一个 UE 任务跑 2h36m,拆账发现 85% 是模型生成,且经常把整个输出预算烧在思考上、正文一个字不出。
探针实测(同一道 UE 推理题,max_tokens=4000):
| 配置 | thinking | content | finish |
|---|---|---|---|
| 原状(无 budget) | 10,037 字 | 0 字 | length |
--reasoning-budget 2500 | 2,197 字 | 5,368 字 | ✅ |
--reasoning-budget 1000(上线) | 2,430 字 | 4,820 字 | ✅ |
根因:模板在 xhigh 档位注入"请验证关键假设、考虑替代方案"的系统指令——这句就是在教模型过度思考,而 ZCode 每轮发的 high 被强制成 xhigh,每轮都被这样指示。
准确度对照(不能只看速度):数列推理 / 指令遵循 / tool_calls 三项测试,RB=1000 与原状答案全部一致——压掉的确实全是废话(正文里模型自己发现了题目的数字矛盾,推理质量保持)。
试过但已还原:模板强制 medium——实测两项略好一项略差一项不稳,全在噪声内,且"未验证的改动不留在生产环境",已逐字节还原。三条候选路线(Swift-1.5+kvmem、NVFP4-Q8mix、Swift-Bonsai-2)也全部实测或评估否决——Swift 宣称"思考少 58.5%"未复现(同题同预算下反而想得更多),厂商宣称不能直接信。
⑥ 四项优化全程:KV4 采纳,MTP 撤下
2026-10-04 按"先建基线、逐项优化、每步自检"执行。阶段:T0 = 优化前;T_KV4 / T_MTP = 中间态。
| 优化项 | 结果 | 判定 |
|---|---|---|
| ① 采样参数核对 | 服务器默认正是官方推荐值(temp 1.0 / top_p 0.95 / top_k 20) | 不存在这项优化,零改动 |
| ② KV4(-ctk/-ctv q4_0) | 质量 7/7 零退化 · 27.5K+100K 检索 PASS · 速度 +13.6% · 整卡显存 −4.35 GB | ✅ 采纳 |
| ③ MTP 投机解码 | 走了三条路(社区 MTP 模型 → Hadamard 修复缺失;kvmem 包 → 缺 sleep-idle;升级 prism-b10754 → 跑通,服务器侧 +23%) | ⚠️ 后撤下(见下) |
| ④ 基线测试脚本 | llm_bench.py:7 题质量 + 长文检索 + 速度,零文件写入 | ✅ 沉淀 |
MTP 为什么撤下——用户实测反馈"感觉变慢了:显存确实少了,但 GPU 利用率只有二十多"。同一时段同 ~100K 上下文对比:
| 配置 | 合成请求(全新 prefill) | 用户会话(复用前缀) |
|---|---|---|
| 原始(q8_0 / 无 MTP) | 67.7 t/s | ~68 t/s |
| KV4 + MTP | 125~142 t/s | 32~54 t/s ⚠️ |
| KV4 + 无 MTP(最终) | 70.3 t/s | 68~74 t/s |
根因(上游已知缺陷 llama.cpp #28049):投机解码的边界状态绑定在缓存前缀的精确末位上。真实代理流量是"部分前缀命中"(本机日志 f_sim=0.997 / f_keep=0.993)——差 0.3% 就导致缓存复用失效、每步重算。合成的全新 prefill 或精确命中测不出这个问题,所以用户"感觉变慢"是对的,机器数据也证明了。排查中还逐一排除了上下文悬崖、显存溢出、工具 schema、UE 抢卡四个干扰项(都实测过)。
最终生效配置:prism-b10754 + Bonsai-2-27B-PQ2_0 + -ctk q4_0 -ctv q4_0 + 无 --spec-type + 其余参数不变。MTP 模型文件保留(上游修复后可用 llm_bench.py --reuse 复测)。回滚凭据三层齐全(.bak 脚本 + 旧二进制目录原封 + bench 原始数据)。
⑦ 方法学教训(本轮最大收获)
速度类验证必须同时测两种请求形态:① 全新 prefill;② 前缀复用(同一 prompt 连发两次,模拟真实代理的逐轮累积)。两者结论可能相反——本机 MTP 就是第一种 110~142 t/s、第二种 32~54 t/s。合成测试复现真实流量形态失败,才会把负收益验证成正收益。基线阶段也踩过同型坑:测试题 max_tokens 给小了(160~320),思考预算没烧完正文就轮不到,三题空答案——那是脚本问题不是模型问题。
另外一个工程细节:探针/基准脚本要零文件写入(结果走 stdout,摘要走 stderr)——既能规避安全扫描的路径穿越拦截,也不会留垃圾。