使用 vLLM 部署 BAAI/bge-reranker-v2-m3,并接入本地 WeKnora

记录一次把 BAAI/bge-reranker-v2-m3 部署成 vLLM reranker 服务,并让本地 WeKnora 通过 HTTP API 调用的完整过程,包括显存、镜像源、模型下载和接口验证。

文章目录

最近在调研 WeKnora 的知识库问答效果时,发现重排模型是一个比较关键的环节。

普通向量召回只负责从知识库里找出一批“看起来相似”的文本片段,但这些片段不一定真的适合回答当前问题。Reranker 的作用就是对这些候选片段重新打分,把更相关的片段排到前面,再交给大模型生成答案。

这次我选择部署的是:

BAAI/bge-reranker-v2-m3

部署方式是:

服务器上单独部署 vLLM reranker 服务
本地 WeKnora 通过 HTTP API 调用远程 reranker

最终接口地址类似:

http://服务器IP:18082/v1/rerank

本文记录完整部署过程,以及中间踩到的几个坑。


一、部署目标

我的目标不是把 reranker 和 WeKnora 部署在同一台机器上,而是:

GPU 服务器:只负责运行 reranker 服务
本地 WeKnora:通过 API 调用远程 reranker

所以整体结构是:

本地 WeKnora
   |
   | HTTP
   v
GPU 服务器 vLLM Reranker
   |
   v
BAAI/bge-reranker-v2-m3

这样做的好处是:

  1. 本地机器不用占 GPU;
  2. WeKnora 可以继续本地开发;
  3. reranker 可以独立维护;
  4. 后续如果换模型,只需要调整服务器侧服务。

二、服务器环境

服务器环境大致如下:

系统:Alibaba Cloud Linux 3
GPU:Tesla T4 16GB
Docker:26.1.3
Docker Compose:v2.27.0
NVIDIA Driver:590.48.01
CUDA:13.1
vLLM 镜像:vllm/vllm-openai:latest,对应 vLLM 0.24.0

先检查 GPU:

nvidia-smi

再检查 Docker:

docker --version
docker compose version

如果普通用户没有 Docker 权限,需要用 sudo docker

验证 Docker 容器里能否访问 GPU:

sudo docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi

只要这条命令能正常看到 GPU,说明 Docker GPU runtime 基本没问题。


三、先释放 GPU 显存

这台服务器原来已经跑了一个 GLM-OCR 的 vLLM 容器,占用了大量显存。

查看显存:

nvidia-smi

当时能看到 T4 16GB 基本已经满了,主要被已有的 glm-ocr-vllm 占用。

所以先停掉旧的 OCR vLLM 容器:

sudo docker stop glm-ocr-vllm

再次检查显存:

nvidia-smi

显存释放之后,再部署 reranker。

这里有一个经验: 同一张 16GB T4 上,不建议同时跑 GLM-OCR vLLM + reranker + 其他 GPU 服务。

如果要长期共存,建议升级到:

最低:24GB 显存
推荐:32GB 显存
更省心:48GB 显存

四、第一次尝试:直接用 vLLM 镜像启动

一开始我直接用下面的方式启动:

sudo docker run -d \
  --name WeKnora-vllm-reranker \
  --gpus all \
  --restart unless-stopped \
  -p 18082:8000 \
  -v vllm_hf_cache:/root/.cache/huggingface \
  -e HF_HOME=/root/.cache/huggingface \
  vllm/vllm-openai:latest \
  BAAI/bge-reranker-v2-m3 \
  --served-model-name BAAI/bge-reranker-v2-m3 \
  --pooler-config.task classify \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype half

几个关键参数解释:

--name WeKnora-vllm-reranker

指定容器名称,方便后续查看日志、重启、删除。

--gpus all

允许容器使用 GPU。

--restart unless-stopped

服务器重启后自动拉起容器,除非手动 stop。

-p 18082:8000

宿主机 18082 端口映射到容器内 8000 端口。 所以外部访问地址是:

http://服务器IP:18082
-v vllm_hf_cache:/root/.cache/huggingface

把 Hugging Face 模型缓存放到 Docker volume,避免容器删除后重新下载。

BAAI/bge-reranker-v2-m3

这是要加载的模型。

--served-model-name BAAI/bge-reranker-v2-m3

指定对外暴露的模型名。后面请求 /v1/rerank 时,JSON 里的 model 要和它一致。

--pooler-config.task classify

这是这次部署的关键参数。 vLLM 0.24.0 中,这个 reranker 会被识别为 XLMRobertaForSequenceClassification,并以 pooling/classify 方式启动。

--dtype half

使用 FP16,降低显存占用。T4 上不建议用 BF16。


五、第一个坑:Hugging Face 网络不可达

第一次启动后查看日志:

sudo docker logs -f WeKnora-vllm-reranker

报错类似:

Network is unreachable
requesting HEAD https://huggingface.co/BAAI/bge-reranker-v2-m3/resolve/main/config.json

也就是说容器访问 Hugging Face 失败,导致模型配置文件 config.json 下载不了。

解决方式是加镜像源:

-e HF_ENDPOINT=https://hf-mirror.com

也就是:

sudo docker run -d \
  --name WeKnora-vllm-reranker \
  --gpus all \
  --restart unless-stopped \
  --ipc=host \
  -p 18082:8000 \
  -v vllm_hf_cache:/root/.cache/huggingface \
  -e HF_HOME=/root/.cache/huggingface \
  -e HF_ENDPOINT=https://hf-mirror.com \
  vllm/vllm-openai:latest \
  BAAI/bge-reranker-v2-m3 \
  --served-model-name BAAI/bge-reranker-v2-m3 \
  --pooler-config.task classify \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype half

这里还加了:

--ipc=host

这是 vLLM Docker 部署里常用的参数,可以避免容器共享内存不足导致的问题。


六、第二个坑:不要写 --device auto

一开始我还写过:

--device auto

结果 vLLM 0.24.0 把 auto 当成了 GPU ID 去解析,最后报错:

ValueError: invalid literal for int() with base 10: 'auto'
NVMLError_NotFound: Not Found

解决方式很简单: 直接删掉 --device auto

只要 Docker 已经通过:

--gpus all

把 GPU 暴露给容器,vLLM 会自己识别 CUDA 设备。


七、第三个坑:vLLM 默认想预留 92% 显存

后面模型能解析了,但又报显存错误:

Free memory on device cuda:0 (11.23/14.56 GiB) on startup is less than desired GPU memory utilization (0.92, 13.4 GiB).
Decrease GPU memory utilization or reduce GPU memory used by other processes.

这不是模型本身一定需要 13GB,而是 vLLM 默认按 0.92 的比例规划 GPU 显存。

当前服务器还有其他 GPU 进程,所以剩余显存只有 11GB 左右,不够 vLLM 默认预留。

解决方式是降低显存预留比例:

--gpu-memory-utilization 0.65

完整启动命令:

sudo docker run -d \
  --name WeKnora-vllm-reranker \
  --gpus all \
  --restart unless-stopped \
  --ipc=host \
  -p 18082:8000 \
  -v vllm_hf_cache:/root/.cache/huggingface \
  -e HF_HOME=/root/.cache/huggingface \
  -e HF_ENDPOINT=https://hf-mirror.com \
  vllm/vllm-openai:latest \
  BAAI/bge-reranker-v2-m3 \
  --served-model-name BAAI/bge-reranker-v2-m3 \
  --pooler-config.task classify \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype half \
  --gpu-memory-utilization 0.65

八、第四个坑:容器一直 running,但 API 不可用

有一次容器状态是 running:

sudo docker ps -a | grep WeKnora-vllm-reranker

但是访问健康检查:

curl -sS -m 5 -i http://127.0.0.1:18082/health

返回:

Recv failure: Connection reset by peer

这说明端口上有进程,但 vLLM API 还没有真正 ready。

进一步看状态:

sudo docker stats --no-stream WeKnora-vllm-reranker

发现 CPU 几乎不动,内存也只有几百 MB。再看缓存:

du -sh /root/.cache/huggingface

只有几十 MB。

这说明模型权重没有完整下载,vLLM 卡在初始化阶段。

最终采用更稳的方式:先手工下载模型到宿主机目录,再让 vLLM 从本地路径加载。


九、推荐方式:先下载模型,再本地加载

创建模型目录:

sudo mkdir -p /data/models/bge-reranker-v2-m3
sudo chown -R jms:jms /data/models/bge-reranker-v2-m3

注意,vllm/vllm-openai 镜像默认 entrypoint 是 vllm,所以如果直接写:

vllm/vllm-openai:latest bash -lc '...'

会被当成 vLLM 子命令执行。

正确方式是覆盖 entrypoint:

sudo docker run --rm -it \
  --entrypoint bash \
  --network host \
  -v /data/models/bge-reranker-v2-m3:/models/bge-reranker-v2-m3 \
  -e HF_ENDPOINT=https://hf-mirror.com \
  -e HF_HUB_DISABLE_XET=1 \
  vllm/vllm-openai:latest \
  -lc 'hf download BAAI/bge-reranker-v2-m3 --local-dir /models/bge-reranker-v2-m3'

这里有几个重点:

--entrypoint bash

让容器真正执行 bash,而不是执行默认的 vLLM entrypoint。

--network host

下载模型时直接使用宿主机网络。

-e HF_ENDPOINT=https://hf-mirror.com

走 Hugging Face 镜像源。

-e HF_HUB_DISABLE_XET=1

禁用 Xet 下载机制,减少网络环境不稳定时卡住的概率。

hf download BAAI/bge-reranker-v2-m3 --local-dir /models/bge-reranker-v2-m3

使用新版 hf 命令下载模型。 新版镜像里 huggingface-cli 已经提示废弃,应该使用 hf download

下载完成后,我这里显示:

13 files
2.29G/2.29G
Download complete

检查模型目录:

ls -lh /data/models/bge-reranker-v2-m3
du -sh /data/models/bge-reranker-v2-m3
find /data/models/bge-reranker-v2-m3 -maxdepth 2 -type f | sort

正常应该能看到类似:

config.json
model.safetensors
tokenizer.json
tokenizer_config.json
sentencepiece.bpe.model

十、最终启动命令

模型下载完成后,从本地目录启动:

sudo docker rm -f WeKnora-vllm-reranker 2>/dev/null || true

sudo docker run -d \
  --name WeKnora-vllm-reranker \
  --gpus all \
  --restart unless-stopped \
  --ipc=host \
  -p 18082:8000 \
  -v /data/models/bge-reranker-v2-m3:/models/bge-reranker-v2-m3:ro \
  vllm/vllm-openai:latest \
  /models/bge-reranker-v2-m3 \
  --served-model-name BAAI/bge-reranker-v2-m3 \
  --pooler-config.task classify \
  --host 0.0.0.0 \
  --port 8000 \
  --dtype half \
  --gpu-memory-utilization 0.65

这次不再让 vLLM 在线下载模型,而是从:

/models/bge-reranker-v2-m3

加载本地模型。

宿主机目录是:

/data/models/bge-reranker-v2-m3

容器内只读挂载:

-v /data/models/bge-reranker-v2-m3:/models/bge-reranker-v2-m3:ro

十一、启动成功日志

查看日志:

sudo docker logs -f WeKnora-vllm-reranker

成功后可以看到类似:

Supported tasks: ['classify', 'token_classify']
Starting vLLM server on http://0.0.0.0:8000
Route: /v1/models, Methods: GET
Route: /rerank, Methods: POST
Route: /v1/rerank, Methods: POST
Route: /v2/rerank, Methods: POST
Application startup complete.

这几个信息很关键:

/v1/rerank 路由存在
vLLM 服务启动完成
模型以 classify 任务方式加载成功

十二、验证接口

1. 健康检查

curl -sS -i http://127.0.0.1:18082/health

预期:

HTTP/1.1 200 OK

2. 查看模型列表

curl -sS http://127.0.0.1:18082/v1/models

返回中应该能看到:

{
  "id": "BAAI/bge-reranker-v2-m3",
  "root": "/models/bge-reranker-v2-m3",
  "max_model_len": 8192
}

这说明服务端模型名是:

BAAI/bge-reranker-v2-m3

后续 WeKnora 配置里的模型名必须和它一致。

3. 测试 rerank

curl -sS http://127.0.0.1:18082/v1/rerank \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer local-vllm' \
  -d '{
    "model": "BAAI/bge-reranker-v2-m3",
    "query": "WeKnora 是什么",
    "documents": [
      "WeKnora 是一个知识库问答和 RAG 系统。",
      "今天上海天气晴朗。",
      "bge-reranker-v2-m3 是一个跨编码器重排模型。"
    ],
    "return_documents": true
  }'

实际返回类似:

{
  "id": "score-a24ae928d7467ad5",
  "model": "BAAI/bge-reranker-v2-m3",
  "usage": {
    "prompt_tokens": 72,
    "total_tokens": 72
  },
  "results": [
    {
      "index": 0,
      "document": {
        "text": "WeKnora 是一个知识库问答和 RAG 系统。",
        "multi_modal": null
      },
      "relevance_score": 0.9996455907821655
    },
    {
      "index": 2,
      "document": {
        "text": "bge-reranker-v2-m3 是一个跨编码器重排模型。",
        "multi_modal": null
      },
      "relevance_score": 0.0003760864201467484
    },
    {
      "index": 1,
      "document": {
        "text": "今天上海天气晴朗。",
        "multi_modal": null
      },
      "relevance_score": 0.0000161083517014049
    }
  ]
}

这个排序是符合预期的。

对于问题:

WeKnora 是什么

最相关的是:

WeKnora 是一个知识库问答和 RAG 系统。

天气这条被排到最后。

说明 reranker 正常工作。


十三、本地 WeKnora 配置

因为 reranker 服务部署在远程服务器,WeKnora 只需要调用 HTTP API。

配置如下:

RERANK_MODEL_NAME=BAAI/bge-reranker-v2-m3
RERANK_BASE_URL=http://服务器IP:18082/v1
RERANK_API_KEY=local-vllm
RERANK_PROVIDER=generic

比如:

RERANK_MODEL_NAME=BAAI/bge-reranker-v2-m3
RERANK_BASE_URL=http://10.72.95.168:18082/v1
RERANK_API_KEY=local-vllm
RERANK_PROVIDER=generic

注意:

RERANK_BASE_URL 要写到 /v1

因为 WeKnora 通常会请求:

${RERANK_BASE_URL}/rerank

所以最终路径会变成:

http://10.72.95.168:18082/v1/rerank

如果 WeKnora 有 SSRF 白名单,还需要加服务器 IP:

SSRF_WHITELIST=10.72.95.168

如果本地 WeKnora 是 Docker 容器,还要确认容器内部能访问服务器:

curl -sS -i http://10.72.95.168:18082/health

十四、常用维护命令

查看容器:

sudo docker ps -a | grep WeKnora-vllm-reranker

查看日志:

sudo docker logs -f WeKnora-vllm-reranker

查看最近日志:

sudo docker logs --tail=120 WeKnora-vllm-reranker

查看 GPU:

nvidia-smi

停止 reranker:

sudo docker stop WeKnora-vllm-reranker

启动 reranker:

sudo docker start WeKnora-vllm-reranker

删除 reranker 容器:

sudo docker rm -f WeKnora-vllm-reranker

因为启动时加了:

--restart unless-stopped

所以服务器重启后,reranker 容器会自动启动。


十五、这次踩坑总结

1. Docker 需要 sudo 权限

普通用户执行:

docker run ...

可能报:

permission denied while trying to connect to the Docker daemon socket

解决:

sudo docker ...

或者把用户加入 docker 组。


2. GPU 显存要先看清楚

部署前一定先执行:

nvidia-smi

如果显存已经被其他服务占满,reranker 起不来。

这次 T4 16GB 上原本已有 GLM-OCR vLLM,占了 11GB 以上,必须先停掉。


3. vLLM 0.24 不要写 --device auto

--device auto 可能被当成 GPU ID 解析,导致:

NVMLError_NotFound

直接删掉即可。


4. 网络不稳定时,不要依赖 vLLM 启动时自动下载模型

更稳的方式是:

先 hf download 到宿主机
再从本地目录启动 vLLM

这比让 vLLM 边启动边下载稳定很多。


5. huggingface-cli 已废弃,使用 hf download

新版镜像里会提示:

huggingface-cli is deprecated
Use hf instead

所以下载命令应该写:

hf download BAAI/bge-reranker-v2-m3 --local-dir /models/bge-reranker-v2-m3

6. vllm/vllm-openai 镜像默认 entrypoint 是 vLLM

如果要在镜像里执行 bash,需要加:

--entrypoint bash

否则命令可能会被当成 vLLM 子命令解析。


7. 显存预留比例需要调低

vLLM 默认 gpu_memory_utilization 约为 0.92,在已有其他 GPU 进程时容易失败。

可以改成:

--gpu-memory-utilization 0.65

如果还不够,可以继续降到:

--gpu-memory-utilization 0.55

十六、最终结论

这次最终成功的方案是:

手工下载 BAAI/bge-reranker-v2-m3 到 /data/models
用 vLLM 从本地目录加载模型
通过 18082 端口暴露 /v1/rerank
本地 WeKnora 配置远程 RERANK_BASE_URL

最终服务地址:

http://服务器IP:18082/v1/rerank

最终模型名:

BAAI/bge-reranker-v2-m3

最终 WeKnora 配置:

RERANK_MODEL_NAME=BAAI/bge-reranker-v2-m3
RERANK_BASE_URL=http://服务器IP:18082/v1
RERANK_API_KEY=local-vllm
RERANK_PROVIDER=generic

部署完成后,WeKnora 的检索链路就变成:

用户问题
  -> 向量召回 TopN 文档片段
  -> bge-reranker-v2-m3 重新打分排序
  -> 选出更相关的上下文
  -> LLM 生成最终答案

从工程角度看,Reranker 不是“让大模型更聪明”,而是让大模型拿到的上下文更准。 对于知识库问答和 RAG 系统来说,这一步很值得单独部署。

你可能还感兴趣