
在本地运行 DeepSeek-R1:使用 Ollama 和 FAISS 构建自定义矢量数据库和人工智能聊天机器人
- Rifx.Online
- Programming , Natural Language Processing , Chatbots
- 12 Feb, 2025
介绍
大型语言模型 (LLMs) 的演变显著改变了人工智能的格局。然而,许多强大的模型需要基于云的 API,这引发了对 数据隐私、成本和延迟 的担忧。
幸运的是,DeepSeek-R1,一个 80亿参数的语言模型,现在可以通过 Ollama 完全离线部署——这是一个简化本地运行 LLM 的框架。
在本综合指南中,我们将逐步介绍:✅ 设置 Ollama 以在本地运行 DeepSeek-R1。✅ 高效下载和配置 DeepSeek-R1 模型。✅ 理解 Ollama 如何通过量化优化 LLM。✅ 构建自定义 FAISS 向量数据库 以实现智能文档检索。✅ 将 DeepSeek 与 LangChain 和 Chainlit 集成 以创建一个互动 AI 聊天机器人。
通过本教程的学习,您将拥有一个功能齐全的 基于 LLM 的检索聊天机器人,能够根据 自定义 PDF 文档 回答问题,所有操作均在 100% 本地机器上 运行。
步骤 1:安装和设置 Ollama
在运行 DeepSeek-R1 之前,我们首先需要安装 Ollama,它提供了一种简化的方式来本地运行和与 LLMs 互动。
从官方网站下载 Ollama Desktop:访问官方网站:🔗 https://ollama.com点击 “下载”按钮以获取 Windows 版本。下载完成后,您将在 下载文件夹中找到 Ollama 安装程序文件(OllamaSetup.exe
)。
运行安装程序:
- 双击
OllamaSetup.exe
文件。 - 按照屏幕上的说明进行操作 — 点击“下一步”和“安装”以完成安装。
验证安装:
- 打开 命令提示符(CMD)。
- 输入以下命令并按 Enter:
- 输入 ‘ollama serve’
此错误表示 Ollama 尝试在端口 127.0.0.1:11434
上运行,但该端口已被占用。
- 验证输入 ‘netstat -ano | findstr :11434’
- 现在在任何浏览器中打开一个新标签页,输入 ‘http://localhost:11434/’,您将看到消息 ‘Ollama is running’
在使用 DeepSeek-R1 和 自定义数据集 之前,我们首先需要 在本地下载(拉取)模型。这确保我们在机器上存储模型以进行 离线推理,而不是每次都实时运行它。
打开您的网页浏览器,访问 Ollama 模型库:🔗 https://ollama.com/library/deepseek-r1
在这里,您将看到不同版本的 DeepSeek-R1 模型,具有各种参数大小。
选择 8B 参数版本,以在性能和资源使用之间获得最佳平衡。
在网站上,您会注意到命令
ollama run deepseek-r1:8b
然而,我们 在这个阶段不打算使用 run
。
❌ ollama run deepseek-r1:8b
- 这会立即启动模型进行本地推理。
- 如果您只是想与模型进行互动聊天,这很有用。
- 但在我们的情况下,我们需要使用自定义数据集,因此我们必须先下载(拉取)模型。
✅ ollama pull deepseek-r1:8b
- 这下载模型而不运行它。
- 这使我们能够将其用作API或与FAISS & LangChain集成,以便用于我们的向量数据库。
- 这是必要的,因为我们想处理自定义数据集,而不是直接在交互模式中使用模型。
输入‘ollama run deepseek-r1:8b’后,您将看到:
- Ollama将从其仓库获取DeepSeek-R1模型(约4.9GB)。
- 模型将被本地存储,因此不需要再次下载。
- 这确保了快速推理和离线可用性。
成功拉取DeepSeek-R1:8B模型后,确认模型已下载并可供使用非常重要。
要检查模型是否存储在本地,请打开您的终端或命令提示符并运行以下命令:
ollama list
您将看到此类输出,
Step 2: Python Setup for DeepSeek-R1 Chatbot
要将 DeepSeek-R1 与 自定义向量数据库 集成,我们需要设置 Python 环境 并安装必要的库。请按照以下步骤操作,以确保一切顺利运行。下载 Python 3.12.x 以适应您的操作系统。
为了保持项目依赖的隔离,请在安装所需库之前创建一个 虚拟环境。
在您的项目文件夹中运行以下命令:
python -m venv deepseek_env # Create a virtual environment
source deepseek_env/bin/activate # For macOS/Linux
source deepseek_env/Scripts/activate # For Windows
为了确保功能顺畅,我们将通过 requirements.txt
文件安装 所有必要的依赖。
创建 requirements.txt 文件
在您的项目目录中创建一个名为 requirements.txt
的文件,并 添加以下依赖项:
langchain
ctransformers
pypdf
tiktoken
langchain-community
transformers
sentence-transformers
tqdm
accelerate
chainlit
unidecode
huggingface_hub
langchain-huggingface
pypdfium2
pydantic==2.9.2
langchain_ollama
创建文件后,在终端中运行以下命令以一次性安装所有所需的包:
pip install -r requirements.txt
创建一个名为 ‘data’ 的文件夹,并 将所有 PDF 文件 放置在此 data/
文件夹中。这些文档将用于创建 FAISS 向量数据库,使聊天机器人能够 根据内容检索和回答查询。
第 3 步:Python 编码
现在我们可以开始构建 FAISS 向量数据库并将其与DeepSeek-R1集成,以便处理自定义数据集!
创建一个 Python 文件 (admin.py) 用于 FAISS 向量数据库的创建。此 Python 脚本用于将 PDF 文档转换为向量数据库,使用FAISS (Facebook AI Similarity Search) 和 Hugging Face sentence-transformer embeddings。
admin.py 代码解析与说明
1. 导入所需库
#from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_huggingface import HuggingFaceEmbeddings
#from langchain.vectorstores import FAISS
from langchain_community.vectorstores import FAISS
from langchain_community.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
import os
🔹 这里发生了什么?
- HuggingFaceEmbeddings: 用于将文本转换为数值向量(嵌入)。
- FAISS: 一个快速高效的向量搜索库,用于存储和检索嵌入。
- PyPDFLoader & DirectoryLoader: 从目录加载PDF并提取其文本。
- RecursiveCharacterTextSplitter: 将大型文档拆分为较小的块,以便更好地生成嵌入。
- os: 处理目录创建。
2. 定义文件路径
DATA_PATH = 'data/'
DB_FAISS_PATH = 'vectorstore/db_faiss'
🔹 这里发生了什么?
- DATA_PATH:存储PDF文档的目录。
- DB_FAISS_PATH:保存FAISS向量数据库的目录。
3. 创建向量数据库的函数
def create_vector_db():
loader = DirectoryLoader(DATA_PATH,
glob='*.pdf',
loader_cls=PyPDFLoader)
🔹 这里发生了什么?
DirectoryLoader(DATA_PATH, glob='*.pdf', loader_cls=PyPDFLoader)
:- 这将加载 data/ 目录下的所有
.pdf
文件。 - 使用
PyPDFLoader
从每个 PDF 中提取文本。
4. 将文档拆分为块
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500,
chunk_overlap=50)
texts = text_splitter.split_documents(documents)
🔹 这里发生了什么?
loader.load()
: 从PDF中读取提取的文本。- RecursiveCharacterTextSplitter: 将长文本拆分为较小的块,以便每个块可以单独处理。
5. 创建文本嵌入
embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2',
model_kwargs={'device': 'cpu'})
🔹 这里发生了什么?
- 嵌入是文本的数值表示,用于高效的相似性搜索。
sentence-transformers/all-MiniLM-L6-v2
:一个 预训练模型,将文本转换为嵌入。model_kwargs={'device': 'cpu'}
:在 CPU 上运行模型(可以更改为'cuda'
以使用 GPU)。
6. 在 FAISS 中存储文本嵌入
db = FAISS.from_documents(texts, embeddings)
db.save_local(DB_FAISS_PATH)
🔹 这里发生了什么?
FAISS.from_documents(texts, embeddings)
:- 将文本块转换为 嵌入。
- 将它们存储在 FAISS 索引 中(一个快速的向量搜索数据库)。
db.save_local(DB_FAISS_PATH)
:- 将 FAISS 索引本地保存,以便后续使用。
7. 运行脚本
if __name__ == "__main__":
# 确保 vectorstore 目录存在
os.makedirs(DB_FAISS_PATH, exist_ok=True)
create_vector_db()
🔹 这里发生了什么?
os.makedirs(DB_FAISS_PATH, exist_ok=True)
: 如果 vectorstore/db_faiss 目录不存在,则创建该目录。create_vector_db()
: 调用该函数以生成 FAISS 向量数据库。
现在创建另一个 python 文件 (app.py),用于关于 自定义 PDF 文档 的问题,聊天机器人将 从 FAISS 中检索相关信息 并使用 DeepSeek-R1 生成响应。
app.py 代码分解与解释
1. 导入所需库
import requests
from langchain_community.document_loaders import PyPDFLoader, DirectoryLoader
from langchain.prompts import PromptTemplate
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import FAISS
from langchain.chains import RetrievalQA
from langchain_ollama import OllamaLLM
import chainlit as cl
🔹 这里发生了什么?
requests
: 用于发送 HTTP 请求(在脚本中未积极使用)。PyPDFLoader, DirectoryLoader
: 加载 PDF 并提取文本。PromptTemplate
: 定义聊天机器人的 问答格式。HuggingFaceEmbeddings
: 将文本转换为 嵌入(数值表示)。FAISS
: 加载和搜索向量化文档。RetrievalQA
: 创建一个 问答链,获取相关信息。OllamaLLM
: 通过 Ollama API 使用 DeepSeek-R1 LLM 进行文本生成。chainlit
: 提供一个 聊天用户界面 以便用户互动。
2. 数据库路径定义和自定义提示模板
DB_FAISS_PATH = 'vectorstore/db_faiss'
custom_prompt_template = """Use the following pieces of information to answer the user's question.
If you don't know the answer, just say that you don't know, don't try to make up an answer.Context: {context}
Question: {question}
Only return the helpful answer below and nothing else.
Helpful answer:
"""
- 这定义了 预构建的 FAISS 向量数据库 的位置(在
admin.py
中创建)。 - 该数据库包含用于检索的 PDF 文本嵌入。
- 提供从检索文档中获得的上下文。
- 确保模型不会生成虚假信息。
- 以清晰的格式结构化响应。
3. 设置提示模板的函数
def set_custom_prompt():
prompt = PromptTemplate(template=custom_prompt_template, input_variables=['context', 'question'])
return prompt
🔹 这个函数的作用
- 使用自定义模板创建一个
PromptTemplate
对象。 - 定义输入变量(来自FAISS的
context
和用户的question
)。 - 返回将在LLM链中使用的提示。
4. 通过 Ollama 加载 DeepSeek-R1
def load_llm():
llm = OllamaLLM(
model="deepseek-r1:8b",
base_url="http://localhost:11434", # Ollama 的默认基础 URL
temperature=0.3, # 较低的创造性以获得简洁的响应
top_p=0.85, # 稍微调整多样性
max_tokens=150 # 限制响应的长度
)
return llm
🔹 这个函数的作用
- 通过 Ollama 加载 DeepSeek-R1 8B 模型(本地运行)。
- 设置关键参数:
temperature=0.3
:保持响应 简洁和事实性(较低的值减少随机性)。top_p=0.85
:控制响应的多样性(接近 1 的值允许更多样化的响应)。max_tokens=150
:限制响应长度(防止答案过长)。
5. 设置检索问答链
def retrieval_qa_chain(llm, prompt, db):
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=db.as_retriever(search_kwargs={'k': 1}), # Retrieve the most relevant document
return_source_documents=True,
chain_type_kwargs={'prompt': prompt}
)
return qa_chain
🔹 此功能的作用
- 使用
RetrievalQA
创建基于检索的聊天机器人。 - 使用
db.as_retriever(search_kwargs={'k': 1})
从 FAISS 数据库中检索信息: k=1
:仅为每个查询获取 最相关 的文档。- 使用 DeepSeek-R1 LLM 生成响应。
- 返回检索到的源文档 (
return_source_documents=True
)。
6. 设置 QA 机器人
def qa_bot():
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
db = FAISS.load_local(DB_FAISS_PATH, embeddings, allow_dangerous_deserialization=True)
llm = load_llm()
qa_prompt = set_custom_prompt()
qa = retrieval_qa_chain(llm, qa_prompt, db)
return qa
🔹 此功能的作用
- 加载 FAISS 向量数据库 (
FAISS.load_local(DB_FAISS_PATH, embeddings)
). - 加载 DeepSeek-R1 LLM (
load_llm()
). - 设置自定义提示模板 (
set_custom_prompt()
). - 创建一个结合 FAISS 检索和 DeepSeek 生成的 QA 链 (
retrieval_qa_chain(...)
). - 返回用于推理的 QA 模型.
7. Chainlit 聊天机器人 UI:机器人初始化
@cl.on_chat_start
async def start():
chain = qa_bot()
msg = cl.Message(content="Starting the bot...")
await msg.send()
msg.content = "Hi, Welcome to the DeepSeek ChatBot. How can I assist you today?"
await msg.update()
cl.user_session.set("chain", chain)
🔹 聊天机器人启动时会发生什么?
- 加载 QA bot (
qa_bot()
)。 - 向用户发送“正在启动机器人…”消息。
- 将消息更新为“嗨,欢迎来到 DeepSeek 聊天机器人!”。
- 将 QA 链存储在 Chainlit 的会话中 (
cl.user_session.set("chain", chain)
)。
8. 处理用户消息
@cl.on_message
async def main(message: cl.Message):
chain = cl.user_session.get("chain")
if chain is None:
await cl.Message(content="错误:链未初始化。").send()
return
cb = cl.AsyncLangchainCallbackHandler(
stream_final_answer=True,
answer_prefix_tokens=["最终", "答案"]
)
response = await chain.acall({'query': message.content}, callbacks=[cb])
answer = response["result"]
sources = response.get("source_documents", [])
if sources:
answer += "\n来源:" + "\n".join([str(doc.metadata['source']) for doc in sources])
else:
answer += "\n未找到来源"
await cl.Message(content=answer).send()
🔹 用户发送消息时会发生什么?
- 检索存储的 QA 模型 (
chain = cl.user_session.get("chain")
)。 - 如果聊天机器人未初始化,则返回错误消息。
- 创建异步回调处理程序以流式传输响应 (
cb = cl.AsyncLangchainCallbackHandler(...)
)。 - 调用 RetrievalQA 链 (
chain.acall({'query': message.content}, callbacks=[cb])
):
- 这将从 FAISS 检索相关文本。
- 使用 DeepSeek-R1 LLM 生成答案。
现在聊天机器人已准备就绪,您可以:✅ 使用 Chainlit 进行测试:
chainlit run app.py
现在 PDF,
如果我们问“工业政策是什么时候引入的?”
第4步:结论
在本指南中,我们成功构建了一个 retrieval-augmented chatbot,能够处理 custom PDF documents 并提供 context-aware responses,使用 DeepSeek-R1, FAISS, 和 LangChain。与传统聊天机器人不同,该系统 在生成答案之前从上传的文档中检索相关信息,确保更高的准确性和可靠性。