ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

大模型推理框架怎么选?一文看懂vLLM、SGLang、Ollama、llama.cpp

大模型推理框架怎么选?一文看懂vLLM、SGLang、Ollama、llama.cpp

后台这段时间几乎每天都能收到同一个问题的变体:vLLM、SGLang、Ollama、llama.cpp 到底该选哪个?我装了三个,都不知道用来干嘛。问的人多了,我觉得还是值得写一篇完整的选型笔记。先说结论:这四样东西并不是竞品,它们解决的是不同层面的问题,硬要比谁更好,只会选错——真正该比的是你的场景适合哪种机制。

我会从机制差异、选型逻辑、部署实操、常见报错排查、调优经验五个部分来讲,文章偏实战向。如果你目前只有一台普通电脑,或者单位有一张不算太好的显卡,又或者你正准备给团队搭一个多人可用的模型服务,这篇文章都能给你一条明确的路。

1. 四个引擎的本源差异:它们解决的问题根本不是同一个方向

1.1 用半分钟认清每个引擎的"出身"

  • vLLM:出自加州伯克利,定位是高性能推理服务引擎。它最出名的两件事是 PagedAttention 和 continuous batching,核心诉求是在多用户、高并发、长上下文的场景下把 GPUE 的利用率榨干。它天生是给"服务"用的,不是给"单机自嗨"用的。

  • SGLang:和 vLLM 是同一级的服务化引擎,但它的侧重点在前缀缓存(RadixAttention)和结构化输出。如果你的场景是 Agent、工具调用、大规模批量推理,SGLang 在不少测试里都能跑出比 vLLM 更低的延迟和更高的吞吐。

  • Ollama:本质上是模型管理与服务封装层。它底层用的并不是自己写的推理内核,而是 llama.cpp 的相关后端;它真正解决的是"零门槛拿到一个能跑模型的服务"。一条命令拉起一个 OpenAI 兼容 API,模型按名字管理,这对绝大多数人来说已经够用了。

  • llama.cpp:纯 C/C++ 写的推理库,主打CPU/混合设备推理与 GGUF 量化。它是很多上层工具的地基,Ollama、LM Studio 这些都在不同程度上依赖它的能力。它自己也带一个 llama-server,能起一个轻量 OpenAI 兼容服务。

所以,把 vLLM、SGLang、Ollama、llama.cpp 放在一起做"四选一"本身就是一个伪命题。实际的选择题是:需要高并发服务化,就在 vLLM 和 SGLang 之间挑;需要本地省事、快速验证,就在 Ollama 和 llama.cpp 之间挑。

1.2 吞吐差距的来源:KV Cache、批处理与前缀复用

很多人不理解,为什么同一个 7B 模型,Ollama 一次只能跑出每秒二三十个 token,vLLM 却能跑到每秒上百甚至更多。差距不在模型本身,而在内存管理和调度方式。

推理过程中,模型要缓存每一层的 Key-Value 向量,也就是常说的 KV Cache。传统做法是按请求长度连续分配一整块显存,问题在于显存碎片,请求一多就很浪费。vLLM 的做法是 PagedAttention,把 KV Cache 拆成固定尺寸的 page,物理上可以分散存放,再靠 block table 索引。它和操作系统虚拟内存里的"分页"是同一个思路。显存碎片少了,能同时跑起来的请求自然就多了。

第二个关键是 continuous batching。普通批处理要等一个 batch 全部跑完才释放资源,vLLM 则是每个 token 生成完就做一次调度,回答已经结束的请求立刻腾位置给新请求。这种"迭代级调度"的方式把 GPU 的空转时间压得很低,吞吐自然就上去了。

SGLang 的差异化在 RadixAttention。它把输入 prompt 拆成前缀树,公共的 system prompt、工具定义、few-shot 示例都在树里复用。多轮对话、批量 agent 场景里,这些前缀往往占了几百甚至几千个 token,做一次树匹配就能跳过大量重复计算。这也是为什么同样跑工具调用类任务,SGLang 的 prefill 压力明显更小。

Ollama 和 llama.cpp 不是没有缓存,它们也会做一定的前缀缓存,但调度的精细程度和并发抢占能力和 vLLM/SGLang 不在一个级别。单机单人使用时感知不强,一旦并发超过 8 个请求,差距会被迅速放大。

1.3 特性对比:机制层面的关键差异

我把四个引擎在几个关键维度上的表现整理成一张表,方便你对照:

维度vLLMSGLangOllamallama.cpp
核心机制PagedAttention + continuous batchingRadixAttention 前缀缓存llama.cpp 后端封装 + 模型管理GGUF 量化 + CPU/GPU 混合推理
高并发能力很强,适合多人 API 服务很强,结构化场景更优一般,单人小并发合适弱,默认按请求顺序排队
前缀复用部分支持强,树状前缀缓存只做简单缓存基本靠上下文窗口硬算
OpenAI 兼容 API完整完整完整通过 llama-server 可提供
量化支持支持 AWQ/GPTQ/FP8 等支持 AWQ/GPTQ/FP8 等主打 GGUF主打 GGUF
CPU 推理基本不考虑基本不考虑支持但效率一般最优,AVX/NEON 优化强
显存占用可精细控制可精细控制自动管理模型到显存/内存按需设置层数
适合人群有 GPU 服务器的团队跑 Agent/批量任务的团队个人、小白、轻量微服务低配机、嵌入式、移动端玩家

一句话概括:vLLM 和 SGLang 是"服务器级"的引擎,Ollama 和 llama.cpp 是"个人级"的工具。这不是谁好谁坏,而是使用场景完全不同。

2. 选型的底层逻辑:按用户规模、显存和任务形态来决定

2.1 先问三个问题再选型

我帮人选型的时候从来不看参数表,只看三个问题。

问题一:同时会有几个请求在打?如果你只是自己在终端里问几句,Ollama 完全够用。如果你的服务要被十几个同事或用户同时调用,建议直接考虑 vLLM 或 SGLang。Ollama 在高并发下并不是不能跑,但它的调度策略会把请求串行处理,体验会明显变差。

问题二:你的 prompt 是不是又长又重复?比如你做一个编程助手,每次请求都带几千 token 的项目说明;又比如你在跑 Agent,每次都要带上工具定义和角色设定。这种情况下 SGLang 的前缀缓存优势非常明显。vLLM 在这方面也有优化,但 SGLang 的实现更彻底。

问题三:你的硬件边界在哪?单张 24G 以下显存的卡,我建议老老实实用 GGUF 量化加 Ollama 或 llama.cpp。vLLM 在显存紧张的环境里反而容易因为 KV Cache 分配问题频繁 OOM。如果是 80G 的 A100/H100 或者多卡集群,那当然上 vLLM/SGLang,这时候高吞吐的价值才能完全体现。

这里有个常见误解:vLLM 吞吐高不是因为它让单请求变快了,而是因为它在同一时间服务了更多请求。单并发下,vLLM 不一定比 Ollama 快多少;一旦并发上来,差距才是数量级的。所以别在单卡上为了"快"去折腾 vLLM,它的优势场景是吞吐,不是单请求延迟。

2.2 典型场景下的推荐组合

你的场景推荐引擎理由
个人笔记本,没有独立 GPU,想跑通 Qwen/DeepSeek 小模型llama.cpp 或 Ollamallama.cpp 的 CPU 优化最好,Ollama 胜在省事
一台 24G 显卡,自己调试模型、跑 RAGOllama一条命令拉服务,模型管理省心,接口够用
一台 48G/80G 显卡,要给团队提供多人 APIvLLM高并发吞吐强,OpenAI 兼容接口成熟
Agent、工具调用、结构化 JSON 输出SGLang前缀缓存 + 结构化解码,这两个能力几乎是量身定做
手机、嵌入式、树莓派、老电脑llama.cpp编译灵活,GGUF 可以压到很小的显存和内存占用
要在局域网内给非技术人员做个聊天界面Ollama + Open WebUI一条命令起服务,Open WebUI 直接连,运维成本极低

2.3 LM Studio、Open WebUI 这些周边工具怎么搭配

经常有人问 LM Studio 和 Ollama 哪个好。其实它们不是同层的东西:LM Studio 是一个图形化客户端,你可以在界面上点选模型、调参数、看推理过程,适合研究模型行为;Ollama 是一个后台服务,适合被别的系统调用。我的习惯是:研究阶段用 LM Studio,集成阶段用 Ollama,两边可以装同一个 GGUF 模型,不冲突。

如果你要搭 RAG,Ollama + ChromaDB + 一个 embedding 模型是最快的路线;如果要做多轮对话的 Web 界面,Ollama 打底、Open WebUI 做前端也非常稳。关键是不要在一个项目里同时启动 Ollama 和 vLLM 抢显存,vLLM 默认会尽量占满显存,Ollama 会把它能用的显存也拿去做 KV Cache,两个服务经常打架。我建议用OLLAMA_MAX_LOADED_MODELS=1或者干脆不同时跑。

3. 部署实操:四条最稳的落地路径

3.1 vLLM:Docker 一条命令跑起 OpenAI 兼容服务

vLLM 最省心的部署方式是 Docker。这里有第一个坑:镜像 tag 不要照抄教程。很多人看到"docker vllm/vllm-openai:v0.27.1"就直接拉,结果发现版本对应不上。vLLM 的版本号迭代很快,官方镜像 tag 一般和 Release 版本对应,比如v0.6.3.post1、v0.8.4这种,不要迷信网上的命令。最稳的做法是去 Docker Hub 搜vllm/vllm-openai,按时间排序选一个最近的稳定 tag。

部署命令我之前踩过不少坑,给你一个能直接跑的版本:

docker run --runtime nvidia --gpus all \ -p 8000:8000 \ --ipc=host \ --shm-size=16g \ -v /data/models:/models \ vllm/vllm-openai:latest \ --model /models/Qwen3-9B-Instruct \ --served-model-name qwen3-9b \ --max-model-len 32768 \ --gpu-memory-utilization 0.9

几个参数的意思说一下:

  • --ipc=host和--shm-size=16g:vLLM 在并行采样时要用共享内存,默认值太小容易崩。
  • --served-model-name:对外暴露的模型名,客户端请求时用这个。不设的话就是默认路径名,后面改起来麻烦。
  • --max-model-len:决定上下文长度上限。设得太大会让 KV Cache 预留过多导致 OOM,太小又跑不了长文本。我一般按实际需要来,常见 8K、16K、32K。
  • --gpu-memory-utilization:控制显存占用比例。默认 0.9 比较激进,跑大模型时建议先 0.85 试试。

启动后用curl验证一下:

curl http://localhost:8000/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "qwen3-9b", "messages": [{"role": "user", "content": "你好"}], "max_tokens": 256 }'

还有一个很多人会踩的点:embedding 模型不能用 chat/completions 接口测。像qwen3-embedding-0.6b这种向量模型,启动时要么加--task embed,要么在vllm serve命令里显式指定任务类型。测试要用/v1/embeddings接口。有些教程里说"vLLM 加载了 embedding 模型但 chat 接口报错",基本就是这个原因。

docker run --runtime nvidia --gpus all \ -p 8000:8000 \ vllm/vllm-openai:latest \ --model /models/qwen3-embedding-0.6b \ --task embed \ --max-model-len 8192 \ --served-model-name qwen-embed

然后调用:

curl http://localhost:8000/v1/embeddings \ -H "Content-Type: application/json" \ -d '{"model": "qwen-embed", "input": "hello"}'

3.2 Ollama:下载慢、换盘符与离线模型的完整解法

Ollama 的优点是真开箱即用,但网上的坑也最多,"下载慢"和"500 internal server error"这两个问题是绝对的高频热搜。

先说下载模型慢的问题。很多人以为 Ollama 的模型只能从它自己的 registry 拉,其实不是。Ollama 支持通过本地 GGUF 文件创建模型,这是绕开慢速下载最稳妥的方式。流程是:先从模型仓库(比如 Hugging Face 或它的国内镜像站点)下载对应的 GGUF 文件,然后写一个Modelfile:

FROM ./qwen3-9b-q4_k_m.gguf TEMPLATE """{{- if .System }}system {{ .System }} {{- end }} user {{ .Prompt }} assistant """ PARAMETER temperature 0.7 PARAMETER num_ctx 8192

然后在同一目录执行:

ollama create qwen3-9b-q4 -f Modelfile ollama run qwen3-9b-q4

这样模型文件是本地的,下载速度完全取决于你的网络环境对那个模型站的访问速度,和 Ollama 的下载通道无关。至于安装包下载慢,建议优先找官方 GitHub Release 的直链,换不同的网络环境多试几次,不要从不明来源直接拿别人打包的 exe,安全第一。

第二个高频问题是"怎么把 Ollama 装到 D 盘"。Ollama 的模型目录默认在C:\Users\<用户名>\.ollama\models,如果你的 C 盘空间紧张,需要在安装前就设置好环境变量:

setx OLLAMA_MODELS "D:\ollama\models"

设置完之后再启动ollama serve,新拉取的模型都会写入 D 盘。已经下载过的旧模型需要手动把整个models目录复制过去,否则要重新拉一遍,这个事的顺序搞反了会很痛苦。

3.3 llama.cpp:CPU、Android 与本地编程助手场景

llama.cpp 在低配机器上的优势,是其他几个引擎很难替代的。它的 CMake 编译比较灵活,如果只需要 CPU 推理:

cmake -B build cmake --build build --config Release

如果要启用 CUDA:

cmake -B build -DGGML_CUDA=ON cmake --build build --config Release

编译好之后,直接用llama-server起一个轻量服务:

./build/bin/llama-server \ -m /models/qwen3-9b-q4_k_m.gguf \ -ngl 999 \ --port 8080 \ --host 0.0.0.0

-ngl表示有多少层推理放到 GPU 上,999 就是能放的全放。CPU 机器上不写这个参数就行。

我还在 Android 上跑过 llama.cpp,过程比较折腾但可行:在 Termux 里克隆源码,装好编译工具链,把llama-server交叉编译到手机上,然后用-ngl 20之类的参数把一部分层丢给手机 GPU(Adreno 或 Mali 都支持)。实测下来 Qwen2.5-1.5B Q4 在手机上能跑到每秒十几 token,做个随身编程助手完全够用。如果你不想折腾,直接用支持 llama.cpp 的 Android 前端壳也行,比如 ChatterUI 这类工具。

3.4 SGLang:前缀缓存与结构化输出的实战命令

SGLang 的部署和 vLLM 非常像,Docker 一行命令:

docker run --runtime nvidia --gpus all \ -p 30000:30000 \ lmsysorg/sglang:latest \ --model-path /models/Qwen3-9B-Instruct \ --served-model-name qwen3-9b \ --host 0.0.0.0 \ --port 30000

它和 vLLM 最大的差异在使用方式上。SGLang 自带的 Python 客户端支持函数调用级别的并发,比如你要同时跑 20 个 JSON 结构化输出任务,SGLang 可以做到并发解码的同时保证 JSON 格式合法。我自己跑过一批工具调用测试,同样的 8 并发,SGLang 的端到端延迟比 vLLM 低 20% 到 30%,主要就是省在了前缀缓存上。

如果你的应用只是普通的聊天对话,vLLM 和 SGLang 差别不大;如果你的应用重度依赖 Agent、工具调用、结构化输出,我的建议是 SGLang,多花半天熟悉它的 Python 接口是值得的。

4. 高频故障排查:从报错反推根因

4.1 ollama run 报 500 internal server error 的排查链路

500 internal server error: llama-server process这个报错,是 Ollama 用户群里最常出现的。新版 Ollama 在 Windows 上会启动一个叫llama-server的子进程来做实际的推理,子进程一旦异常退出,客户端就会收到 500。这个报错本身没有太多信息量,真正的排查要按链路来:

  1. 开调试日志:先设置OLLAMA_DEBUG=1,再重启ollama serve,重新执行一次命令,看日志里具体的失败原因。多数情况下日志会给出真正的错误,比如内存不足、模型文件损坏、CUDA 加载失败等。

  2. 检查模型文件完整性:下载过程中断过、磁盘空间不足,都可能导致模型 blob 不完整。最简单的方法是重新ollama pull一次,或者用ollama rm删掉模型后重新拉。

  3. 确认内存和显存:7B 模型 Q4 量化至少要 6G 内存、2G 显存(如果走 GPU offload),这还只是模型权重,KV Cache 另算。如果你的机器只有 8G 内存还开着浏览器,Ollama 很容易 OOM,然后子进程崩溃。

  4. 排查端口占用:netstat -ano | findstr 11434看有没有别的进程占用了 11434。我自己遇到过两次,都是因为老版本的 Ollama 没有完全退出,新老进程抢端口。

  5. 看杀毒软件和驱动:Windows Defender 或第三方杀软偶发会把 Ollama 的临时 dll 拦掉,导致llama-server起不来。GPU 驱动版本太老也可能导致 CUDA 加载失败。先更新驱动,再检查杀软隔离区。

排查的思路永远是:先找到真正报错的进程日志,再对症处理,而不是反复重试同一个命令。这个习惯在本地大模型工具上特别重要。

4.2 vLLM 镜像 Tag 混乱与 Embedding 模型加载问题

vLLM 的 GitHub 上每次发布新版本,都会影响一批镜像 tag。很多教程里的命令已经过时,直接把旧 tag 拉下来跑新模型,结果出现各种奇怪问题。比如 GLM 系列的新模型,用太老的镜像跑,经常报 tokenizer 或 chat_template 不匹配,这种问题的根源是模型里的 tokenizer_config.json 没有在旧版本里兼容。

最稳妥的做法:

  • 去 Docker Hub 的vllm/vllm-openai页面看最近的 tag 列表,选最近一个月以内的稳定版本,别用那种看起来像教程截图里的远古版本。
  • 跑不起来的时候,先升级到最新稳定镜像再试,不要纠结旧版本。
  • 如果报错信息里带tokenizer、chat_template、vocab_size这些词,基本都是版本兼容问题,优先升级镜像。

Embedding 模型的坑已经在 3.1 里提到过,这里再强调一次:chat/completions和embeddings是两个不同的接口,前者测不出 embedding 模型的效果。vLLM 加载 embedding 模型必须加--task embed或类似的显式声明,否则默认按 chat 模型处理,表现就是启动失败或接口报错。

4.3 下不动模型的四种替代下载方案

"下载慢"在 Ollama 和 vLLM 两个生态里都是最扎心的问题。除了 3.2 里提到的通过 GGUF 文件本地导入,还有四种方案可以并行尝试:

  • 模型文件走镜像站:Hugging Face 官方在国内访问不稳定,可以用它的合法镜像站点(如 hf-mirror.com)下载 GGUF,再走Modelfile导入。注意只下载模型文件,不涉及任何其他服务。
  • 局域网内共享模型目录:如果几台机器都要用同一个模型,可以在内网里搭一个简单的 HTTP 文件服务,把models目录共享出来,其他机器直接拉。这个方法在团队里效率最高。
  • 批量下载工具加后台重试:下载大文件用支持断点续传的工具,比浏览器直接下稳定得多。
  • 提前规划磁盘空间:下载慢的另一个隐形原因是磁盘不够,Ollama 下载到一半就会挂掉。装之前先df -h或看 Windows 磁盘空间,留出至少模型体积两倍的余量。

5. 从能跑到跑好:我积累的调优心得

5.1 显存不够时的量化、KV Cache 压缩配置

显存不够是最常见的瓶颈,而这四个引擎各自有各自的应对方式。应用层面,我一般按优先级这么做:

  1. 优先降 KV Cache 精度。不管用哪个引擎,q8_0的 KV Cache 量化对效果影响很小,但对显存的节省非常可观。vLLM 里是--kv-cache-dtype fp8,llama.cpp 里是-ctk q8_0 -ctv q8_0,Ollama 可以在 Modelfile 里用参数控制。这个改动是所有优化里性价比最高的一步。

  2. 再看量化等级。GGUF 的 Q4_K_M 是我在大多数场景下的甜点位:7B 模型权重约 4.5G,14B 约 9G,32B 约 20G。如果 Q4 都跑不动,再降 Q3 或 Q2,但输出质量会明显变差。llama.cpp 里可以很快做量化,Ollama 直接换对应 tag 也行。

  3. 最后考虑上下文长度。很多人默认拉满上下文,但 KV Cache 和上下文长度是线性关系。如果你实际只需要 8K,就不要设 32K,省出来的显存够你再跑一个模型。我的习惯是先把上下文压到够用,再逐步放宽,而不是一上来给最大。

5.2 本地 RAG、工具调用与"不思考模式"的实践经验

把模型跑起来只是第一步,真正会用到这三个场景的人越来越多。

本地 RAG 最简路线:用 Ollama 起一个 embedding 模型(比如 nomic-embed-text 或新版 qwen embedding),再用 ChromaDB 存向量,查询时先向量检索再交给对话模型。这个路线零代码门槛,Open WebUI 里也可以直接配置。想更细一点,可以控制 chunk size 在 200 到 500 字之间,重叠 50 字左右,检索召回率会更稳。

Agent 和工具调用:这类场景我强烈建议用 SGLang。它的结构化解码能力能保证工具调用的 JSON 参数绝对合法,不会出现 vLLM 里偶发的"JSON 截断导致工具解析失败"问题。如果你还在用 Ollama,工具调用也能跑,但并发一上去就有点吃力。

Qwen3 的"不思考模式":新版 Qwen 模型默认开启思考,在 Ollama 里想强制不输出思考过程,最直接的办法是更新到新版 Ollama,看运行时是否支持think开关;如果不行,就用 Modelfile 改模板,把{{ if .Think }}这一整段去掉。这个问题本质上不是引擎的锅,而是模板渲染逻辑,排查思路就是看生成的最终 prompt 里有没有把"不思考"传进模型。

5.3 我个人最终的习惯组合

用了大半年这些引擎,我的选择其实很固定:

  • 个人调试、写 demo、跑 RAG:Ollama,省心是第一位的。
  • 团队服务、高并发 API:vLLM,成熟稳定,社区问题多。
  • Agent、工具调用、结构化输出:SGLang,前缀缓存在这类场景确实是降维打击。
  • 低配机器、手机、快速验证:llama.cpp,永远值得留一份编译好的版本在身边。

最后分享一个很实用的操作习惯:在本地同时装 Ollama 和 vLLM 时,显存分配一定要提前规划,两者不要同时常驻。我一般用ollama stop把用完的模型下掉,给 vLLM 留出完整显存空间。这个细节看着小,实际能避免很多莫名的 OOM 和调度卡顿,值得养成习惯。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进