十年老服务器跑千问 3.5 4B:被硬件逼出来的调参实录

先说结论:一台十年前的机器,能跑、能对外提供服务,但整个过程不是"装完就完事"------这代硬件(AMD Polaris 显卡 + 没有 AVX2 的 Ivy Bridge CPU)逼出了三个反直觉的决定:关掉 flash attention、接受 CPU 当主力、用固定别名把参数钉死。

环境:Intel Xeon E5-2670 v2 / 32 GB DDR3L / AMD RX 580 2048SP 8 GB / Windows 10 专业版 19045,跑 Ollama 0.40.14 + 千问 3.5 4B(Q4_K_M),给局域网内的 WorkBuddy、Dify 等平台提供 OpenAI 兼容 API。

一、硬件直接决定了方案边界

项目 实际配置 对方案的影响
CPU Xeon E5-2670 v2,10 核 20 线程,Ivy Bridge,仅 AVX 无 AVX2 只能当兜底算力,预填充约 54~74 tok/s
内存 32 GB DDR3L-1600 撑得住 9.6 GB 的模型 + KV 占用
显卡 RX 580 2048SP / 8 GB,Polaris gfx803 ROCm 不支持(只支持 RX 7600 起的 RDNA3),只能走 Vulkan
系统 Windows 10 19045 Ollama 0.40.x 默认启用 Vulkan 后端,Polaris 可用

三条硬边界:

  1. 不能上 ROCm,只能 Vulkan。 Vulkan 在 Polaris 上对"非标准注意力结构"支持不全,这是后面所有取舍的根源。
  2. 8 GB 显存决定模型上限。 4B Q4(3.3 GB)宽裕,8B Q4(约 5 GB)临界,14B 以上必须 CPU 混合、速度崩到 1~2 tok/s。
  3. 模型选型定死。 能用的就是 Qwen3.5 4B(混合架构)。换纯稠密模型(如 qwen3:4b-instruct-2507)虽然更上卡,但实测会随机输出整串 0 的静默缺陷,已放弃并删除。

顺带一提:本机还有个向日葵虚拟显示器 OrayIddDriver Device,没卸载------它不参与 Vulkan 计算,删了没收益,还可能影响远程连接。

二、最终配置长什么样

对外只有一个名字:qwen-api。所有平台都填这个模型名,底层换模型只重建别名,调用方代码不用改。

dockerfile 复制代码
FROM qwen3.5:4b-q4_K_M

PARAMETER num_ctx 16384
PARAMETER temperature 0.7
PARAMETER top_p 0.9
PARAMETER repeat_penalty 1.05
PARAMETER num_predict 2048

SYSTEM "你是运行在这台 Windows 电脑上的本地大模型助手。回答简洁、准确,默认使用中文;不确定时直接说明不确定,不要编造事实。"

用户级环境变量(Ollama 只在启动时读一次,改完必须重启服务):

变量 值 作用
OLLAMA_HOST 0.0.0.0:11434 监听所有网卡,否则局域网其他机器连不上
OLLAMA_KEEP_ALIVE -1 模型永久常驻,不主动释放
OLLAMA_MAX_LOADED_MODELS 1 8 GB 显存只容得下一个模型
OLLAMA_NUM_PARALLEL 2 名义 2 路并发,实测被压到 1 槽
OLLAMA_FLASH_ATTENTION false 关键反向设置
OLLAMA_CONTEXT_LENGTH 16384 兜底值,实际以 Modelfile 的 num_ctx 为准

服务用启动脚本拉起(脚本里先设好变量再启动),避免"改了环境变量却没生效"这个高频坑。没做开机自启,重启后手动跑一次。

三、七次调参,每一次都为什么

1. flash attention:从开到关(本次部署最重要的决定)

默认开启时看着很完美:33/33 层全上卡、100% GPU、占 3.4 GB、生成 8.6 tok/s。

但实测发现长提示会静默输出垃圾(一整串 0),而且稳定复现:

提示 token 输出
1633 正常
1708 垃圾(全 0)
1783 垃圾
1858 垃圾(连测两次均复现)

关掉 FA 后,同样 1858 token 的提示完全正常。根因是 Vulkan 后端在 Polaris 上的 flash attention 在长序列存在数值缺陷,不是模型损坏、也不是参数配错,调参救不回来。

代价照实说:

FA 开 FA 关(当前)
上卡层数 33/33 1/33(16384 上下文时 0/33)
占用 3.4 GB 8.7 → 9.6 GB
生成速度 8.6 tok/s 5.5 tok/s
预填充 55 tok/s 72~74 tok/s(8192 时)
长提示 >1650 token 输出垃圾 正常

取舍逻辑只有一句:可预期的慢,优于不可预期的错。 给程序用的 API,正文突然变成一串 0 且不报错,比慢危险得多。

2. num_ctx:8192 → 16384

最初按"够用就好"定 8192,后来要接 Dify 这类会累积对话历史的多平台,才提到 16384。同样 4098 token 的提示,代价如下:

配置 预填充 每轮固定开销 占用 上卡
8192 5556 s(7274 tok/s) 约 56 s 8.7 GB 1/33 层
16384(当前) 约 76 s(54 tok/s) 约 76 s 9.6 GB 0/33 层

每轮多花约 20 秒,原因是 KV 缓存翻倍 + 显卡装不下层,退化成接近纯 CPU 推理。

但要分清场景:WorkBuddy 每轮提示固定 4098 token 且不带历史,4098 + 2048 = 6146 本来就在 8192 之内。只给它用,8192 更划算;多平台共用才需要 16384。

3. num_predict:1024 → 2048

第一次接 WorkBuddy 就踩到:回答生成到 1024 token 正好撞上限被截断,客户端自动发起第二次请求续写,两段合计 7 分 38 秒;续写时模型还编造了一段"Token 溢出恢复模式"的自述文字。

提到 2048 后该现象消失,代价是长回答最坏情况耗时翻倍。

4. OLLAMA_HOST:127.0.0.1 → 0.0.0.0

默认只监听本机,另一台电脑的 WorkBuddy 根本连不上。改成 0.0.0.0 后监听变为 :::11434,局域网访问 /api/version 实测返回 0.40.14。

安全提醒:这等于同网段任何设备都能免鉴权调用本机显卡。只建议在可信网络开;要收紧就只绑具体网卡 IP 并限定来源 IP。

5. keep_alive=-1 与 max_loaded=1

这台机器就是用来跑模型的,所以让模型永久占住显存(ps 里 UNTIL 显示 Forever),避免每次请求重新加载(首次加载约 20 秒)。同时限死只加载一个模型,防止 8 GB 显存被第二个模型撑爆。

6. NUM_PARALLEL=2:留着但没收益

实测 2 路并发:单路 8.7 s,两路分别 8.3 s / 16.6 s,总墙钟 16.6 s,相对串行 17.4 s 只有 1.05x。单路请求已经吃满显存带宽,加并发数不能提吞吐,只能排队。

更关键的是:上下文提到 16384 后,日志显示 n_slots = 1,Ollama 自己就把并发槽压回 1 了。

7. 别名固化参数

把上下文、采样、输出上限、系统提示全钉进 qwen-api 的 Modelfile,好处是任何客户端接进来都自动继承正确参数,不用每个平台各配一遍。

四、接第三方 Agent 客户端才知道的事

以 WorkBuddy 为例,配置很简单:

Base URL 填 http://本机IP:11434/v1(必须带 /v1),Key 随便非空即可,模型名必须与 ollama list 一致,思考模式不勾选。

接通之后暴露出来的三个事实,比模型本身更值得记:

  1. 每轮提示固定 4098 token,且每轮从头重算。 日志连续四次请求都是 4098,一个 token 不差------因为它不带对话历史,缓存无从命中。这就是"问什么都得先等一分钟"的原因,与问题长短无关。顺便说,这也是它记不住上一轮聊了什么的原因,不是模型健忘。
  2. 关掉"工具调用"并不能缩短提示。 取消勾选后仍是 4098,大头是客户端自己注入的项目记忆与系统提示。
  3. 一轮对话的真实耗时构成:提示 4098 token(预填充 56 s)+ 生成 544 token(97 s)= 服务端 3 分钟,界面显示 3 分 54 秒,多出来的是它自己启动运行环境的时间。

实测截图(一句话解释缓存,生成 544 token、正文完整、无思考残留):

简单算术题也验证过准确性与耗时(2+3*4 返回 14,9 的三次方返回 729):

五、另外两条坑

5.1 思考模式:两个接口两种关法,服务端关不掉

Qwen3.5 默认开思考,对程序调用是灾难------一句话回答烧掉 500+ token,正文可能为空。

接口 关闭写法
原生 /api/chat、/api/generate "think": false
OpenAI 兼容 /v1/chat/completions "reasoning_effort": "none"(think:false 在此无效)

实测(同一问题,上限 150 token):

传参 耗时 token 正文
什么都不传 26.2 s 150(烧满) 空
think: false(走 /v1) 25.9 s 150 空(参数被忽略)
reasoning_effort: "none" 6.2 s 34 65 字正常

服务端试过三种固化方式,全部无效:Modelfile 里 PARAMETER think false(直接报 unknown parameter)、系统提示词强令禁止思考、提示词加 /no_think。结论:思考只能在调用方关。

例外是 WorkBuddy:界面里"思考模式"不勾选时输出是干净正文,说明它内部已经处理了。

5.2 1650 token 的红线

FA 关闭是当前配置,这条红线在 FA 开时才会触发;当前配置下长提示正常。但一旦有人为了提速把 FA 打开,就必须同步约束每次输入不超过 1500 token(约 1000 汉字),否则静默输出垃圾。

六、维护与排查

powershell 复制代码
& "$env:LOCALAPPDATA\Programs\Ollama\ollama.exe" ps
& "$env:LOCALAPPDATA\Programs\Ollama\ollama.exe" list
Invoke-RestMethod -Uri "http://127.0.0.1:11434/api/version"

改参数:改 Modelfile → create qwen-api -f Modelfile → 下次请求自动生效。

看日志定位问题:server.log,重点看 prompt eval time(每轮真实提示长度与速度)、offloaded N/M layers(上卡层数)、flash_attn(FA 状态)。

现象 先查什么
回答为空 思考模式没关(/v1 用 reasoning_effort: none)
输出一整串 0 提示过长且 FA 被打开了(>1650 token)
慢到几分钟 预填充在 CPU 上跑,看 ps 的 PROCESSOR 占比
别的电脑连不上 OLLAMA_HOST 没生效或防火墙拦了
改了环境变量没反应 没用启动脚本,服务继承了旧环境

回滚:停进程 → unins000.exe /VERYSILENT → 删 .ollama 目录 → 清 6 个 OLLAMA_* 变量。

七、这套配置适合谁

适合:单机、少量客户端、能接受"一分钟级"响应、要求结果正确不能出垃圾的场景。

不适合:高并发、亚秒级响应、需要显卡吃满速率的场景。这代硬件(Polaris + Ivy Bridge)的天花板就在这里,软件层面能调的已经调完------再提速只剩换 N 卡走 CUDA,或换 RDNA3 走 ROCm 两条硬件路。

相关推荐
田里的水稻29 分钟前
FA_CNN卷积神经网络二十七---填充与步幅PyTorch
人工智能·pytorch·cnn
CyrusYiVPP31 分钟前
哪个用户会再来买?RFM模型+plotly画出用户分层,数据分析实战第2期
人工智能·python
AI源码39 分钟前
基于深度学习的热带水果检测系统设计与实现
人工智能·深度学习
AGI_Eval41 分钟前
AI 3D生成技术:从“可用”走向“可靠”还有多远?
人工智能·3d
程序员cxuan42 分钟前
LobeHub 是真的夯,简直 Super-Agent 最佳实现。
人工智能·后端·程序员
QXWZ_IA1 小时前
高楼遮挡RTK失锁:三层安全网怎么搭
人工智能·安全
sun861220771 小时前
Hy4 Preview 高效预览与协作应用指南
人工智能
码云之上1 小时前
10 颗一模一样的「Edit」:中后台 Web Agent 怎么找到对的那一行
前端·人工智能·浏览器
阿里云大数据AI技术1 小时前
EMR Serverless Ray:让 AI 推理融入数据处理
人工智能·spark