@abaalei 你好啊 晚安喔, 我的cc努力了一天 把hermes調用工具修好了,但速度只有22左右,暫時修到這,,,對齊你給你的資訊後,他最後一次改的,調用工具又失敗了,等額度恢復再繼續折騰 ,分享我的進程 @colt 也同步給你喔
DFlash Hermes Server — 現況紀錄
核心需求(不可談判)
Context: 32k 先跑通,後上 64k
用途:Hermes agent 接 Telegram 任務請求
速度目標:decode 45 tok/s 以上
舊腳本 q4_64k_telegram.sh 可 30-40 tok/s,DFlash 要超越它
8k/16k context 不算完成
硬體
GPU: AMD Radeon RX 7900 XTX 24GB,ROCm/HIP,gfx1100 (RDNA3)
ComfyUI 在 port 8188(第二張 GPU 路徑),不能動
現有 production fallback: q4_64k_telegram.sh,不能覆蓋
目前主腳本
/home/jaran/dflash_oai_v2.sh
現在的參數:
Binary: /home/jaran/src/lucebox-hub/server/build-hip-7900xtx/dflash_server
Target: Qwen3.6-27B-Q4_K_M.gguf(14.99 GiB GPU)
Draft: dflash-draft-3.6-q4_k_m.gguf(Q4,request-scoped)
--max-ctx 32768
--fa-window 2048
--cache-type-k tq3_0 --cache-type-v tq3_0
--chunk 256
--ddtree --ddtree-budget 8
--draft-residency request-scoped
--default-max-tokens 1024 --hard-limit-reply-budget 1024
env: DFLASH27B_PREFILL_UBATCH=512
目前狀態:能跑,但完全不可用
觀察到的 log 數字(tq3_0 KV + Q4 draft)
prefill=9.0s for 5219 tokens
decode=1.6s speed=13.1 tok/s
total: 10.8s for 21 tokens output → 1.9 tok/s overall
9 秒 silent prefill → Hermes/Telegram 等不了,直接當掉
Decode 本身 13 tok/s,離 45 tok/s 目標還差 3 倍多
腳本沒 crash,是從 Hermes bot 角度「沒回應」
速度比較
預期(文章 bench):68 tok/s(test_dflash + 短 HumanEval prompt)
現在 dflash_server:13 tok/s decode,580 tok/s prefill(9s/5k tokens)
差距:prefill 慢 5x,decode 慢 5x
已試過、失敗的方向
tq3_0 → q4_0 KV(我的改法,已還原)
目的:以為 tq3_0 會強制 CHUNKED FA kernel(無 MMA),換 q4_0 開 MMA
結果:速度完全沒變(prefill 仍然 9.0s,decode 仍然 13.1 tok/s)
副作用:第 2 次 request OOM(ggml_gallocr_reserve_n_impl: failed to allocate 480.95 MiB)
結論:KV quantization 類型對速度無影響;q4_0 KV 比 tq3_0 多 0.5 GiB,讓第 2 request 的 gallocr 配不到
已還原回 tq3_0
fa-window 0 → 2048
沒有解決 OOM(Q8 draft 還是爆)
但有助省顯存(codex 改的,保留)
Q8 draft → Q4 draft
解決了 [unpark] draft restored OOM
代價:draft quality 降,acceptance rate 只有 10-23%,avg_commit 2.5-4.75
已知但未解的問題
問題 1:prefill 為何慢 5x?
5219 tokens / 9s = 580 tok/s,預期應該 3000+ tok/s
--chunk 256 傳給 ServerConfig,但 qwen35 backend 的 do_prefill 讀的是 DFLASH27B_PREFILL_UBATCH env var
DFLASH27B_PREFILL_UBATCH=512 在 script 裡已設,backend 實際用 512 token/chunk
--chunk 256 和 DFLASH27B_PREFILL_UBATCH 是否同一件事、--chunk 到底控制什麼,尚未查清
問題 2:gallocr OOM 在第 2 request
只有 q4_0 KV 會出現,tq3_0 KV 不會
480.95 MiB 配置失敗 (ggml_gallocr_reserve_n_impl)
原因不明——VRAM 估算應該夠,但實際不夠
問題 3:decode 為何慢 5x?
每個 spec-decode step 約 183ms,bandwidth-limited 應該 15ms
GPU 是否在 high performance mode?card1 是否是正確的 DRM card?尚未確認
可能是 ROCm 小 kernel dispatch 累積 overhead
尚未測試但可能有效的方向
pflash mode(在 server_main.cpp 裡有 --pflash 選項)
是一種「persistent flash prefill」,可能完全換掉 chunked prefill path
需查 pflash 在 qwen35/32k context 的 VRAM 需求和 API
DFLASH27B_PREFILL_UBATCH 調大(如 1024 或 2048)
減少 GPU dispatch 次數:5219/1024 = 5 次 vs 5219/512 = 10 次
代價:gallocr scratch 增大,需確認 tq3_0 KV 下能否容納
GPU 電源模式確認
腳本用 card1,但 7900 XTX 可能是 card0
若 power level 沒設到 high,decode 會慢數倍
prefix cache
目前 --prefix-cache-slots 0(關閉)
啟用後可快取 system prompt + tools(~2000 tokens),下次 request 只 prefill 新 tokens
代價:每個 slot 佔 ~full KV size VRAM
2026-06-12 最新修改(已 rebuild)
Fix 1:F16 → Q8_0(lazy rollback path)
檔案:src/qwen35/qwen35_target_graph.cpp line 364
migrate_prefill_cache 的 ssm_intermediate 用 F16,而 eager path 用 Q8_0
修正後節省 ~540 MiB VRAM
Fix 2:DFLASH27B_DRAFT_CTX_MAX=512 env var
新增 env var 支援到 qwen35_backend.cpp init() 開頭
根本原因:draft_ctx_max=4096(預設)→ feature_mirror cap=4096 → 400 MiB VRAM
更重要:每個 decode step,draft 要處理 min(committed, 4096) tokens
5000+ token Hermes 系統提示後,draft 處理 4096 tokens 每步
文章 HumanEval bench = 300 tokens(短 prompt)→ 13.6x 差距
draft_compute: 4096 token = ~160ms >> verify 52ms,這是 183ms/step 的根本原因
設 512:feature mirror 400 MiB → 50 MiB,draft_compute ~20ms
預估:total step ~82ms,avg_commit ~4.5 tokens → ~55 tok/s
Script 變更
加了 DFLASH27B_DRAFT_CTX_MAX=512
已換 Q8 draft(上次就換了)
2026-06-12 Fix 3:fattn.cu:312 crash root cause + fix(已 rebuild)
Root cause
do_ar_decode 在 temperature>0(needs_logit_processing()=true)時被呼叫
do_ar_decode hardcode with_mask=false、n_tokens=1
對 tq3_0 KV:win_len_padded = round_up(win_len, 256) → always divisible by 256
can_use_vector_kernel = K->ne[1]%256==0 = true → dispatch VEC kernel
HIP: tq3_0 excluded from VEC (#ifndef GGML_USE_HIP) → GGML_ABORT at line 312
為何 Request 1 不 crash
Request 1 有 tools → temperature=0 → needs_logit_processing()=false → 走 spec-decode
Spec-decode verify/replay n_tokens≥2 → need_mask = (n>1) = true → CHUNKED → OK
為何 Request 2 crash
Request 2 無 tools,temperature>0(預設)→ AR decode
do_ar_decode n_tokens=1, with_mask=false → VEC → CRASH
Fix(已應用到 qwen35_backend.cpp)
init() 第 232 行:detect tq3_0 KV → cfg_.kq_stride_pad = 256
do_ar_decode loop:ar_with_mask = (cfg_.kq_stride_pad > KQ_MASK_PAD) → with_mask=true for tq3_0
do_ar_decode loop:mask 填充 code(build_causal_mask + ggml_backend_tensor_set)
2026-06-12 Tool call 調查與修復(進行中)
問題現象
Tool call 失敗:server log tool_call parse failed; suppressing buffered tool text bytes=11
11 bytes = <tool_call>(token 248058),model 生了 <tool_call> 後立刻 EOS
26 tokens 總輸出,finish=stop,0 個 tool call
發現的根本原因
原因 1:--fa-window 2048 截斷 attention 視窗(最關鍵)
Hermes 請求 prompt_tokens=6025,--fa-window 2048 → decode 時只看最近 2048 tokens
系統提示(含 tool format 指令)在 token 0~4000,完全在 window 外
Model 想調 tool 但看不到格式指令 → 生 <tool_call> 後不知道放什麼 → EOS
Fix: --fa-window 0(無限窗口,完整 attention)
原因 2:tool_memory miss → 歷史 tool call 渲染為空(次要)
Server 重啟後 in-memory tool_memory 清空
normalize_chat_messages 對 role=assistant + tool_calls 的訊息查不到原始文字
RESPONSES format 有 fallback,chat format 沒有 → 歷史 tool call 變成空 content
Model 看到「空的 assistant turn + tool_response」,context 不完整
Fix: http_server.cpp 加 fallback:從 tool_calls JSON 重建 <tool_call> 格式
原因 3(原始):tq3_0 KV + fattn.cu:312 crash(已修復)
temperature>0 時走 AR decode,tq3_0 觸發 VEC kernel,HIP 不支援 → ABORT
Fix: kq_stride_pad=256 + ar_with_mask=true + mask 填充
目前修改清單
檔案
修改內容
src/server/http_server.cpp
chat format tool_memory miss fallback:從 JSON 重建 <tool_call>
dflash_oai_v2.sh
--fa-window 0、換 Huihui 模型、移除 --cache-type-k/v tq3_0、移除 HSA_OVERRIDE_GFX_VERSION
src/qwen35/qwen35_backend.cpp
Fix 3 tq3_0 fattn crash(kq_stride_pad + ar_with_mask)
作者建議參數(已對齊)
模型:Huihui-Qwen3.6-27B-abliterated.Q4_K_M.gguf
draft:dflash-draft-3.6-q8_0.gguf(Q8)
--fa-window 0
--ddtree-budget 8
KV cache:q4_0(default,無 tq3_0 的 HIP 問題)
不設 HSA_OVERRIDE_GFX_VERSION(ROCm 自動識別 gfx1100)
目前狀態
服務重啟中,等待測試 Hermes BSB tool call
2026-06-12 速度分析(第二輪)
測量基準
[spec-decode] tokens=84 time=3.938s speed=21.33 tok/s steps=25 accepted=60/400 (15%) avg_commit=3.36
prefill=12.0s decode=3.9s(21.3tok/s)
目標:45+ tok/s,目前差 2.1x。
模型架構(從 GGUF metadata 讀出)
qwen35.block_count: 64
qwen35.full_attention_interval: 4 ← 每 4 層才有一個全 attention 層
qwen35.embedding_length: 5120
qwen35.ssm.inner_size: 6144
qwen35.ssm.state_size: 128
64 層中:16 層 = 全 attention(KV cache + FlashAttn)
48 層 = SSM(DeltaNet / gated_delta_net)— 無 KV cache,純矩陣遞推
Draft 模型 (dflash-draft-3.6-q8_0.gguf
block_size: 16 — 每步生成 16 個投機 token
block_count: 5 — 5 層 attention-only 模型(很小)
n_target_layers: 5 — 從 target 取 5 層 hidden state 作輸入
瓶頸根本原因:SSM 串行 kernel
spec-decode verify 步驟呼叫 verify_batch()(qwen35_dflash_target.cpp:27),其中:
build_target_step(... capture_delta_intermediate=false ...)
→ parent_ids = nullptr(非 DDTree tree 路徑)
→ cap_ptr = nullptr(無 rollback capture)
這滿足啟用 chunked DeltaNet 的全部條件:
// qwen35_target_graph.cpp line 800
if (!parent_ids && !cap && n_seq_tokens > 1) {
if (const char * s = std::getenv("DFLASH27B_CHUNKED")) use_chunked = true;
}
但預設關閉,原因:
"port produces correct shape but slightly wrong final state, causing AL degradation and loopy output."
為何 DFLASH27B_CHUNKED=1 對我們是安全的
verify 的 state 不影響最終輸出:
snapshot_kv() 在 verify 前保存 SSM state
restore_kv() 在 verify 後立刻恢復(qwen35_dflash_target.cpp:107)
Replay 步驟重新跑接受的 tokens 來建立正確 state
→ chunked verify 的 state 誤差從來不被保留
n_tokens=16 ≤ CS=64 → n_chunks=1 → 無跨 chunk 誤差:
chunked bug 發生在 n_chunks > 1(即 n_tokens > 64)時的跨 chunk state 傳播
我們的 block_size=16 永遠只有 1 個 chunk,算法正確
唯一影響:output logits 精度 → 接受率略降,但預計影響甚微
「loopy output」 可能是在不同測試條件(n_tokens > 64 或沒有 restore)下觀察到的
DRM 電源模式確認
card1(vendor=0x1002:0x744c,25.7 GB)= RX 7900 XTX #1 → power_dpm=high ✓
card2(vendor=0x1002:0x744c,25.7 GB)= RX 7900 XTX #2 → power_dpm=auto(ComfyUI)
HIP_VISIBLE_DEVICES=0 = card1 = DFlash 用的 GPU ✓
ROCm: /opt/rocm → /etc/alternatives/rocm → /opt/rocm-7.2.0 → 一致,無 ABI 問題
Build 狀態確認(作者建議已對比)
項目
狀態
DFLASH27B_HIP_SM80_EQUIV
OFF ✓
DFLASH27B_FA_ALL_QUANTS
ON ✓
ROCm 版本
7.2.0(非 7.2.3 but same ABI)✓
LD_LIBRARY_PATH
/opt/rocm/lib = /opt/rocm-7.2.0/lib 同一個 ✓
速度優化行動項
已套用:DFLASH27B_CHUNKED=1(dflash_oai_v2.sh)
預期效果:SSM 2-3x 加速 → step time 157ms → ~55ms
預期 tok/s:3.36 avg_commit / 0.055s = ~61 tok/s(超過 45 目標)
無需 rebuild,直接重啟生效
待測試:啟動後觀察
[spec-decode] tokens=? time=? speed=? avg_commit=?
預期 speed > 45 tok/s,avg_commit 可能略降但 step_time 大降。
仍未做的優化(備用)
前綴快取 --prefix-cache-slots 4:
快取系統提示 ~3000 tokens → prefill 12s 降到 ~6s
不影響 decode 速度,但降低 Hermes 感受的首 token 延遲
先測 CHUNKED 效果,如果 prefill 仍是瓶頸再考慮
增大 DFLASH27B_DRAFT_CTX_MAX(例如 1024):
draft 看更多 context → 接受率提升
代價:draft 步驟略慢
目前 CHUNKED 應已足夠,此項備用
不要再做的事
不要再調 KV quantization 當速度 fix(已確認無效,SSM 75% 主導)
不要把 max-ctx 縮到 8k/16k
不要動 q4_64k_telegram.sh
不要動 ComfyUI port 8188