起因
2026-05-26 更新: 现在已经完全切到 llama.cpp,Ollama 已删除
下面这篇是当初折腾时写的记录,结论已过时——最终本地赢了
之前一直用 Ollama 跑 qwen3-vl 模型做视觉识别,AI 助手看图、截图描述都靠它
表现中规中矩,但有两个点让我动了「要不本地跑试试」的念头
一是 Ollama 的 API 有一定 overhead,尤其在做连续的视觉对话时,感觉推理之间多了些不透明的环节
二是手上有张 3060 12GB,平时 ComfyUI 生图的时候显存吃满,但空闲时五六 GB 就在那闲着,想利用起来
于是决定:自己搭一个纯 llama.cpp 的后端,看看能不能比 Ollama 更快、更可控
第一步:编译 llama.cpp
要做对得起显卡的推理,得先编译带 CUDA 支持的 llama.cpp
git clone https://github.com/ggml-ai/llama.cppcd llama.cppcmake -B build -DGGML_CUDA=ONcmake --build build --config Release -j$(nproc)过程很顺利,Ryzen 9700X 编译这东西就是洒洒水,几分钟搞定
选模型:第一次踩坑
模型选的是 haervwe/qwen3-vl-4b-heretic——一个做了一次「删除」的版本,去掉了一些限制,能更自由地描述图片内容
下载下来发现一个奇怪的事情:llama.cpp 加载 GGUF 时提示 unknown architecture,完全跑不了
细查发现,这个模型的 GGUF 包含了 Ollama 私有的 blob 格式数据,在 GGUF 文件里嵌入了一段额外的张量,只有 Ollama 自己能解析
这不是标准的 GGUF,而是一种被称作「Ollama GGUF」的特殊变体
你下载的 GGUF 文件里可能藏了一段 .ollama_blob,llama.cpp 看到直接 say no
那行,换一个
改下 Huihui/Qwen3-VL-4B-Instruct-abliterated-GGUF,同样是做了限制移除的版本(abliterated 就是「删除」的意思),而且走的是纯标准 GGUF,llama.cpp、llama-server 随便加载:
wget -c https://huggingface.co/noctrex/Huihui-Qwen3-VL-4B-Instruct-abliterated-GGUF/resolve/main/ggml-model-Q4_K_M.ggufwget -c https://huggingface.co/noctrex/Huihui-Qwen3-VL-4B-Instruct-abliterated-GGUF/resolve/main/mmproj-model-F16.gguf2.4GB 的模型 + 800MB 的视觉投影器,一共 3.2GB,3060 表示没压力
跑起来
llama-server 启动参数:
./llama-server \ --model Huihui-Qwen3-VL-4B-Instruct-abliterated-Q4_K_M.gguf \ --mmproj mmproj-model-F16.gguf \ --host 0.0.0.0 --port 8083 \ --n-gpu-layers 99 \ --parallel 1 \ --ctx-size 8192 \ --cache-type-k q8_0 --cache-type-v q8_0首测结果不错:
- prompt 处理:~2.7s
- 生成速度:~85 tok/s
- 显存占用:~4.8 GB(含基础系统占用约 1GB)
Q4_K_M 量化对 4B 级别模型来说几乎不影响质量,4.8GB 显存也让 3060 还剩一大截给其他服务
让它更优雅:on-demand relay
但问题是,我不想让模型常驻显存占着位置——ComfyUI 有时候需要那几 GB
于是写了个 vl-relay.py 守护进程,它做的事很简单:
- 启动时模型不加载,零显存占用
- 收到第一条视觉请求时,自动拉起 llama-server
- 连续 5 分钟没有请求,自动 kill 进程、释放显存
- 对外暴露一个 OpenAI 兼容 API,AI 助手不需要改任何配置就能直接用
整个流程对上层完全透明,API 调法和 Ollama 一模一样
# 核心逻辑示意class VLCoordinator: def handle_request(self, prompt, image): self.start_server_if_needed() self.last_active = time.time() result = self.forward_to_llama(prompt, image) return result
def check_idle(self): while True: if time.time() - self.last_active > 300: # 5分钟 self.stop_server() break这套机制开源了:
和 Ollama 正面刚
TIP测试变量多,仅供参考
既然本地搭好了,那和 Ollama 比一下,用同一张截图做视觉描述
测试 1:优化版本地(q8_0 KV, 8K ctx)
| 项目 | 本地(优化) | Ollama |
|---|---|---|
| 总耗时 | 7.0s | 10.0s |
| 显存 | ~5.8 GB | ~4 GB |
| 描述质量 | 可以,偏列表化 | 自然语言式描述 |
本地快了 30%,但是描述风格偏结构化,不如 Ollama 流畅
测试 2:全尺寸本地(f16 KV, 32K ctx)
调大 ctx-size、恢复 f16 KV 缓存
| 项目 | 本地(全尺寸) |
|---|---|
| 总耗时 | 6.6s |
| 显存 | ~6.3 GB |
| 描述质量 | 和 Ollama 基本一致 |
快了更多,描述质量也追上了,但显存飙到了 6.3GB
再往上调到 262K ctx + f16 KV 直接 OOM——12GB 的 3060 就这么多,想上大上下文只能认命
好消息是,8K ctx + q8_0 KV 带来的质量损失极小——看图根本不需要大上下文,q8_0 KV 对视觉 token 也没有可见劣化
优化版 5.8GB 和全尺寸 6.3GB 的描述结果放在一起,人眼分不出区别
最后选了谁
当初写这篇的时候结论是「还是继续用 Ollama」
但现在回头看,最终还是彻底回到了 llama.cpp,Ollama 已经删了
理由说起来也简单:
- Ollama 的 overhead 虽然不大但也不是零 — 纯 llama.cpp 的
--sleep-idle-seconds机制比 relay 优雅得多,自动休眠自动唤醒,不需要额外写守护进程 - 模型管理 — 自己管 GGUF 文件反而比被 Ollama 的模型目录管着更灵活,想换就去 hf-mirror 下个 GGUF,不需要走 Ollama 的 pull
- 去审查版模型 — Ollama 官方没有 abliterated 版本,想用只能自己下 GGUF 从自定义路径加载,那还不如直接上 llama.cpp
所以绕了一圈,跟上一篇文章说的一样——返璞归真了
不过这次折腾也不是白费,折腾过程中搭出来的 on-demand relay 机制 后来启发了我用 --sleep-idle-seconds 代替常驻,反而让最终方案更简洁
环境信息
系统: Arch Linux内核: 7.0.3-zenCPU: AMD Ryzen 7 9700X 8-CoreGPU: NVIDIA RTX 3060 12GB (驱动 595.71, CUDA 13.2)|模型: Huihui-Qwen3-VL-4B-Instruct-abliterated (Q4_K_M)|llama.cpp: 编译版(已不依赖 Ollama)开源仓库:如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
