vLLM 部署 RAG 检索增强生成系统:基于 LangChain 与 LlamaIndex 的 Milvus 向量库集成实战
本文基于 vLLM 官方文档 Retrieval-Augmented Generation 与仓库内两个 RAG 示例脚本(LangChain 版、LlamaIndex 版),完整讲解如何用 vLLM 同时承载 Embedding 服务与 Chat 服务,配合 Milvus 向量库搭建一套可运行的检索增强生成(RAG)流水线。读完后你将掌握:双服务部署方式、两套框架的完整命令行参数、文档加载与分块策略,以及 vLLM OpenAI 兼容接口在 RAG 场景下的调用方式。
什么是 RAG,以及 vLLM 在其中的角色
Retrieval-Augmented Generation(RAG,检索增强生成) 是一种让生成式 AI 模型能够检索并融入新信息的技术。它改变 LLM 的交互方式:模型在回答用户问题时,会参考一组指定文档,用这些信息补充模型预训练数据中已有的知识,从而使用领域特定信息或最新信息。典型用例包括:让聊天机器人访问公司内部数据,或基于权威来源生成回答。
vLLM 在这条流水线中承担两个 OpenAI 兼容的推理服务:
- Embedding 服务(默认端口 8000):以
/v1/embeddings接口提供文本向量化。从源码看,该路由定义在 vllm/entrypoints/pooling/embed/api_router.py 中,POST /v1/embeddings请求经validate_json_request校验后交给ServingEmbedding处理器,因此任意 OpenAI 客户端库(如langchain_openai.OpenAIEmbeddings、llama_index.embeddings.openai_like.OpenAILikeEmbedding)只需把 base URL 指向 vLLM 即可工作。 - Chat 服务(默认端口 8001):以
/v1/chat/completions接口提供基于检索上下文的生成回答。
仓库提供的两种集成方案为:
- vLLM + LangChain + Milvus
- vLLM + LlamaIndex + Milvus
两种方案的服务端部署完全一致,区别仅在客户端编排框架。
方案一:vLLM + LangChain + Milvus
1. 环境准备
安装 vLLM 与 LangChain 相关依赖:
pip install -U vllm \
langchain_milvus langchain_openai \
langchain_community beautifulsoup4 \
langchain-text-splitters
其中 langchain_community(提供 WebBaseLoader 网页加载器)、beautifulsoup4(HTML 解析)与 langchain-text-splitters(RecursiveCharacterTextSplitter 分块器)都是示例脚本的显式依赖,缺装会在 import 阶段报错。
2. 部署两个 vLLM 服务
# Start embedding service (port 8000)
vllm serve ssmits/Qwen2-7B-Instruct-embed-base
# Start chat service (port 8001)
vllm serve qwen/Qwen1.5-0.5B-Chat --port 8001
注意两个要点:
- Embedding 模型与 Chat 模型分别运行在独立的 vLLM 实例上,端口分别为 8000 与 8001,客户端通过不同的 base URL 区分它们;
- 首次运行可能需要较长时间下载模型权重(脚本注释中亦提示了这一点)。
3. 运行示例脚本
脚本位于 examples/applications/rag/retrieval_augmented_generation_with_langchain.py:
python retrieval_augmented_generation_with_langchain.py
不带参数时,脚本会加载默认 URL 的文档、建库,并用固定问题 "How to install vLLM?" 做一次问答。
4. 完整命令行参数说明
脚本通过 argparse 暴露了全部可配置项(定义见 get_parser(),见 脚本 L136-L191):
| 参数 | 简写 | 默认值 | 说明 |
|---|---|---|---|
--vllm-api-key |
- | EMPTY |
vLLM 兼容服务的 API key(vLLM 默认不校验,故可填 EMPTY) |
--vllm-embedding-endpoint |
- | http://localhost:8000/v1 |
Embedding 服务 base URL |
--vllm-chat-endpoint |
- | http://localhost:8001/v1 |
Chat 服务 base URL |
--uri |
- | ./milvus.db |
Milvus 数据库 URI(本地 SQLite 文件即轻量版 Milvus Lite) |
--url |
- | vLLM 官方 quickstart 文档页 | 待处理的文档 URL |
--embedding-model |
- | ssmits/Qwen2-7B-Instruct-embed-base |
向量化模型名 |
--chat-model |
- | qwen/Qwen1.5-0.5B-Chat |
对话模型名 |
-i / --interactive |
- | 关闭 | 开启交互式问答模式(输入 q/quit 退出) |
-k / --top-k |
- | 3 |
每次检索返回的文档块数量 |
-c / --chunk-size |
- | 1000 |
文档分块大小(字符数) |
-o / --chunk-overlap |
- | 200 |
相邻分块重叠大小 |
例如处理自定义网页并以交互模式问答:
python retrieval_augmented_generation_with_langchain.py \
--url https://example.com/your-doc.html \
-i -k 4
5. 脚本内部流程(源码剖析)
脚本的 main() 按顺序执行四个阶段,每一阶段都对应 RAG 流水线的标准环节:
- 文档加载与分块(
load_and_split_documents):用WebBaseLoader抓取网页正文,再用RecursiveCharacterTextSplitter(chunk_size, chunk_overlap)递归切分。分块参数直接影响检索粒度——块太大则向量语义被稀释,块太小则上下文不足,默认 1000/200 是常用起点。 - 向量库初始化(
init_vectorstore):Milvus.from_documents(..., drop_old=True)将分块向量化后写入 Milvus;OpenAIEmbeddings的openai_api_base指向 vLLM 的 8000 端口,即所有 embedding 请求实际由 vLLM 计算。drop_old=True保证重复运行时旧集合被清空。 - LLM 初始化(
init_llm):ChatOpenAI指向 8001 端口的 vLLM Chat 服务。 - QA 链组装(
create_qa_chain):
return (
{
"context": retriever | format_docs,
"question": RunnablePassthrough(),
}
| prompt
| llm
| StrOutputParser()
)
这是一个典型的 LangChain 可运行(Runnable)管道:检索器取回 top_k 个文档块并由 format_docs 以 \n\n 拼接为 context,与 question 一起填入 QA prompt 模板(模板要求"若不知道答案就直说不知道,回答不超过三句"),最后经 LLM 生成并以字符串输出。
方案二:vLLM + LlamaIndex + Milvus
1. 环境准备
pip install vllm \
llama-index llama-index-readers-web \
llama-index-llms-openai-like \
llama-index-embeddings-openai-like \
llama-index-vector-stores-milvus \
其中 llama-index-readers-web 提供 SimpleWebPageReader,llama-index-llms-openai-like / llama-index-embeddings-openai-like 提供 OpenAI 兼容端点的 LLM 与 Embedding 客户端,llama-index-vector-stores-milvus 提供 MilvusVectorStore。
2. 部署 vLLM 服务
与方案一完全相同,两个服务的启动命令保持不变:
# Start embedding service (port 8000)
vllm serve ssmits/Qwen2-7B-Instruct-embed-base
# Start chat service (port 8001)
vllm serve qwen/Qwen1.5-0.5B-Chat --port 8001
3. 运行示例脚本
脚本位于 examples/applications/rag/retrieval_augmented_generation_with_llamaindex.py:
python retrieval_augmented_generation_with_llamaindex.py
4. 完整命令行参数说明
参数定义见 脚本 L120-L175:
| 参数 | 简写 | 默认值 | 说明 |
|---|---|---|---|
--url |
- | vLLM 官方 quickstart 文档页 | 待处理的文档 URL |
--embedding-model |
- | ssmits/Qwen2-7B-Instruct-embed-base |
向量化模型名 |
--chat-model |
- | qwen/Qwen1.5-0.5B-Chat |
对话模型名 |
--vllm-api-key |
- | EMPTY |
vLLM 兼容服务的 API key |
--embedding-endpoint |
- | http://localhost:8000/v1 |
Embedding 服务 base URL |
--chat-endpoint |
- | http://localhost:8001/v1 |
Chat 服务 base URL |
--db-path |
- | ./milvus_demo.db |
Milvus 数据库文件路径 |
-i / --interactive |
- | 关闭 | 开启交互式问答模式(输入 quit/exit/q 退出) |
-c / --chunk-size |
- | 1000 |
文档分块大小 |
-o / --chunk-overlap |
- | 200 |
相邻分块重叠大小 |
-k / --top-k |
- | 3 |
每次检索返回的文档块数量 |
5. 脚本内部流程(源码剖析)
main() 的执行顺序为:加载文档 → 配置模型 → 初始化向量库 → 建索引 → 查询。
- 加载文档(
load_documents):SimpleWebPageReader(html_to_text=True)将网页 HTML 转为纯文本节点,与 LangChain 版中WebBaseLoader的作用对应。 - 配置模型(
setup_models):把OpenAILikeEmbedding与OpenAILike挂到 LlamaIndex 的全局Settings上,两者的api_base分别指向 8000/8001 端口的 vLLM 服务;同时把SentenceSplitter(chunk_size, chunk_overlap)设为默认变换管道,在索引时完成分块。值得注意的是OpenAILike客户端硬编码了context_window=128000并声明is_chat_model=True,从源码结构看这是为 LlamaIndex 的提示词填充逻辑预留的上下文窗口声明,与底层 Qwen1.5-0.5B 的实际窗口无直接关系。 - 初始化向量库(
setup_vector_store):
sample_emb = Settings.embed_model.get_text_embedding("test")
print(f"Embedding dimension: {len(sample_emb)}")
return MilvusVectorStore(uri=db_path, dim=len(sample_emb), overwrite=True)
这是一个很实用的技巧:先向 vLLM Embedding 服务请求一条测试文本的向量,用返回向量的长度动态确定 dim 再建库,避免手工指定维度与模型不匹配。
- 建索引(
create_index):通过StorageContext.from_defaults(vector_store=...)把 Milvus 向量库注入VectorStoreIndex.from_documents。 - 查询(
query_document):index.as_query_engine(similarity_top_k=top_k)生成查询引擎,按相似度取回 top-k 个节点后由 LLM 生成回答。
两套方案的对比与共同要点
| 维度 | LangChain 方案 | LlamaIndex 方案 |
|---|---|---|
| 网页加载 | WebBaseLoader(langchain_community) |
SimpleWebPageReader(llama-index-readers-web) |
| 分块器 | RecursiveCharacterTextSplitter |
SentenceSplitter |
| 向量库 API | Milvus.from_documents(drop_old=True) |
MilvusVectorStore(overwrite=True) |
| 维度获取 | 由 Milvus.from_documents 内部处理 |
显式请求一条样本向量推断 dim |
| 检索入口 | vectorstore.as_retriever(search_kwargs={"k": top_k}) |
index.as_query_engine(similarity_top_k=top_k) |
| 向量库文件 | ./milvus.db |
./milvus_demo.db |
两者在 vLLM 侧的行为完全一致:客户端都只依赖 OpenAI 兼容的 /v1/embeddings 与 /v1/chat/completions 接口,vLLM 服务无需任何 RAG 相关定制。这也说明该部署方式可以平滑替换到你自己已有的 RAG 应用中——只需把原 OpenAI 的 base_url 改为 vLLM 端点,api_key 填 EMPTY 即可。
常见问题排查
- 端口冲突:若 8000/8001 已被占用,用
--port启动第二个 vLLM 实例,并同步修改脚本的--vllm-embedding-endpoint/--vllm-chat-endpoint(LlamaIndex 版)或对应参数。 - 模型不支持 Embedding:
/v1/embeddings路由在 vllm/entrypoints/pooling/embed/api_router.py 中对不支持 Embeddings API 的模型会抛出NotImplementedError。请确认 embedding 端点启动的是 embedding 模型而非纯生成模型。 - 首次运行慢:脚本注释已说明首次运行可能需要较长时间下载模型;另需注意脚本默认 URL 为 vLLM 文档站页面,网络不通时会因
WebBaseLoader/SimpleWebPageReader抓取失败而抛出异常(LangChain 版会打印Error loading document from <url>后重新抛出)。 - 检索质量不佳:优先调整
-k(top-k)、-c(chunk-size)与-o(chunk-overlap)三个参数,它们共同决定送入 prompt 的上下文规模与相关性。
小结
vLLM 的 RAG 集成遵循"服务端只做推理、编排交给框架"的清晰分工:两个 vllm serve 实例分别提供向量化与对话能力,LangChain 或 LlamaIndex 负责网页抓取、分块、Milvus 存取与提示词拼装,Milvus(本地 SQLite 文件形态)负责持久化向量索引。仓库内两个示例脚本提供了完整的可运行起点,所有关键行为(端点地址、模型名、分块与检索参数)均可通过命令行覆盖,便于按需扩展为内部知识库问答系统。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00