1. 8G 显存这道坎,到底卡在哪儿
先把结论摆在前面:8G 显存跑本地代码生成模型,能跑,但能跑和好用之间隔着一条很深的沟。我前后折腾了差不多两个月,从最初兴冲冲下载模型、结果第一次推理就爆显存,到后来能稳定在 7B 量化模型上做日常代码补全和函数生成,中间翻的车足够写一本小册子。这篇就把整个链路摊开讲,包括硬件边界怎么算、模型怎么选、Ollama 怎么配、function calling 怎么接、以及那些文档里不会写的坑。
先说清楚适用人群。如果你手上是一张 8G 显存的卡(不管是 3060、4060 还是笔记本上的移动版),想在本机跑一个能帮你写代码、补函数、做简单重构的大模型,那这篇就是给你写的。如果你有 24G 以上的卡,很多限制对你不存在,但里面关于量化选型、上下文长度控制、显存占用的计算方法依然有参考价值。如果你完全没接触过本地部署,也没关系,我会把每一步的理由讲透,你照着做就行。
核心矛盾其实就一句话:模型参数、量化精度、上下文长度、并发数,这四个变量共同决定显存占用,而 8G 是个非常紧的预算。很多人翻车不是因为不会装,而是因为一开始就选错了模型规格,或者没意识到上下文长度也是吃显存的大头。我见过太多人拿着 8G 卡去拉一个 14B 的模型,然后抱怨"跑不起来是不是显卡坏了"。不是坏了,是数学上就不够。
这里先给一个粗略但实用的估算公式,后面会反复用到:
显存占用 ≈ 参数量(B) × 每参数字节数 + KV Cache + 框架开销
其中每参数字节数取决于量化精度:FP16 是 2 字节,INT8 是 1 字节,INT4 大约 0.5 到 0.7 字节。KV Cache 则和上下文长度、层数、隐藏维度强相关,长上下文时它甚至能超过模型权重本身。框架开销(CUDA context、推理引擎本身)通常要预留 0.5 到 1G。
按这个公式,一个 7B 的 INT4 模型,权重约 3.5 到 4G,加上 KV Cache 和开销,8G 卡在 4K 上下文下勉强够用,8K 就开始紧张,16K 基本要爆。这就是为什么"能跑"和"好用"差距巨大——你总不能用 2K 上下文去写代码吧,一个文件都塞不进去。
2. 选模型不是看排行榜,是看你的显存账本
2.1 参数量与量化等级的取舍逻辑
新手最容易犯的错,是直接去模型排行榜上挑第一名,然后发现根本跑不动。排行榜上的模型大多以 FP16 或 BF16 评测,那是给 A100 集群准备的。8G 卡的正确姿势是:先定参数量上限,再在量化等级上做文章。
我的实测经验是,8G 显存下,7B 模型用 Q4_K_M 量化是甜点区,Q5_K_M 会明显吃紧,Q8 基本别想。3B 到 4B 的模型可以用 Q5 甚至 Q6,速度更快,但代码能力会打折扣。1.5B 级别的模型虽然能跑 Q8,但写出来的代码经常有低级语法错误,只能做最简单的补全。
这里有个反直觉的点:量化不是越低越好,也不是越高越好,而是要看任务。做代码生成时,Q4 和 Q5 的差距在简单补全上几乎看不出来,但在需要多步推理的复杂函数生成上,Q5 的准确率明显更高。我做过一组对比,同一个 prompt 让模型生成一个带错误处理的文件读取函数,Q4 版本有 30% 概率漏掉异常分支,Q5 版本只有 10% 左右。所以如果你的任务偏复杂,宁可换小一点的模型跑 Q5,也别硬上大模型跑 Q4。
| 模型规模 | 推荐量化 | 权重占用 | 4K上下文总占用 | 适用场景 |
|---|---|---|---|---|
| 1.5B | Q8_0 | ~1.6G | ~2.5G | 简单补全、注释生成 |
| 3B | Q5_K_M | ~2.2G | ~3.5G | 单函数生成、小重构 |
| 7B | Q4_K_M | ~4.0G | ~6.0G | 多函数生成、代码解释 |
| 7B | Q5_K_M | ~4.8G | ~7.0G | 复杂逻辑、边界紧张 |
| 13B | Q4_K_M | ~7.5G | 超预算 | 8G 卡不建议 |
这张表是我自己反复测出来的,不是理论值。注意最后一列,13B 的 Q4 权重就 7.5G 了,加上 KV Cache 必爆,所以 8G 卡的上限就是 7B,没有例外。
2.2 代码专用模型和通用模型的差异
另一个关键选择是:用代码专用模型还是通用模型。代码专用模型(比如各种 Coder 系列)在训练时喂了大量代码语料,对缩进、括号匹配、常见库的 API 记忆更准。通用模型则在理解自然语言需求上更强,适合你把需求描述得比较口语化的时候。
我的建议是两个都留一份。日常补全和写样板代码用代码专用模型,速度快、格式准;需要理解复杂需求、做架构层面的建议时切通用模型。Ollama 的好处就是可以同时拉多个模型,用的时候ollama run指定名字就行,切换成本很低。但要注意,同时加载两个模型会双倍吃显存,所以别指望它们同时常驻,用完一个记得让它卸载(后面会讲怎么控制卸载时间)。
2.3 上下文长度:被严重低估的显存杀手
前面反复提到 KV Cache,这里展开讲。KV Cache 是推理时为了加速而缓存的历史键值对,它的占用和上下文长度成正比。很多人只盯着模型权重,结果模型能加载,一推理就 OOM,问题就出在这。
以 7B 模型为例,在 4K 上下文下,KV Cache 大约占 1G 左右;到 8K 就接近 2G;16K 能到 4G。这意味着你即使权重只占 4G,加上 16K 上下文的 KV Cache 和框架开销,8G 卡也会爆。所以控制上下文长度是 8G 卡能不能用得舒服的关键。
实操上,Ollama 可以通过参数限制上下文。默认它可能会尝试用模型支持的最大上下文,这对 8G 卡是灾难。你需要在 Modelfile 里或者运行时指定num_ctx,我一般设 4096,需要处理大文件时临时调到 8192,但会盯着显存。这个参数后面配置章节会详细讲。
3. Ollama 在 8G 卡上的配置细节
3.1 安装与模型存储路径的坑
Ollama 的安装本身不复杂,官网下载对应平台的包,一路下一步就行。但有两个坑必须提前说。
第一个是模型存储路径。默认情况下,模型会存在系统盘的用户目录下,一个 7B 模型动辄 4 到 5G,几个模型下来系统盘就红了。而且系统盘如果是机械硬盘或者空间紧张的 SSD,加载速度会很慢。解决办法是设置环境变量OLLAMA_MODELS指向一个大容量盘。Windows 下在系统环境变量里加,Linux 下在启动脚本里 export。改完之后要把原来下载的模型手动迁移过去,或者干脆删了重新拉。
第二个是下载速度。直连拉模型经常慢到怀疑人生,一个 4G 的模型下半小时。解决办法是配置镜像源,把OLLAMA_HOST或者相关的 registry 地址指向国内可访问的镜像。具体地址会变,我这里不写死,你搜一下当前可用的镜像就行。配好之后下载速度能从几百 K 提到几 M,体验完全不同。
注意:改存储路径一定要在拉模型之前做,否则你得手动搬文件,还得改配置里的路径引用,很麻烦。
3.2 关键参数:num_ctx、num_gpu、num_thread
Ollama 的模型行为由 Modelfile 控制,你也可以在运行时用参数覆盖。对 8G 卡来说,三个参数最关键。
num_ctx就是上下文长度,前面讲过,我建议默认 4096。写代码时如果一个文件特别大,可以临时用ollama run 模型名 --parameter num_ctx 8192调大,但要有爆显存的心理准备。
num_gpu控制有多少层放到 GPU 上。8G 卡跑 7B Q4 时,理论上可以全部放 GPU,但如果同时开了别的吃显存的程序(浏览器、IDE 的 GPU 加速),可能就需要留几层给 CPU。这个参数调起来有点玄学,我的经验是先全放 GPU,如果 OOM 就减 2 到 4 层,直到稳定。放 CPU 的层会拖慢速度,但总比跑不起来强。
num_thread是 CPU 线程数,只在你有一部分层跑在 CPU 上时才重要。一般设成物理核心数就行,别设成超线程数,否则会互相抢资源。
这里给一个我常用的 Modelfile 片段:
FROM qwen2.5-coder:7b-q4_K_M PARAMETER num_ctx 4096 PARAMETER num_gpu 99 PARAMETER temperature 0.2 PARAMETER top_p 0.9num_gpu 99是个惯用法,意思是"尽可能多放 GPU",Ollama 会自动截断到实际层数。temperature 0.2是代码生成的关键,代码要的是确定性,不是创意,温度高了会给你写出各种奇怪的变体。top_p 0.9配合低温度,保证输出稳定。
3.3 显存占用的实时监控方法
配置调完之后,你得知道实际占了多少。Windows 下用任务管理器的性能标签看专用 GPU 内存,Linux 下用nvidia-smi。但这两个都只能看总量,看不到 Ollama 具体占了多少。
更细的办法是用nvidia-smi的循环模式:nvidia-smi -l 1,每秒刷新一次,你能看到模型加载瞬间的显存跳变。加载完成后显存会稳定在一个值,推理时会有小幅波动。如果推理过程中显存持续上涨然后 OOM,那基本就是上下文太长导致 KV Cache 爆了。
还有一个技巧:Ollama 有个OLLAMA_KEEP_ALIVE环境变量,控制模型在最后一次调用后保持加载多久。默认是 5 分钟,意味着你用完 5 分钟内它还占着显存。如果你要跑别的吃显存的任务,可以设成 0 让它立即卸载,或者设长一点避免反复加载。我一般设 10 分钟,因为加载一次模型要好几秒,频繁卸载重载很烦。
4. 从翻车到跑通:我的完整排查链路
4.1 第一次翻车:模型加载成功但推理即崩
最开始我拉了一个 13B 的模型,ollama run之后显示加载成功,我输入一个简单的 prompt,结果直接报 500 错误,日志里写着 llama-server process 异常退出。当时我以为是模型文件损坏,重新拉了一遍,还是一样。
排查过程是这样的:先看nvidia-smi,发现加载完成后显存已经占到 7.8G,推理一开始就冲到 8G 以上然后进程被杀。这就明确了,不是模型坏了,是显存不够。13B 的 Q4 权重就 7.5G,加上 KV Cache 必爆。换成 7B 之后问题消失。
这个坑的教训是:加载成功不等于能推理。加载只把权重放进显存,推理时还要分配 KV Cache 和计算缓冲区。所以选模型时要按"权重 + KV Cache + 开销"来算,不能只看权重。
4.2 第二次翻车:上下文一长就 OOM
换成 7B 之后,简单 prompt 没问题了。但我试着让它读一个 300 行的代码文件做重构,又崩了。这次日志显示是 KV Cache 分配失败。
原因很清楚:默认上下文可能被设成了模型支持的最大值(有些模型支持 32K),300 行代码加上 prompt 轻松超过 8K,KV Cache 直接吃掉 2G 以上,加上权重 4G 和开销,超了。解决办法就是前面说的,把num_ctx显式设成 4096,需要更长时手动调,并且分段处理大文件,而不是一次性塞进去。
这里有个实用技巧:处理大文件时,不要整个文件塞进去,而是按函数或按块切分。让模型一次处理一个函数,上下文压力小,输出质量也更高。我后来写了个小脚本,用正则把代码按函数边界切开,逐个送给模型,效果比一次性塞进去好得多。
4.3 第三次翻车:function calling 格式对不上
跑通基本生成之后,我想做 function calling,让模型输出结构化的函数调用而不是纯文本。结果模型输出的 JSON 经常格式错误,要么多一个逗号,要么少一个括号,解析直接失败。
这个问题有两层原因。一是模型本身对 JSON 格式的遵循能力有限,尤其是小模型;二是 prompt 里没有给足格式约束。解决办法是:在 system prompt 里明确给出 JSON schema,并且用 few-shot 示例告诉它期望的输出格式。另外,Ollama 较新版本支持format: json参数,能强制模型输出合法 JSON,这个一定要开。
开了format: json之后,格式错误率从 30% 降到 5% 以下。剩下的 5% 主要是模型在复杂逻辑下仍然会跑偏,这个只能靠后处理兜底——解析失败时重试一次,或者降级到纯文本解析。
4.4 第四次翻车:混合显卡环境下的识别问题
我有一台机器是核显加独显的混合配置,Ollama 有时候会认错卡,把计算分配到核显上,速度慢到无法忍受。排查方法是看 Ollama 启动日志,它会打印检测到的 GPU。如果认错了,可以用CUDA_VISIBLE_DEVICES环境变量强制指定用哪张卡。
这个坑在笔记本上特别常见,因为很多笔记本的独显默认是省电模式,Ollama 启动时可能检测不到。解决办法是在显卡控制面板里把 Ollama 设成"高性能"模式,或者在 BIOS 里关掉核显(如果不需要外接显示器的话)。
5. function calling 与代码生成的工程化落地
5.1 为什么代码生成需要 function calling
纯文本生成对写代码来说够用,但要做工程化集成就不够了。比如你想让模型帮你查一个 API 的用法,它需要调用搜索工具;想让它验证生成的代码能不能跑,它需要调用执行工具。这些都需要 function calling——模型输出结构化的调用请求,你的程序解析后执行,再把结果喂回去。
对 8G 卡来说,function calling 的挑战在于:它需要更长的上下文(因为要带工具定义和历史调用记录),而上下文正是我们的瓶颈。所以工具定义要精简,别把一堆用不到的工具都塞进去。我的做法是按需加载工具,当前任务需要哪些工具就只传哪些,能省不少 token。
5.2 工具定义的写法与常见错误
工具定义一般用 JSON schema 描述,包括工具名、描述、参数列表。这里最容易犯的错是描述写得太模糊,模型不知道该什么时候调用。比如一个"读文件"工具,描述只写"读取文件",模型可能在你只是想让它解释代码时也去调它。正确的写法是把使用场景写清楚:"当需要获取本地文件内容时调用,参数为文件绝对路径"。
另一个错误是参数类型不匹配。模型有时候会把数字参数输出成字符串,或者把数组输出成单个值。解决办法是在 schema 里把类型写死,并且在 prompt 里强调类型要求。开了format: json之后这个问题会好很多,但复杂嵌套结构仍然可能出错。
5.3 多轮调用的上下文管理
function calling 往往是多轮的:模型调用工具,拿到结果,再决定下一步。每一轮都会增加上下文长度,几轮下来就逼近上限了。管理方法是及时裁剪历史,只保留最近几轮的工具调用记录,更早的用摘要代替。
我一般保留最近 3 轮完整记录,更早的压缩成一句话摘要。这样既能保持上下文连贯,又不会无限增长。另外,工具返回的结果如果很长(比如读了一个大文件),也要截断,只保留关键部分。
6. 让 8G 卡跑得更舒服的几个实战技巧
6.1 模型常驻与按需加载的平衡
前面提过OLLAMA_KEEP_ALIVE,这里展开讲策略。如果你一天到晚都在用,设长一点(比如 30 分钟)避免反复加载;如果只是偶尔用,设短一点释放显存给别的程序。我的习惯是工作日设 30 分钟,因为随时可能用;周末设 5 分钟,因为用得少。
还有一个技巧是用小的模型做常驻,大的模型按需加载。比如常驻一个 1.5B 的模型做简单补全,需要复杂生成时再加载 7B。这样日常占用低,需要时也能顶上。
6.2 提示词工程对显存的间接影响
提示词写得好,能减少来回次数,间接省显存。比如把需求一次说清楚,而不是来回追问;把格式要求写在 system prompt 里,而不是每次都在 user prompt 里重复。这些都能减少上下文增长。
另外,别让模型输出废话。有些模型喜欢在代码前后加一堆解释,这些解释也占上下文。在 prompt 里明确要求"只输出代码,不要解释",能省不少 token。我一般会在 system prompt 里写:"你是一个代码生成助手,只输出代码,不输出任何解释性文字。"
6.3 什么时候该放弃本地,转向别的方案
说实话,8G 卡跑本地代码生成,适合的是隐私敏感、离线环境、或者就是想折腾的场景。如果你只是想要一个顺手的代码助手,云端方案在效果和速度上都更好。本地方案的价值在于数据不出本机,以及没有网络依赖。
我的建议是混合使用:日常简单补全用本地小模型,复杂任务用云端。这样既保护了敏感代码,又能在需要时获得更强的能力。本地部署不是要取代一切,而是在特定场景下提供不可替代的价值。
6.4 长期运行的稳定性维护
本地模型跑久了会遇到一些稳定性问题,比如显存碎片化导致原本能跑的配置突然 OOM。解决办法是定期重启 Ollama 服务,释放碎片。我一般每天重启一次,或者发现异常时重启。
另外,模型文件偶尔会损坏(尤其是下载中断过),表现为加载时报奇怪的错误。这时候删掉重新拉就行。建议保留一份模型清单,记录每个模型的来源和版本,出问题时好排查。
7. 一些踩坑之后的个人体会
折腾这么久,最大的体会是:8G 卡跑本地大模型,核心不是技术多难,而是预期管理。你得接受它跑不了最大的模型,接受它上下文有限,接受它偶尔会崩。在这个前提下,它依然能提供实实在在的价值——我日常的样板代码、单元测试、正则表达式,基本都靠它生成,省了大量查文档和敲键盘的时间。
另一个体会是,参数调优比换硬件更有效。同样的 8G 卡,配置调好了和没调好,体验差距巨大。花时间理解 num_ctx、num_gpu、量化等级这些参数,比急着换卡划算得多。
最后分享一个小技巧:把常用的 prompt 模板存成文件,用的时候直接读进来。这样既保证了一致性,又省得每次重新组织语言。我存了十几个模板,覆盖代码生成、重构、解释、测试等场景,用起来很顺手。这个习惯是从反复调试中养成的,一开始我每次都手写 prompt,后来发现同样的需求反复写很浪费,就固化下来了。