☰
基于DeepSeek搭建RAG系统:环境配置与避坑实战指南
2026/9/29 23:05:52 网站建设 项目流程

简介:这份教程面向希望快速上手检索增强生成系统的开发者与运维人员,围绕DeepSeek模型,完整讲解RAG系统从技术栈认知到多机部署的落地路径。内容涵盖CUDA并行计算、vLLM大模型推理加速与Docker容器化部署等关键工具,并按Dify服务器、Rerank与Embedding模型服务器、DeepSeek模型服务器三台ECS实例逐步展开,涉及xinference安装、bge-reranker-large与bge-large-zh-v1.5部署、环境版本选定及Python依赖配置等实操细节。资源包为1个docx文档,约580KB,以图文步骤与目录结构呈现,便于按章节对照操作。目前已有324人学习。读者可借此掌握多节点环境搭建思路、模型选型依据与常见配置排错方法,适合作为RAG系统部署的入门与参考手册。

1. 从零搭一套能跑的 DeepSeek RAG:为什么环境这一步最容易翻车

很多人第一次做 RAG,注意力全在提示词和向量库选型上,结果卡在环境这一步整整两天。我见过最典型的场景是:模型权重下好了,向量库也装了,import一跑就报 CUDA 版本不匹配,或者 DeepSeek 的 tokenizer 加载时提示 transformers 版本过低。RAG 系统本质上是「检索 + 生成」两条链路拼起来的工程,检索侧要 embedding 模型和向量库,生成侧要 DeepSeek 的推理服务或 API,两边对 Python、CUDA、依赖版本的要求经常打架。这篇实战笔记就围绕「基于 DeepSeek 搭建 RAG 系统」的环境搭建展开,把 Python 环境、DeepSeek 接入方式、向量库、embedding 模型这几块拆开讲清楚,每一步都给可复现的命令和参数。适合两类人:一是刚接触 RAG、想在自己机器上跑通最小闭环的新手;二是已经写过 demo、但环境一换就崩、想搞清楚依赖边界的老手。环境搭建不是走个过场,它决定了你后面调 chunk 大小、换 embedding 模型时会不会被底层报错反复打断。

2. DeepSeek 接入方式选型:本地权重还是 API,环境差在哪

2.1 两种接入路径的依赖差异

DeepSeek 在 RAG 里承担的是生成侧角色,接入方式直接决定你环境里要装什么。常见做法有两类:一类是调用官方 API,本地只需要一个 HTTP 客户端和 API Key,环境极轻;另一类是本地部署权重,需要 GPU、CUDA、PyTorch 和推理框架,环境重但数据不出本地。选型不是拍脑袋,先看你的约束条件。

维度API 接入本地权重部署
显存要求无7B 量化约 6-8GB,全精度更高
依赖复杂度低,仅需 requests/openai SDK高,需 CUDA + PyTorch + 推理框架
数据流向请求发往服务端全程本地
适合场景快速验证、原型、无 GPU数据敏感、离线、长期高频调用
环境搭建耗时10 分钟半天到两天

如果你只是想先把 RAG 闭环跑通,我一般建议先用 API 接入,把检索链路调顺,再考虑换本地权重。因为 RAG 的坑大部分在检索侧,生成侧换实现相对独立,先跑通再替换能省很多来回。

2.2 用 conda 隔离一个干净的 Python 环境

不管走哪条路,第一步都是隔离环境。RAG 项目依赖又多又杂,直接装在 base 环境里,后面装向量库或换 embedding 模型时极易冲突。用 conda 建一个独立环境,Python 版本选 3.10 或 3.11,这两个版本对 PyTorch、transformers、主流向量库的兼容性最稳。

# 创建名为 rag-deepseek 的独立环境,指定 Python 3.10 conda create -n rag-deepseek python=3.10 -y # 激活环境 conda activate rag-deepseek # 升级 pip,避免旧版 pip 解析依赖出错 python -m pip install --upgrade pip # 确认 Python 版本,应为 3.10.x python --version

逻辑说明:conda create -n的-n指定环境名,python=3.10锁定解释器版本,-y跳过交互确认。激活后所有 pip 安装都只影响这个环境,不会污染系统 Python。参数上,Python 不要选 3.12 以上,部分向量库和推理框架的预编译轮子还没跟上,容易触发源码编译,编译又依赖一堆系统库,是新手翻车高发区。

2.3 安装核心依赖并锁定版本

环境建好后装依赖。RAG 最小闭环需要四类包:HTTP 客户端(调 DeepSeek API)、embedding 模型库、向量库、文本处理工具。这里给一份能直接跑的依赖清单,版本号是我实测比较稳的组合。

# DeepSeek API 兼容 OpenAI SDK 格式,直接用 openai 客户端 pip install openai==1.30.0 # embedding 模型与分词 pip install transformers==4.40.0 sentence-transformers==2.7.0 # 向量库,chromadb 轻量、零配置,适合起步 pip install chromadb==0.4.24 # 文本切分与工具 pip install langchain-text-splitters==0.0.1 numpy==1.26.4

逻辑说明:openai包用来调 DeepSeek 的兼容接口,sentence-transformers负责把文本转成向量,chromadb做本地向量存储,langchain-text-splitters提供递归切分器。参数上,numpy锁 1.26.4 是因为 2.x 版本和部分向量库的 C 扩展不兼容,会报_ARRAY_API not found,这是血泪经验。装完用一条命令验证:

python -c "import openai, transformers, chromadb, sentence_transformers; print('deps ok')"

能打印deps ok说明依赖层没问题。如果报ImportError,先看是不是环境没激活,再看具体缺哪个包,不要盲目重装。

3. 检索侧环境:embedding 模型与向量库怎么配才不打架

3.1 embedding 模型选型与本地缓存

检索侧的核心是把文本转成向量,embedding 模型的选择直接影响召回质量。常见做法是用sentence-transformers加载一个中文友好的模型,比如BAAI/bge-small-zh-v1.5,体积小、速度快,适合起步。第一次加载会从远端下载权重,国内网络下经常卡住,所以要先配好缓存目录,必要时手动下载。

from sentence_transformers import SentenceTransformer # 指定模型名,首次运行会下载权重到缓存目录 model = SentenceTransformer("BAAI/bge-small-zh-v1.5") # 把一句话转成向量,normalize 后便于用余弦相似度 vec = model.encode("DeepSeek 的 RAG 环境怎么搭", normalize_embeddings=True) # 打印维度,bge-small-zh 应为 512 print(vec.shape)

逻辑说明:SentenceTransformer构造时会检查本地缓存,没有就下载。encode的normalize_embeddings=True把向量归一化,这样向量库用内积就等价于余弦相似度,省去额外计算。参数上,bge-small-zh-v1.5输出 512 维,bge-base-zh-v1.5是 768 维,维度越高表达力越强但存储和检索越慢,起步用 small 就够。如果下载卡住,设置环境变量HF_HOME指向一个空间足够的目录,再重试。

3.2 向量库初始化与持久化目录

向量库选 Chroma 是因为它零配置、支持本地持久化,适合单机 RAG。初始化时要显式指定持久化路径,否则默认存在内存里,进程一退数据就没了,这是新手最常踩的坑之一。

import chromadb # 指定持久化目录,数据会落盘到 ./chroma_db client = chromadb.PersistentClient(path="./chroma_db") # 创建或获取一个集合,集合类似关系库里的表 collection = client.get_or_create_collection( name="deepseek_rag", metadata={"hnsw:space": "cosine"} # 用余弦距离 ) print(collection.count()) # 首次应为 0

逻辑说明:PersistentClient的path参数决定数据落盘位置,重启后能恢复。get_or_create_collection幂等,存在就取、不存在就建。metadata里的hnsw:space指定距离度量,用cosine和前面 embedding 的归一化配套。参数上,集合名一旦写入不要随意改,改了等于新建一个空集合,旧数据还在但查不到,容易误判成「数据丢了」。

3.3 把切分、向量化、入库串成一条链路

环境配好后,用一段最小代码验证检索链路是否通。这里把文本切分、向量化、入库三步串起来,跑通就说明检索侧环境没问题。

from langchain_text_splitters import RecursiveCharacterTextSplitter # 准备一段测试文本 text = "DeepSeek 是一个大语言模型。RAG 是检索增强生成。" * 20 # 递归切分,chunk_size 控制每块长度,overlap 保留上下文 splitter = RecursiveCharacterTextSplitter( chunk_size=200, chunk_overlap=40, separators=["\n\n", "\n", "。", " "] ) chunks = splitter.split_text(text) print(f"切出 {len(chunks)} 块") # 向量化并入库 embeddings = model.encode(chunks, normalize_embeddings=True).tolist() collection.add( ids=[f"id_{i}" for i in range(len(chunks))], documents=chunks, embeddings=embeddings ) print(collection.count())

逻辑说明:RecursiveCharacterTextSplitter按分隔符优先级递归切,先按段落再按句子,尽量不切断语义。chunk_size=200是字符数,中文场景下 200-500 比较常见,太小丢上下文,太大检索不精准。chunk_overlap=40让相邻块有重叠,避免关键信息正好卡在边界被切掉。collection.add的ids必须唯一,重复会报错,实际项目里用文档 ID 加序号生成。跑完collection.count()应该等于切块数,对不上就检查是不是切分或入库环节漏了。

4. 生成侧环境:DeepSeek 调用与检索结果拼接

4.1 用 OpenAI SDK 调 DeepSeek 接口

DeepSeek 的接口兼容 OpenAI 格式,所以直接用openai包,把base_url指向 DeepSeek 的服务地址即可。API Key 不要硬编码在代码里,用环境变量读取,这是基本习惯。

import os from openai import OpenAI # 从环境变量读 Key,避免写死在代码里 client = OpenAI( api_key=os.environ["DEEPSEEK_API_KEY"], base_url="https://api.deepseek.com" ) resp = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "用一句话解释 RAG"}], temperature=0.3 ) print(resp.choices[0].message.content)

逻辑说明:base_url指向 DeepSeek 的兼容端点,model填deepseek-chat。temperature=0.3让输出更稳定,RAG 场景不需要太发散。参数上,Key 通过os.environ读取,在 shell 里用export DEEPSEEK_API_KEY=你的key设置,不要提交到代码仓库。如果报 401,先确认环境变量在当前 shell 生效;报连接超时,检查网络和 base_url 是否写错。

4.2 检索结果拼进提示词的模板

生成侧的关键是把检索到的文档拼进提示词,让模型基于上下文回答。模板设计要明确告诉模型「只根据给定资料回答」,否则模型容易自由发挥。

def build_prompt(question, docs): # 把检索到的文档拼成上下文,编号便于模型引用 context = "\n\n".join( f"[{i+1}] {d}" for i, d in enumerate(docs) ) return f"""根据以下资料回答问题,资料中没有的信息不要编造。 资料: {context} 问题:{question} """ # 检索:把问题向量化后查库 q_vec = model.encode([question], normalize_embeddings=True).tolist() res = collection.query(query_embeddings=q_vec, n_results=3) docs = res["documents"][0] prompt = build_prompt(question, docs)

逻辑说明:build_prompt把文档编号拼接,方便模型在回答里引用来源。collection.query的n_results=3控制召回条数,太少可能漏信息,太多会撑爆上下文且引入噪声,起步用 3-5 比较稳。参数上,query_embeddings必须是二维列表,即使只查一条也要包成[q_vec],这是 Chroma 的接口约定,写错会报维度错误。

4.3 端到端跑通一次问答

把检索和生成接起来,跑一次完整问答,验证整条链路。

question = "RAG 是什么?" q_vec = model.encode([question], normalize_embeddings=True).tolist() res = collection.query(query_embeddings=q_vec, n_results=3) docs = res["documents"][0] prompt = build_prompt(question, docs) resp = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": prompt}], temperature=0.3 ) print(resp.choices[0].message.content)

逻辑说明:这段把前面所有环节串起来,问题向量化、查库、拼提示词、调模型。能打印出基于资料的回答,说明环境搭建完成。参数上,如果回答里出现资料中没有的内容,说明提示词约束不够,把「不要编造」写得更强硬,或降低temperature。如果召回文档和问题不相关,问题多半在 embedding 模型或切分粒度,不是生成侧的事。

5. 环境搭建避坑:五个让我重装过环境的真实问题

5.1 现象:import chromadb 报 hnswlib 编译失败

原因:pip 找不到预编译轮子,回退到源码编译,而系统缺 C++ 编译工具链。解决:优先升级 pip 到最新,让它拉预编译轮子;仍失败就装系统编译工具,Linux 下apt install build-essential,macOS 下xcode-select --install。实在不行换 Python 3.10,轮子覆盖最全。

5.2 现象:embedding 模型下载卡在 0%

原因:默认从境外源拉权重,网络不稳定。解决:设置HF_HOME指向本地目录,用镜像源加速,或手动下载权重后放到缓存目录对应位置。注意缓存目录结构是models--组织名--模型名,放错位置不会被识别。

5.3 现象:向量库重启后数据为空

原因:初始化时用了Client()而不是PersistentClient(),数据只在内存。解决:改成PersistentClient(path=...),并确认 path 是绝对路径或相对当前工作目录的稳定路径。如果工作目录变了,相对路径会指向新位置,看起来像数据丢了。

5.4 现象:调 DeepSeek 报 400 或返回乱码

原因:messages格式不对,或model名写错。解决:确认messages是[{"role": "user", "content": "..."}]结构,model填deepseek-chat。乱码多半是编码问题,确保请求内容用 UTF-8。

5.5 现象:检索结果和问题完全不相关

原因:embedding 模型和向量库距离度量不匹配,或切分粒度过大。解决:确认入库和查询用的是同一个 embedding 模型,向量库距离度量设为cosine且 embedding 做了归一化。切分粒度调小到 200-300 字符再试。

6. 进阶技巧:用一次「冷启动自检」把环境问题挡在写业务之前

环境搭完别急着写业务代码,先做一次冷启动自检。所谓冷启动,就是把环境完全重建一遍,从空目录开始跑通最小闭环。这一步能暴露很多「当前 shell 有残留状态」掩盖的问题,比如某个包是之前手动装的、某个环境变量是临时 export 的。我一般会写一个自检脚本,把依赖检查、模型加载、向量库读写、API 调用四件事串起来,每次换机器或升级依赖后跑一遍。

import os, sys def self_check(): # 1. 依赖检查 import openai, transformers, chromadb, sentence_transformers print("[1/4] deps ok") # 2. embedding 模型加载 from sentence_transformers import SentenceTransformer m = SentenceTransformer("BAAI/bge-small-zh-v1.5") v = m.encode("自检", normalize_embeddings=True) assert v.shape[0] == 512, "embedding 维度异常" print("[2/4] embedding ok") # 3. 向量库读写 import chromadb c = chromadb.PersistentClient(path="./self_check_db") col = c.get_or_create_collection("check", metadata={"hnsw:space": "cosine"}) col.add(ids=["1"], documents=["测试"], embeddings=[v.tolist()]) assert col.count() >= 1, "向量库写入失败" print("[3/4] vector store ok") # 4. API 调用 from openai import OpenAI cli = OpenAI(api_key=os.environ["DEEPSEEK_API_KEY"], base_url="https://api.deepseek.com") r = cli.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "回复 ok"}], temperature=0 ) assert r.choices[0].message.content, "API 返回为空" print("[4/4] api ok") if __name__ == "__main__": self_check()

逻辑说明:四步分别覆盖依赖、embedding、向量库、API,任何一步失败都会中断并暴露具体环节。assert用来做硬性校验,维度不对、写入失败、返回为空都会立刻报错,比事后排查省时间。参数上,自检用的向量库路径单独设./self_check_db,不污染正式数据;temperature=0让 API 返回确定,便于判断连通性。

自检脚本建议纳入版本管理,每次改依赖或换机器先跑。我自己的习惯是:任何 RAG 项目开工前,先跑自检,四步全绿再写业务。这样后面调 chunk、换模型时,能确定问题出在业务逻辑而不是环境。环境搭建这件事,前期多花半小时做自检,后期能省下几小时的玄学排查。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询