基于 VLLM 部署 Qwen3-Embedding 与 Reranker 实践

在构建现代检索增强生成(RAG)系统时,高效的文本嵌入和精准的相关性重排序能力是决定最终效果的关键。传统方案往往依赖 HuggingFace Transformers 搭配自定义服务逻辑,但面临推理延迟高、吞吐低、显存占用大等问题。

vLLM 的出现改变了这一局面——其核心的 PagedAttention 技术实现了类似操作系统的虚拟内存管理机制,极大提升了长序列处理效率,配合连续批处理(Continuous Batching),在 embedding 和 reranking 场景下表现出远超常规实现的性能优势。

本文将聚焦实战部署流程,使用 vLLM 快速上线通义千问 Qwen3 系列中的两个轻量级专用模型:

  • Qwen3-Embedding-0.6B:用于生成高质量语义向量
  • Qwen3-Reranker-0.6B:专为 query-document 相关性打分设计

整个过程不依赖复杂框架改造,只需标准 Docker 环境与基础 Python 调用脚本,即可完成从本地测试到生产就绪的过渡。


环境准备

操作系统建议使用 Ubuntu 20.04 LTS 或更高版本,确保 CUDA 驱动正常安装。推荐硬件配置如下:

  • GPU:NVIDIA RTX 3090 / A10G 及以上(支持 FP16 计算)
  • vLLM 版本:0.6.0+
  • Python:3.10+
  • CUDA:11.8 或 12.x

⚠️ 本文基于模力方舟平台预置的 vLLM 高性能推理镜像 进行演示,该镜像已集成以下关键优化:

  • 内置 PagedAttention,实现高效显存调度
  • 支持动态批处理与请求优先级调度
  • 预装 OpenAI 兼容 API Server,开箱即用
  • 支持 GPTQ/AWQ 量化模型加载
  • 吞吐量相较原生 Transformers 提升 5–10 倍

拉取并运行容器:

docker run -d --gpus all \
  --name vllm-qwen-rerank \
  -p 8000:8000 -p 8001:8001 \
  molesphere/vllm:latest

进入容器后安装必要依赖:

pip install modelscope openai requests loguru

模型下载

我们选用通义千问 Qwen3 系列中两个专用小模型,兼顾效果与推理成本:

  1. Qwen3-Embedding-0.6B:适用于文本编码、检索、聚类等任务
  2. Qwen3-Reranker-0.6B:双塔结构分类器,输出 query-doc 相关性得分

通过 modelscope 工具下载至本地目录:

modelscope download --model Qwen/Qwen3-Embedding-0.6B --local_dir ./models/Qwen3-Embedding-0.6B
modelscope download --model Qwen/Qwen3-Reranker-0.6B --local_dir ./models/Qwen3-Reranker-0.6B

预期目录结构如下:

./models/
├── Qwen3-Embedding-0.6B/
│   ├── config.json
│   ├── model.safetensors
│   └── tokenizer.json
└── Qwen3-Reranker-0.6B/
    ├── config.json
    ├── model.safetensors
    └── tokenizer.json

启动 Embedding 服务

使用 vLLM 内建命令启动嵌入模型服务,需指定 --task embed 以启用 embedding 模式:

VLLM_USE_V1=0 vllm serve ./models/Qwen3-Embedding-0.6B/ \
    --host 0.0.0.0 \
    --port 8000 \
    --task embed \
    --trust-remote-code \
    --dtype half \
    --gpu-memory-utilization 0.9 \
    --max-model-len 8192

参数说明:

参数 作用
--task embed 开启 embedding 输出模式
--trust-remote-code 允许加载 Qwen 自定义模型结构
--dtype half 使用 float16 加速推理
--gpu-memory-utilization 0.9 控制显存使用上限,避免 OOM
--max-model-len 8192 支持最长 8K 上下文

服务启动后,默认暴露 OpenAI 兼容接口:

http://localhost:8000/v1/embeddings

Python 调用 Embedding 接口

利用 openai 客户端库可无缝对接 vLLM 提供的服务:

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project

from openai import OpenAI
import numpy as np
from loguru import logger

openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8000/v1"

def main():
    client = OpenAI(api_key=openai_api_key, base_url=openai_api_base)

    # 获取模型名
    models = client.models.list()
    model_name = models.data[0].id
    logger.info(f"Loaded embedding model: {model_name}")

    # 输入样本
    inputs = [
        "中国的首都是北京",
        "牛顿提出了万有引力定律",
        "机器学习是人工智能的一个分支"
    ]

    response = client.embeddings.create(
        input=inputs,
        model=model_name,
        encoding_format="float"
    )

    # 打印维度及前10个值验证
    for i, data in enumerate(response.data):
        embedding = data.embedding
        logger.info(f"Input[{i}]: '{inputs[i]}'")
        logger.info(f"Embedding dim: {len(embedding)}, First 10: {embedding[:10]}")

if __name__ == "__main__":
    main()

✅ 成功调用将返回长度为 1024 的浮点向量(具体由模型决定),可用于插入向量数据库或计算余弦相似度。


启动 Reranker 服务

Reranker 模型为双文本打分架构,不能按普通语言模型方式加载。根据 vLLM 文档 - Sentence Pair Scoring 要求,必须通过 --hf_overrides 注入元信息。

启动命令如下:

VLLM_USE_V1=0 vllm serve ./models/Qwen3-Reranker-0.6B/ \
    --host 0.0.0.0 \
    --port 8001 \
    --task score \
    --served_model_name qwen3_reranker \
    --trust-remote-code \
    --dtype half \
    --gpu-memory-utilization 0.9 \
    --max-model-len 4096 \
    --hf_overrides '{
        "architectures": ["Qwen3ForSequenceClassification"],
        "classifier_from_token": ["no", "yes"],
        "is_original_qwen3_reranker": true
    }'

关键参数解析:

  • --task score:启用 sentence pair scoring 模式
  • --hf_overrides:覆盖原始模型配置,适配 Qwen3 Reranker 架构
  • classifier_from_token:定义输出标签对应 token,模型返回 "yes"/"no" 概率分布
  • is_original_qwen3_reranker:激活官方 prompt 构造逻辑

服务启动后,评分接口地址为:

http://localhost:8001/score

Python 调用 Reranker 接口

由于 vLLM 尚未提供原生 SDK 支持 reranker 打分,需直接使用 requests 发起 POST 请求。

以下是完整调用示例,包含标准 prompt 构造逻辑:

import requests
from typing import List, Tuple

url = "http://localhost:8001/score"
MODEL_NAME = "qwen3_reranker"

# 官方推荐 prompt 模板
PREFIX = '<|im_start|>system\nJudge whether the Document meets the requirements based on the Query and the Instruct provided. Note that the answer can only be "yes" or "no".<|im_end|>\n<|im_start|>user\n'
SUFFIX = "<|im_end|>\n<|im_start|>assistant\n<think>\n\n</think>\n\n"

QUERY_TEMPLATE = "{prefix}<Instruct>: {instruction}\n<Query>: {query}\n"
DOC_TEMPLATE = "<Document>: {doc}{suffix}"

INSTRUCTION = "Given a web search query, retrieve relevant passages that answer the query"

def format_pair(query: str, doc: str) -> Tuple[str, str]:
    formatted_query = QUERY_TEMPLATE.format(
        prefix=PREFIX, instruction=INSTRUCTION, query=query
    )
    formatted_doc = DOC_TEMPLATE.format(doc=doc, suffix=SUFFIX)
    return formatted_query, formatted_doc

def rerank(queries: List[str], docs: List[str]) -> List[float]:
    assert len(queries) == len(docs), "Queries and documents must have same length"

    formatted_pairs = [format_pair(q, d) for q, d in zip(queries, docs)]
    text_1 = [pair[0] for pair in formatted_pairs]
    text_2 = [pair[1] for pair in formatted_pairs]

    response = requests.post(
        url,
        json={
            "model": MODEL_NAME,
            "text_1": text_1,
            "text_2": text_2,
            "truncate_prompt_tokens": -1,
        }
    ).json()

    if "error" in response:
        raise RuntimeError(f"Reranking failed: {response['error']}")

    # 提取 'yes' 类别的得分(即相关性分数)
    scores = [item['score'] for item in response['scores']]
    return scores

# === 测试示例 ===

if __name__ == "__main__":
    test_queries = [
        "什么是光合作用?",
        "如何做红烧肉?",
        "太阳系有几大行星?"
    ]

    test_docs = [
        "光合作用是植物利用阳光将二氧化碳和水转化为有机物和氧气的过程。",
        "红烧肉是一道经典的中式菜肴,主要原料是五花肉。",
        "冥王星被降级后,太阳系目前公认有八大行星。"
    ]

    try:
        results = rerank(test_queries, test_docs)
        for i, (q, d, s) in enumerate(zip(test_queries, test_docs, results)):
            print(f"[{i+1}] Score: {s:.4f} | Query: {q}")
            print(f"      Doc: {d}\n")
    except Exception as e:
        print("Error during reranking:", str(e))

✅ 示例输出:

[1] Score: 0.9872 | Query: 什么是光合作用? Doc: 光合作用是植物利用阳光将二氧化碳和水转化为有机物和氧气的过程。

分数越接近 1 表示文档与查询越相关,适合用于对召回结果进行精细化排序。


生产建议与优化技巧

虽然上述流程已在开发环境验证可行,但在实际生产部署中还需进一步优化稳定性与资源利用率。

1. 批量处理与并发控制

充分利用 vLLM 的连续批处理能力,提升 GPU 利用率:

  • 设置合理的 --max-num-seqs(如 256)以容纳更多并发请求
  • 调整 --max-num-batched-tokens 匹配典型输入长度(例如设为 4096)

对于高吞吐场景,建议客户端启用批量提交策略,合并多个小请求。

2. 模型量化降低资源消耗

若显存受限,可将模型转换为 GPTQ 或 AWQ 4bit 量化版本:

vllm serve ./models/Qwen3-Embedding-0.6B-GPTQ/ \
    --quantization gptq \
    --task embed \
    --dtype half \
    ...

注意:量化版本需提前转换并确认权重文件完整性。

3. API 网关与安全防护

在生产环境中不应直接暴露 vLLM 服务端口,建议添加反向代理层:

  • 使用 Nginx/Kong 实现限流、鉴权、日志记录
  • 结合 JWT 或 API Key 实现访问控制
  • 添加 HTTPS 加密通信

例如,在 Nginx 中配置基本认证与速率限制:

location /v1/embeddings {
    limit_req zone=one burst=10;
    proxy_pass http://localhost:8000/v1/embeddings;
    proxy_set_header Authorization $http_authorization;
}

4. 监控与可观测性

开启 Prometheus 指标采集,监控关键性能指标:

  • 请求延迟(P50/P95/P99)
  • 每秒请求数(QPS)
  • GPU 显存使用率
  • 批处理平均大小

vLLM 默认暴露 /metrics 接口,可轻松接入 Grafana + Prometheus 栈。

此外,建议在业务代码中添加日志埋点,记录异常响应与慢请求,便于问题排查。


这种高度集成的部署方式,不仅显著降低了运维复杂度,也让中小团队能够以极低成本构建高性能 RAG 系统。随着 vLLM 对更多专用模型的支持不断完善,未来还可探索将其应用于指令微调、摘要生成等多样化任务,持续释放大模型推理潜力。

Logo

免费领 200 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐