
前几天一个朋友看到我的nvidia-smi截图盯着显存那栏问了一句你 Agent 用的模型文件都 5.9GB 了怎么实际只占 2.7GB 显存是不是显示 bug我把 llama-server 的启动参数和运行时日志翻出来逐项对账确认这数字是真的。如果你也在自养 Agent并且打算在 6GB/8GB 的小显存显卡上跑本地模型这篇日志应该能帮你把模型到底吃了多少显存这笔账彻底算清楚。先说结论2.7GB 是量化后的权重、KV cache 和框架运行开销三者相加的结果5.9GB 是模型原始 FP16 权重的体积。两者之间的差额来自你愿意为推理质量和显存占用做的每一笔交换。下面我把每笔交换、每个参数、每段实测都展开写方便你直接照抄。1. 先对账5.9GB 与 2.7GB 之间的几笔去向1.1 5.9GB 是仓库里的原始体重不是上桌后的食量很多人拿到一个模型第一反应是看下载文件多大然后用这个数字去估算显存。这个习惯本身没太大问题但它忽略了一个关键前提模型文件的体积取决于保存精度推理时的显存占用取决于加载精度和同时存在的缓存数据。两者并不是同一个量。我用的这个模型参数规模在 3B 量级HuggingFace 仓库里的原始权重是 FP16也就是每个参数用 2 字节存储。3B 参数乘以 2 字节算下来大概就是 5.9GB。这是仓库里的原始体重。但真正部署到本地推理时我不会直接加载这份 FP16 原始权重而是先把它转成 GGUF 格式再做一次 4bit 量化。量化后每个参数只需要约 0.6 字节。同样的模型权重体积从 5.9GB 降到 1.8GB 左右。这才是推理时真正常驻显存的大头。所以第一笔账很简单5.9GB 和 2.7GB 之间最粗的一条水分来自量化。如果你不量化、直接硬加载 FP165.9GB 权重加其他开销8GB 卡当场就会吃力6GB 卡基本没戏。我选择量化不是因为显存不够才妥协而是Agent 这种场景本来就适合用较低精度换取更高吞吐。1.2 显存账本的四条收银项显存账本不是只有权重一项。实际跑起来GPU 显存里至少躺着四笔开销费用项体积区间说明量化后权重约 1.8GB模型参数的常驻部分量化精度决定KV Cache0.3GB ~ 1GB随上下文长度线性增长可进一步量化压缩CUDA context 与框架开销0.3GB ~ 0.6GB只要启动 GPU 必然占用基本省不掉临时激活值与显存碎片0.1GB ~ 0.3GB推理过程中的中间张量波动范围不大2.7GB 这个数字就是上面四笔相加的最终结果。单独看每一笔都不大合在一起就构成一个非常轻量的推理 footprint。1.3 这 2.7GB 对自养 Agent 意味着什么自养 Agent 和单纯跑一个聊天模型不一样。我的 Agent 除了推理模型还要同时常驻 embedding 模型做向量化、跑一个轻量级向量数据库、再加上 Agent 框架自身的内存和显存开销。如果推理模型一口气吃掉 5.9GB那就没有余量给其他组件了。把推理模型的显存压到 2.7GB 之后我的 8GB 显卡还有大约 5GB 余量足够再放一个 0.5GB 的 embedding 模型、跑一个几百 MB 的向量库进程还能给图形桌面留出呼吸空间。这是自养和租别人 API最大的区别你真正拥有这块卡的全部预算怎么分配完全由自己说了算。2. 权重量化整笔账里最肥的一刀2.1 16bit 到 4.85bit 的压缩逻辑先说一个直观类比FP16 相当于每个参数用 2 个字节记账Q4_K_M 量化相当于只用 4.85 个比特记账。参数本身的数值含义大体保留但精度细节被舍弃了一部分。换算下来量化后的体积约是 FP16 的 30%。3B 参数的模型FP16 是 5.9GBQ4_K_M 量化后大概 1.82GB省下整整 4GB。这是整份显存账里最肥的一刀也是 2.7GB 这个数字能成立的第一前提。具体操作上我不直接加载 FP16 再在运行时量化而是提前用工具转成 GGUF 文件。这样启动推理时只需要读入已经压缩好的权重省去加载后再压缩的临时显存峰值。2.2 我为什么不选 Q3/Q2 而锁死 Q4_K_M有不少人看到 5.9GB 变 1.8GB 之后会想那再往下压一压Q3、Q2 不是更小我试过。Q2_K 能把体积压到 1.2GB 左右但代价是模型在工具调用场景下的输出变得不太可控。Agent 需要模型按照严格 JSON 格式输出 tool call低量化等级下模型更容易在中间插入自然语言、多生成一个花括号、把参数名拼错。一次输出格式错误Agent 框架就要重试一次延迟翻倍体验很糟糕。Q4_K_M 是实测下来性价比最高的档位体积只比 Q3 多大约 0.5GB但工具调用的格式稳定性明显上了一个台阶。我的 Agent 一天要跑几百轮 tool call格式错误率在 Q4_K_M 下可以控制在可接受范围内而 Q2 大概没跑几轮就开始胡来。2.3 从 HF 原始权重到 GGUF 量化文件的完整链路如果你想复现这个过程链路是固定的下载原始权重。git lfs clone或直接用huggingface-cli download都行关键是拿到完整的 FP16 权重目录。用llama.cpp里的convert_hf_to_gguf.py把权重转成 GGUF 格式的 F16 文件。再用llama-quantize对 F16 文件做 4bit 量化。我当时的命令大致是这样# 转换格式得到 FP16 精度的 GGUF 文件 python convert_hf_to_gguf.py ./model_dir --outfile model-f16.gguf --outtype f16 # 量化到 Q4_K_M得到最终部署文件 llama-quantize model-f16.gguf model-q4_k_m.gguf Q4_K_M量化后的 GGUF 文件大概 1.9GB启动时加载的就是这个文件。磁盘上多保留一份 5.9GB 的原始权重没有意义我一般在确认量化文件能正常跑之后就把它删了省出不少硬盘空间。补充一句如果你在 Ollama 里直接拉模型它内部已经做了量化不用自己走这条链路。但 Ollama 的量化档位是预设好的想精确控制 Q4_K_M 或 Q6_K还是用 llama.cpp 自己转更透明。3. KV Cache 的显存记账法Agent 短上下文红利3.1 KV Cache 是怎么算钱的权重只是显存账本的第一项。第二项是 KV Cache这玩意儿经常被人忽略但在长上下文场景下它才是真正的显存杀手。KV Cache 的显存计算公式可以简化成KV Cache 字节数 ≈ 2 × 层数 × 上下文长度 × 每层KV维度 × 每元素字节数其中上下文长度和每元素字节数是你启动推理时可以手动控制的。层数和每层维度由模型架构决定改不了。我的模型大约 30 多层每层 KV 维度在 2000 到 3000 之间。如果上下文长度拉到 8192KV Cache 在 FP16 精度下会膨胀到 2GB 以上几乎顶得上权重本身。这也是很多人明明模型只有 4GB一跑长上下文显存却爆掉的原因。3.2 用量化 KV 和 2048 上下文把缓存压到 0.3GB控制 KV Cache 有两种手段可以叠加使用。第一种是限制上下文长度。我把 Agent 的默认 ctx 设置为 2048而不是 8192。这样 KV Cache 的体积直接缩小四分之三。第二种是 KV Cache 量化。llama.cpp 支持把 KV Cache 也用 8bit 存储也就是-ctk q8_0 -ctv q8_0。每个 KV 元素从 2 字节降到 1 字节缓存体积再减半。两个手段叠加后我的 KV Cache 体积在 0.3GB 左右上下文长度FP16 KV CacheQ8_0 KV Cache8192约 1.2GB约 0.6GB4096约 0.6GB约 0.3GB2048约 0.3GB约 0.15GB那为什么我仍然在总账里算了 0.3GB因为我设置 ctx 为 2048 时实际峰值会比这个理想数字高一点加上中间激活值给个保守估计 0.3GB。3.3 Agent 不是聊天机器人外部记忆帮我省下上下文有人可能会担心2048 上下文够用吗这里要区分两个概念聊天机器人需要记住整段漫长对话而自养 Agent 不需要把历史对话全塞进上下文。Agent 的工作模式是接收任务调用工具观察结果输出结论。每一轮的核心信息很短几百 token 就够。真正需要长期保留的信息我交给外部记忆系统比如 embedding 模型加向量数据库。Agent 每次启动时只检索和当前任务最相关的几条记录塞进上下文而不是把所有历史一股脑全倒进去。这种上下文化的外部记忆设计本身就是一种显存优化策略。它让模型上下文的长度稳定在 2048 以内KV Cache 始终保持在低位。如果你想压低显存却不愿意限制上下文那 KV Cache 迟早会把省下来的空间全吃回去。4. 显存里的固定房租CUDA context 和推理框架开销4.1 只要上 GPU 就要交的 500MB第三笔账是 CUDA context。显卡驱动为进程分配显存时不是只给模型权重开一块空地还会创建 CUDA context、加载 cuBLAS/cuDNN 的 kernel、预留算子的工作区。这个基础开销在 300MB 到 600MB 之间具体看框架和驱动版本。我实测自家环境llama.cpp 启动后 CUDA context 大约占 400MB 到 500MB。这部分不管你跑什么模型都要交属于固定房租。很多显存爆掉的案例问题就出在这里用户算好模型权重 4GB、KV 1GB5GB 显存足够结果一启动发现总占用到了 5.5GB剩下几百MB 不知道哪儿去了。其实不是泄漏就是没把 CUDA context 算进预算里。4.2 为什么我用 llama.cpp 系 server 而不是 transformers 推理选对推理框架能省下不少隐形成本。PyTorch 的transformers推理很灵活但对显存不太友好它会为整个计算图预留较多临时空间CUDA context 也偏大启动后分分钟吃掉 1GB 基础开销。对于自养 Agent 这种需要长时间常驻、又要把显存压到极限的场景这个开销太大了。llama.cpp 系的 server 走的是另一条设计思路C 实现无 Python 运行时拖累显存分配更抠门启动开销明显更小。我实测同一个 3B 模型transformers 跑起来总占用轻松超过 3.5GB而 llama.cpp 压到 2.7GB。差距就是框架的基础收费不同。如果你用 Ollama它底层也是 llama.cpp同样能拿到这个红利只是暴露出来的高级参数少一些。4.3 最终显存公式与各种场景预算表把三笔账合在一起就是完整公式实际显存占用 ≈ 量化后权重体积 KV Cache体积 CUDA/框架开销代入我的配置1.82GBQ4_K_M权重 0.3GB2048上下文Q8 KV Cache 0.5GBCUDA overhead ≈ 2.7GB如果我把 ctx 拉到 4096KV Cache 翻倍总占用会变成约 2.9GB。如果我把量化换成 Q8_0权重变成约 3.3GB总占用就会跳到 4.1GB 左右。每一档选择都有代价你可以根据自己显卡的余量来回调配置组合权重KV Cache框架开销总占用Q4_K_M ctx2048 KV Q81.82GB0.3GB0.5GB2.7GBQ4_K_M ctx4096 KV Q81.82GB0.6GB0.5GB2.9GBQ8_0 ctx2048 KV FP163.30GB0.3GB0.5GB4.1GBQ8_0 ctx4096 KV FP163.30GB0.6GB0.5GB4.4GB5. 复现配置与实测2.7GB 跑 Agent 的具体表现5.1 我的完整启动参数与监控脚本下面这套参数是我正在用的可以直接抄。我用的推理后端是llama-server模型是 3B 量级的 Q4_K_M GGUF 文件。llama-server \ -m ./models/model-q4_k_m.gguf \ --host 127.0.0.1 \ --port 8080 \ -ngl 999 \ -c 2048 \ --ctk q8_0 \ --ctv q8_0 \ --temp 0.2 \ --top-p 0.9 \ --parallel 1参数逐个解释-ngl 999把所有权重层都放在 GPU。3B 模型量化后不到 2GB没有 offload 的必要如果换更大的模型这个参数可以下调留一部分层在 CPU 侧。-c 2048上下文长度。这是 KV Cache 膨胀的刹车片。--ctk q8_0 --ctv q8_0KV Cache 量化到 8bit配合-c 2048一起压缓存体积。--temp 0.2低温采样。Agent 工具调用需要稳定输出高温会让 JSON 格式放飞自我。启动后用这个脚本盯显存watch -n 0.5 nvidia-smi如果要更精确地看进程级显存占用可以用nvidia-smi --query-compute-appspid,used_memory --formatcsv我还习惯在 Agent 日志里记一笔推理耗时的钩子每次调用完成后把首 token 延迟、生成 token 数、显存占用峰值写进当天日志。自养 Agent 最忌讳凭感觉优化没有日志数据你根本不知道哪次改动让显存涨了 300MB。5.2 实测数据延迟、吞吐与工具调用稳定性跑起来之后我记录了几组真实数据。硬件是 RTX 4060 8GBCPU 是普通桌面级处理器内存 32GB。首 token 延迟在 300ms 到 800ms 之间波动。生成速度稳定在 35 到 45 tokens/s。对于 Agent 场景来说这个速度已经很够用因为 Agent 一轮操作本来就要等工具返回结果瓶颈通常在外部 API而不是模型推理。工具调用稳定性方面我做了 20 轮连续测试要求模型输出规范的 tool call JSON包括调用搜索、请求天气、读取本地文件等动作。结果 20 轮里出现 1 次额外解释性文本插在 JSON 外层其余 19 次都一次通过。我把速度、格式错误率、显存峰值全部写进了当天日志作为后续量化档位调整的基准。5.3 如果你的模型比 5.9GB 更大offload 扩展方案肯定有人要问上面这套玩法只适用于小模型如果模型本身是 7B 甚至更大的呢7B 模型原始 FP16 约 14GB量化成 Q4_K_M 约 4.5GB。如果还想把显存压到 3GB 以内单靠量化不够还需要动用 CPU offload也就是把一部分 transformer 层放到 CPU 内存里计算GPU 只处理剩下的层。具体做法是把-ngl调小llama-server -m ./model-7b-q4_k_m.gguf -ngl 25 -c 2048 --ctk q8_0 --ctv q8_0-ngl 25表示把前 25 层放 GPU后面的层走 CPU。GPU 上的层越少显存占用越低但 CPU 推理会让整体速度下降。以 7B Q4 模型为例-ngl从 99 降到 25显存能降到 3GB 左右速度则从 30 tokens/s 掉到 10 tokens/s 上下。显卡显存小的时候这是没办法的选择但至少能让你的 Agent 在 6G 卡上跑一个 7B 模型不至于直接 OOM。5.4 踩过的一个坑别把所有上下文都留给模型最后说一个实际翻过车的地方。我最初把-c设成 4096想着 Agent 偶尔塞点历史对话也没问题。结果跑了几轮之后发现显存占用比预期高了 300MB系统日志里还出现了 swap 抖动。排查下来有两个原因。一是 KV Cache 确实涨了二是 Agent 框架里埋了不少历史记录每一轮都会把之前的调用结果拼进 prompt。我后来在框架层加了一道上下文裁剪历史记录要么摘要要么丢给外部向量库只保留最近两三轮的关键信息。模型输入稳定在 1500 token 以内KV Cache 回落显存也回到 2.7GB。这个坑告诉你内核参数调好之后还要检查 Agent 自己的 prompt 组织逻辑。如果不限制喂给模型的内容长度再小的 KV Cache 预算也会被撑爆。我个人在实际操作中的体会是显存优化不是某一个参数的魔法而是一整套记账习惯。模型权重量化省 4GBKV Cache 控制省 0.5GB框架选型省 0.5GB外加 Agent 侧的上下文裁剪省 0.3GB。每一笔都不算惊心动魄叠在一起5.9GB 的模型就能在 2.7GB 显存里安稳跑起来。自养 Agent 的过程里最有价值的部分其实就是把这些零碎账目一笔一笔记清楚然后让每一块钱都花在刀刃上。