一张游戏卡跑出60t/s:我用Qwen3.6-27B搭了个Coding Agent

一张游戏卡跑出60t/s:我用Qwen3.6-27B搭了个Coding Agent

你手头只有一张RTX 3090(24GB),目标是在本地跑一个能写代码、能看图、能调工具的27B模型。官网说支持128K上下文,但你一开128K就崩,多轮对话从60t/s断崖式掉到10t/s,连打字都卡。这不是模型的问题,是部署调优的路径没走对。这篇文章记录了我在单张3090上驯服Qwen3.6-27B的完整过程——从踩坑到甜点配置,数据说话,代码可复制。

场景原型:我需要一个本地Coding Agent

某个产品经理丢来一个需求:“能不能让AI在本地帮我们写Python脚本、改代码,还能截图给我看?” 核心要求:
– 模型参数量27B(够用且能塞进24GB显存)
– 支持function calling(调工具、搜文档)
– 支持视觉多模态(截图识别 + 代码理解)
– 多轮对话不能掉速(一个任务经常要改20个来回)
当时摆在面前有两个推理引擎:SGLangllama.cpp。SGLang对MoE模型优化极好(快5-7×),但冷启动要200秒,单卡跑27B的Dense模型性能反而不如llama.cpp。而llama.cpp冷启动只要4.5秒,显存控制更精细。最终选了 llama.cpp server-cuda
模型选Qwen3.6-27B,原因是它采用 Gated DeltaNet + Attention混合架构:65层中只有16层是传统Attention(带KV cache),剩下49层是无需KV cache的Gated DeltaNet。这意味着:
– KV cache只有传统模型的 1/4
– 同样显存能塞更长上下文
– 原生支持MTP(Multi-Token Prediction)投机解码


第一轮试水:开箱即崩

我直接按官方示例,配了128K上下文,加上 GGML_CUDA_GRAPH_OPT=1 想加速,结果:
– 首次请求确实快,接近 59 t/s
– 多轮对话到第3轮,速度暴跌到 10-13 t/s
– 再往后,每轮都有2-3秒延迟,明显卡顿
查日志发现:每次KV cache长度变化,都会触发CUDA Graph重录,而那49层Gated DeltaNet的图编译本身就慢。这是最大的坑——关掉 GGML_CUDA_GRAPH_OPT 后,多轮对话速度立刻稳定在 59-65 t/s


关键调优的三大发现

1. 对称量化KV cache比不对称快17%

对比测试:

KV Cache 配置 速度 t/s 质量
f16 / f16 64.2 最佳
q8_0 / q8_0(对称) 64.5 接近f16
q8_0 / q4_0(不对称) 55.1 略差

对称量化可以让GPU一次性加载K和V的8-bit块,省去类型转换。所以最终选择 --cache-type-k q8_0 --cache-type-v q8_0

2. MTP投机解码的正确甜点

MTP (Multi-Token Prediction) 让模型一次预测多个token,然后由draft模型验证。测试结果:

MTP n-max 速度 t/s 相比基线提升
0(关) 40.7
1 55.2 +36%
**2** **63.2** **+55%**
3 60.1 +48%(收益递减)

--spec-draft-n-max 2,draft模型的KV cache用q4_0量化(省显存)。

3. 为什么放弃128K,选64K?

原始目标128K,但实测问题:
– 显存不够:24GB显存 + 系统swap,128K下生成时速度降为 6.5 t/s(32K时还有17 t/s)
– 虚拟内存瓶颈:超过32K后,每token处理时间从14ms涨到154ms
– 实际coding agent场景:30轮对话才到50K左右,64K完全够用
换成64K后,显存剩余约4GB,多轮对话全程稳定59-65 t/s。


完整配置:一张3090上的甜点

以下Docker Compose配置已稳定运行数周,用于搭建Coding Agent后端:

services:
  lla-qwen27b:
    image: ghcr.io/ggml-org/llama.cpp:server-cuda
    container_name: lla-qwen27B-gpu0
    restart: unless-stopped
    runtime: nvidia
    ports:
      - "11434:8080"
    volumes:
      - ./models:/models:ro
    environment:
      - CUDA_VISIBLE_DEVICES=0
    command: >
      -m /models/Qwen3.6-27B-UD-Q4_K_XL.gguf
      --mmproj /models/mmproj-Qwen3.6-27B-f16.gguf
      --host 0.0.0.0 --port 8080
      --ctx-size 65536
      --cache-type-k q8_0 --cache-type-v q8_0
      --flash-attn on
      --image-min-tokens 1024
      --parallel 2
      --n-predict 16384
      -ngl 99 -t 8 -tb 4 -b 2048 -ub 1024
      --no-mmap --no-mmproj-offload
      --spec-type draft-mtp --spec-draft-n-max 2
      --spec-draft-type-k q4_0 --spec-draft-type-v q4_0

显存占用稳定在 20.5 GB(83%),剩余约4GB做buffer。

实际效果:长上下文下的速度衰减

虽然64K比128K好很多,但Attention的O(n)本质无法改变。速度随上下文增长而下降:

上下文长度 纯Decode速度 每 token 处理时间
100 ~60 t/s ~14ms
1K ~45 t/s ~22ms
8K ~17 t/s ~59ms
32K ~6.5 t/s ~154ms

对Coding Agent的影响:新任务(2-3K)秒回;修改几轮后(15-20K)预填几秒,生成仍快;深入20+轮(35-50K)预填需要20-30秒,可以接受;如果到64K满负荷,建议开新task清理历史。

关于工具调用和多模态

llama.cpp的OpenAI兼容API原生支持function calling,无需额外参数。一个典型请求:

curl http://localhost:11434/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Qwen3.6-27B",
    "messages": [{"role":"user","content":"搜索人工智能最新进展"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "search_web",
        "description": "搜索互联网",
        "parameters": {
          "type": "object",
          "properties": {"query": {"type": "string"}},
          "required": ["query"]
        }
      }
    }],
    "tool_choice": "auto",
    "max_tokens": 200
  }'

返回的 tool_calls 可直接传给工具函数,结果再喂回模型,完整支持多轮工具调用。
视觉多模态通过 --mmproj 加载投影权重,API使用标准的 image_url 格式,每张图片分配1024个min token。


最后的三条铁律

  1. 不要开 CUDA Graph(GGML_CUDA_GRAPH_OPT=1)——混合架构下KV cache变化频繁,重录成本太高,多轮对话直接掉到10t/s。
  2. 客户端必须限制 max_tokens——服务端的--n-predict不是硬限制,某些客户端如DeerFlow如果不设上限,模型会无限生成,导致单请求卡死整个槽位。
  3. 64K是单卡甜点——128K在24GB上会触发虚拟内存瓶颈,速度降到6t/s以下。如果非得跑长上下文,建议用35B MoE + SGLang(但需要两张卡)。
    一张游戏卡跑出60t/s性能、多轮对话不掉速、支持工具调用和多模态——Qwen3.6-27B + llama.cpp的组合让我彻底告别了云端API。你手头的单卡部署,踩过什么别致的坑?欢迎来交流。

皖ICP备2025105865号-2|皖公网安备34010402704739号