之前我们讨论过自建大语言模型服务,当时使用的是Ollama作为后端服务,使用Lobechat作为前端,通过API调用后端的Ollama来实现对话。
最近我在尝试使用OpenClaw,但它烧Token的速度实在太快了,就想着自己运行一个本地的大语言模型来提供Token。跟ChatGPT聊过之后,它告诉我这种情况下最好使用vLLM而非Ollama,因为vLLM提供与OpenAI兼容的API,支持Copilot,且性能更好。于是我就试着转向了vLLM。
vLLM
# vLLM和Ollama都是在自己电脑上或者在服务器上运行大语言模型(LLM)的工具,然而他们的使用场景却有所不同。简单来讲,vLLM致力于高效地运行大语言模型,而Ollama专注于使大语言模型方便地运行起来。
另外,vLLM提供与OpenAI兼容的API,如果我们要运行的服务依赖于OpenAI之类的商业模型API,而且支持用户自定义模型提供商,那我们就可以接入我们自己部署的vLLM。
Ollama
# 关于Ollama的介绍和部署请参考本系列的第一篇文章。
由于Ollama使用的是一些自定义的REST API,和OpenAI的API不兼容,不能直接接入很多使用大语言模型的服务。不过,有开发者开发了一款可以调用Ollama并兼容OpenAI API的工具LiteLLM 。
我们只需要将这个工具和Ollama容器一块儿部署,再做一些设置,它就可以提供与OpenAI的API兼容的API。
vLLM和Ollama的比较及注意事项
# 显存占用
在运行时,我们能感知到的vLLM与Ollama最大的区别是,部署vLLM需要将整个模型加载到GPU的显存里,即使模型处于闲置(未被调用)的状态,vLLM依然会占用这部分显存。
也就是说,vLLM几乎不能和其他重度使用显卡的程序一块儿运行。例如,运行vLLM后,如果你想打游戏,那就需要先下线vLLM。
而Ollama则没有这个问题。Ollama只有在被调用时才会把模型加载到显存里,在你想打游戏时,你不需要关闭Ollama,只要在你打游戏期间不去调用Ollama,那它就能和你的游戏相安无事。
可调用模型数量
在部署Ollama后,我们可以下载多个模型,用户可以决定调用哪一个。
但在部署vLLM后,加载到显存中的模型是固定的,一个vLLM容器只能加载一个模型,要想选择模型调用,就需要同时部署多个vLLM容器。这对于我们个人用户显然是不现实的,我们的显卡一般是做不到的。如果想加载多个模型,那就只能用很小的模型,意义不大。
部署大语言模型及前端
# 我有两台有16GB显存显卡的电脑,因此我选择在电脑A上部署vLLM,在电脑B上部署带有vLLM接口的Ollama(我们暂且称其为vOllama)。通过域名将vLLM和vOllama的接口暴露于公网。
在任一台电脑上(可以是A或B,也可以是另一台电脑C)上部署Open WebUI前端,它可以方便地接入自己部署的大语言模型。
vLLM部署
# docker-compose
# docker-compose.yml文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
services :
vllm :
image : vllm/vllm-openai:v0.17.0
container_name : vllm
restart : unless-stopped
runtime : nvidia
deploy :
resources :
reservations :
devices :
- driver : nvidia
count : all
capabilities : [ gpu]
environment :
- HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}
- PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
- HF_HUB_DISABLE_XET=1
- HF_HUB_ENABLE_HF_TRANSFER=0
- VLLM_API_KEY=${VLLM_API_KEY:-dummy}
- TZ=${TZ:-UTC}
volumes :
- ~/.cache/huggingface:/root/.cache/huggingface
ipc : host
command : >
${MODEL}
${MODEL_PARAMETERS}
networks :
traefik-net :
aliases :
- vllm
networks :
traefik-net :
external : true
这里的MODEL、MODEL_PARAMETERS和其他变量在环境变量文件.env文件中定义:
1
2
3
4
5
HF_TOKEN = hf_your_token
MODEL = Qwen/Qwen2.5-Coder-14B-Instruct-AWQ
MODEL_PARAMETERS = "--host 0.0.0.0 --port 8000 --gpu-memory-utilization 0.81 --dtype auto --kv-cache-dtype fp8 --enforce-eager"
VLLM_API_KEY = your_api_key
TZ = Europe/Paris
其中,HF_TOKEN是Huggingface的token,我们需要它以便从Huggingface下载大语言模型。
vLLM只能加载一个模型,我们选了Qwen/Qwen2.5-Coder-14B-Instruct-AWQ,加载它大约需要14GB的显存。
在命令行中输入类似下面的命令来测试我们的vLLM是否部署成功:
1
2
3
4
5
6
7
curl https://vllm.example/v1/chat/completions \
-H "Authorization: Bearer your_vllm_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2.5-Coder-14B-Instruct-AWQ",
"messages": [{"role": "user", "content": "Hello"}]
}'
如果收到类似下面的回复就表示vLLM已成功部署并可以通过域名访问:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
"id" : "chatcmpl-82d9387336a446bd" ,
"object" : "chat.completion" ,
"created" : 1774276907 ,
"model" : "Qwen/Qwen2.5-Coder-14B-Instruct-AWQ" ,
"choices" : [
{
"index" : 0 ,
"message" : {
"role" : "assistant" ,
"content" : "Hello! How can I assist you today?" ,
"refusal" : null ,
"annotations" : null ,
"audio" : null ,
"function_call" : null ,
"tool_calls" : [],
"reasoning" : null
},
"logprobs" : null ,
"finish_reason" : "stop" ,
"stop_reason" : null ,
"token_ids" : null
}
],
"service_tier" : null ,
"system_fingerprint" : null ,
"usage" : {
"prompt_tokens" : 30 ,
"total_tokens" : 40 ,
"completion_tokens" : 10 ,
"prompt_tokens_details" : null
},
"prompt_logprobs" : null ,
"prompt_token_ids" : null ,
"kv_transfer_params" : null
}
在命令行里这个JSON会被压缩在一行显示。
vOllama部署
# 配置文件
# docker-compose.yml文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
services :
ollama :
container_name : ollama
image : docker.1ms.run/ollama/ollama:0.17.7
environment :
- OLLAMA_ORIGINS=*
- OLLAMA_HOST=0.0.0.0
- TZ=America/New_York
deploy :
resources :
reservations :
devices :
- driver : nvidia
capabilities : [ "gpu" ]
count : all
volumes :
- ./ollama-data:/root/.ollama
restart : always
networks :
- traefik-net
litellm :
image : ghcr.io/berriai/litellm:main-latest
container_name : litellm
restart : unless-stopped
command : [ "--config" , "/app/config.yaml" ]
volumes :
- ./litellm-config.yaml:/app/config.yaml
networks :
- traefik-net
networks :
traefik-net :
external : true
这里我们部署了ollama和litellm两个容器,它们都使用我们之前创建的网络traefik-net,我们依然使用Cloudflare Tunnel来反向代理vOllama服务。
litellm需要一个配置文件litellm-config.yml,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
model_list :
- model_name : qwen3.5:0.8b
litellm_params :
model : ollama/qwen3.5:0.8b
api_base : http://ollama:11434
- model_name : mistral:7b
litellm_params :
model : ollama/mistral:7b
api_base : http://ollama:11434
general_settings :
master_key : your_ollama_api_key
配置文件主要有以下作用:
model_list告诉litellm我们在ollama里面运行了哪些大语言模型。当我们给Ollama下载安装了其他模型,我们需要更新这个文件以加入新的模型。由于我们将模型暴露在了公网上,我们需要设置一个API key(即master_key)来控制访问,不提供API key的不能调用我们的模型。 安装大语言模型
# 使用docker compose up -d来部署Ollama容器。 部署成功后,Ollama还是空的,里面没有下载任何大语言模型。我们使用docker exec -it ollama ollama run qwen3.5:0.8b来下载用运行qwen3.5:0.8b这个模型。 安装完成后可以继续下载其他模型 使用docker exec -it ollama ollama list可以列出安装了哪些模型。 可以使用如下命令测试是否部署成功:
1
2
3
4
5
6
7
curl https://ollama.example/v1/chat/completions \
-H "Authorization: Bearer your_ollama_api_key" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3.5:0.8b",
"messages": [{"role": "user", "content": "Hello"}]
}'
如果收到类似下面的回复就表示vOllama已成功部署并可以通过域名访问:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"id" : "chatcmpl-5844b507-f967-4412-b8fa-815c4e3235b1" ,
"created" : 1774276549 ,
"model" : "qwen3.5:0.8b" ,
"object" : "chat.completion" ,
"choices" : [
{
"finish_reason" : "stop" ,
"index" : 0 ,
"message" : {
"content" : "Greetings! How's going? 😊 Feel free to ask me anything—whether you need help, just wanted to say hello, or maybe something else. I'm here for fun and helpful!" ,
"role" : "assistant"
}
}
],
"usage" : {
"completion_tokens" : 111 ,
"prompt_tokens" : 15 ,
"total_tokens" : 126
}
}
在命令行里这个JSON会被压缩在一行显示。
Open WebUI部署
# 只用curl或者API来调用部署的大模型显然是不方便的,我们需要部署一个网页版的客户端。这里我们选用一个开源的客户端Open WebUI 。
Open WebUI简介
# Open WebUI是一个开源的大语言模型网页版客户端,风格跟OpenUI的ChatGPT页面很像。它允许我们接入自己部署的vLLM或者Ollama模型,也支持接入商用的各种模型(需要API key)。
Open WebUI部署
# docker-compose.yml文件如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version : "3.9"
services :
open-webui :
image : ghcr.io/open-webui/open-webui:0.8.10
container_name : open-webui
volumes :
- ${DATA_DIR}:/app/backend/data
environment :
- OPENAI_API_BASE_URL=http://vllm:8000/v1
- OPENAI_API_KEY=${VLLM_API_KEY:-dummy}
- TZ=${TZ:-UTC}
restart : unless-stopped
networks :
traefik-net :
aliases :
- open-webui
networks :
traefik-net :
external : true
这里的OPENAI_API_BASE_URL和OPENAI_API_KEY填了我们在本地部署的vLLM。不填也可以,待会儿可以在图形界面设置。
使用下面的命令启动open-webui容器:
Open WebUI配置
# 设置反向代理后,我们就可以通过域名访问Open WebUI了。第一次访问时,Open WebUI会要求我们设置一个管理员账号和密码。
用管理员账号登录后,点击右上角的头像,可以看到"Admin Panel"选项,点击进入可以设置Open WebUI所需要连接的后端模型服务。
在“Admin Panel"的"Settings"页面里,有”Connections"选项,在这里可以添加我们之前部署的Ollama服务、vLLM服务,或者vOllama服务,以及商用的大语言模型服务(如OpenAI、Azure、Anthropic等)。
添加完成后,点击"settings"页面里的"Models"选项,你可以看到Ollama服务或者vLLM服务中可用的模型。这些模型默认的权限是"private",我们可以点击某个模型右侧的编辑按钮,在模型具体设置里点击"Access"选项,具体设置哪些用户可以访问这个模型。
最后,在设置好模型后,就可以像使用ChatGPT一样使用Open WebUI了,输入问题后,Open WebUI会调用我们之前部署的vLLM或者Ollama服务来获取回复:
问题解决
# Ollama安装Qwen3.6
# 在使用Ollama安装去审核版Qwen3.6模型Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive 时,可能会遇到安装失败的问题。报错信息如下:
1
llama_model_load: error loading model: error loading model architecture: unknown model architecture: 'qwen35moe'
这个问题在GitHub上有讨论:
https://github.com/ollama/ollama/issues/14503
其原因是在下载Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive模型完成后,Ollama生成的模型文件Modelfile并不正确。里面有两行From /root/.ollama/xxx,这表明Ollama试图加载两个模型文件,但实际上只需要加载一个模型文件。我们需要将第二个From /root/.ollama/xxx删除掉或用#注释掉,保存后重新运行这个模型就可以了。
但实际上Modelfile似乎并没有被显式地创建,我们需要进入容器
1
docker exec -it ollama bash
然后在容器内内执行下面的命令来生成这个文件:
1
ollama show --modelfile hf.co/HauhauCS/Qwen3.6-35B-A3B-Uncensored-HauhauCS-Aggressive:IQ2_M | cat > /root/Modelfile
注释掉或者删除掉第二行From /root/.ollama/xxx后,保存退出,然后重新创建这个模型:
1
ollama create Qwen3.6-35B-A3B-Uncensored-IQ2M -f /root/Modelfile