Skip to content

想让你的 AI 更懂行?手把手教你用 DeepSeek R1 + Ollama 搭建本地 RAG 应用

嘿,朋友!有没有想过让你的 AI 不仅仅是“会说话”,而是能“引经据典”、基于真实资料来回答问题?这就是 RAG(检索增强生成) 技术的神奇之处。简单说,它就像给 AI 外挂了一个知识库,让它的回答更靠谱、信息更准确。

这篇教程就手把手带你,利用现在很火的 DeepSeek R1 模型和 Ollama 这个工具,在你自己的电脑上搭建一个 RAG 应用。全程本地化,无需担心数据隐私!

动手前的准备:配好环境是第一步

在咱们正式开始“盖楼”之前,得先把“地基”打好——也就是安装 Ollama 并配置好环境。

别担心,过程不复杂。Ollama 的 GitHub 仓库 有超详细的官方指南,这里我帮你提炼下关键步骤:

第 1 步:下载 Ollama

直接去 Ollama 官网下载页面,找到适合你电脑系统(Windows, macOS, Linux 都有)的安装包,下载下来,双击运行就好。

第 2 步:检查下装好了没

打开你的命令行工具(比如 Windows 的 PowerShell 或 CMD,macOS 的 Terminal),输入 ollama 然后敲回车。如果屏幕上出现了 Ollama 的欢迎信息和命令列表,那就说明 Olla~ma 已经安家落户成功啦!

第 3 步:请“大脑”和“翻译官”就位

我们需要两个模型:一个是负责思考和生成回答的“大脑”(DeepSeek R1),另一个是负责理解文本、把它变成电脑能处理的“向量”的“翻译官”(文本嵌入模型)。

  • 请 DeepSeek R1 大脑: 在命令行里输入下面这行,把它拉取到你本地。

    bash
    ollama pull deepseek-r1:1.5b

    温馨提示: 下载模型可能需要一点时间,网速快慢决定等待时长。如果中途卡壳了,别慌,重新试试命令通常能解决。

  • 请 nomic-embed-text 翻译官: 同样,在命令行输入这行,拉取文本嵌入模型。

    bash
    ollama pull nomic-embed-text

    “文本嵌入模型”是个啥? 简单理解,它就是个能把文字(比如一句话、一段文章)转换成一串数字(向量)的工具。这样,电脑就能通过计算这些数字之间的距离,来判断哪些文字在意思上更接近。后面我们检索资料就靠它了。

第 4 步:让 DeepSeek R1 跑起来!

在命令行窗口敲入以下命令,启动 DeepSeek R1 模型服务。

bash
ollama run deepseek-r1:1.5b

看到类似下面的界面,就表示模型已经在后台准备好,随时可以接收指令了。

你也可以直接通过 ollama run deepseek-r1:1.5b 命令一步到位地拉取并运行模型,非常方便。

注意: 如果你只是想体验下用 Ollama 跑 DeepSeek R1 模型,和它聊聊天,那到这里就够了,后面的步骤是给想搭建完整 RAG 应用的朋友准备的。

第 5 步:安装 Python 工具包

咱们这个 RAG 应用需要用 Python 来“粘合”各个部分。确保你的电脑装了 Python (3.8 或更新版本)。然后,在命令行用 pip 这个工具安装几个必备的库:

bash
# LangChain 核心库,是构建 AI 应用的框架
pip install langchain

# LangChain 社区提供的各种实用工具
pip install langchain_community

# Chroma 数据库,用来存放我们的“知识索引”
pip install langchain_chroma

# 让 LangChain 能和 Ollama 顺利“对话”的桥梁
pip install langchain_ollama

好啦,准备工作完成!是不是感觉离目标更近一步了?接下来,我们就正式进入 RAG 应用的搭建环节。

1. 给 AI “喂”点知识——加载并拆分文档

首先,得把我们想让 AI 学习的知识(比如一个 PDF 文件)加载进来,并且切成一小块一小块的,方便后续处理。

python
# 从 LangChain 社区工具里导入 PDF 加载器
from langchain_community.document_loaders import PDFPlumberLoader
# 从 LangChain 导入文本分割工具
from langchain.text_splitter import RecursiveCharacterTextSplitter

# 指定你的 PDF 文件路径
file = "DeepSeek_R1.pdf"

# 创建一个 PDF 加载器实例,加载文件
loader = PDFPlumberLoader(file)
docs = loader.load() # 这会把 PDF 内容读出来

# 创建一个文本分割器实例
# chunk_size=500 表示尽量按 500 个字符切分
# chunk_overlap=0 表示切分出来的块之间不重叠
text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=0)
# 执行切分动作,把长文档变成很多小片段
all_splits = text_splitter.split_documents(docs)

这段 Python 代码干了两件事:一是像打开文件一样,用 PDFPlumberLoader 读取了 DeepSeek_R1.pdf 的内容;二是创建了一个“切割机” (RecursiveCharacterTextSplitter),把整个文档按大约 500 个字一块给切开了。

为啥要切分呢? 因为大模型一次能“消化”的文字长度是有限的。把长文档切成小块,既能保证每块信息都能被模型处理,也方便后面我们精确地找到跟问题最相关的那几块内容。

2. 建立“知识索引”——初始化向量存储

有了切好的知识小块,下一步就是把它们变成电脑能理解的“向量”,并存起来,方便快速查找。这里我们用 Chroma 这个“向量数据库”来帮忙。

python
# 从 LangChain Chroma 库导入 Chroma
from langchain_chroma import Chroma
# 从 LangChain Ollama 库导入 Ollama 嵌入功能
from langchain_ollama import OllamaEmbeddings

# 创建一个 Ollama 嵌入模型的实例,告诉它我们要用 'nomic-embed-text'
local_embeddings = OllamaEmbeddings(model="nomic-embed-text")

# 创建 Chroma 向量数据库
# 把切分好的文档块 (all_splits) 和 嵌入模型 (local_embeddings) 交给 Chroma
# Chroma 会自动调用嵌入模型把文本转成向量,然后存起来
vectorstore = Chroma.from_documents(documents=all_splits, embedding=local_embeddings)

看,这里我们先是告诉程序,我们要用之前下载的 nomic-embed-text 模型来做“文本到向量”的转换工作 (OllamaEmbeddings)。然后,调用 Chroma.from_documents,它就会自动地把我们之前切好的 all_splits 里的每一块文本,都通过 local_embeddings 转换成向量,并保存在 Chroma 数据库里。

“向量存储”听起来很高级,其实… 你可以把它想象成一个超级智能的图书馆索引系统。传统索引可能按书名、作者,而向量索引是按“意思”来的。你问一个问题,它能迅速告诉你,“嘿,这几块内容跟你问的意思最像!” 这就是 RAG 检索知识的关键所在。

3. 串联流程——构建处理链 (Chain)

准备工作就绪,是时候把各个零件“组装”起来,形成一个完整的工作流程了。LangChain 提供了一种叫做“链(Chain)”的简洁方式来实现这一点。

python
# 从 LangChain 核心库导入 输出解析器 和 聊天提示模板
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
# 从 LangChain Ollama 库导入聊天模型
from langchain_ollama import ChatOllama

# 创建一个 Ollama 聊天模型的实例,指定使用 'deepseek-r1:1.5b'
model = ChatOllama(
    model="deepseek-r1:1.5b",
)

# 定义一个提示模板,告诉模型要做什么
# {docs} 是一个占位符,后面我们会把找到的文档内容填进去
prompt = ChatPromptTemplate.from_template(
    "请根据我提供的这些文档片段,总结一下它们的主要内容: {docs}"
)

# 定义一个辅助函数,方便地把多份文档内容合并成一个字符串
def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

# 构建处理链:
# 1. 把传入的文档列表 (docs) 用 format_docs 函数格式化
# 2. 把格式化后的文档内容填入 prompt 模板
# 3. 把填充好的提示交给 model (DeepSeek R1) 处理
# 4. 用 StrOutputParser 获取模型生成的纯文本回答
chain = {"docs": format_docs} | prompt | model | StrOutputParser()

# 来测试一下!
# 假设我们想问 "DeepSeek 项目的目标是什么?"
question = "What is the purpose of the DeepSeek project?"
# 先在向量数据库里找找相关的文档片段
docs = vectorstore.similarity_search(question)
# 然后把找到的文档片段交给 chain 来处理和总结
chain.invoke(docs)

这段代码做了几件事:

  • 指定了我们用 deepseek-r1:1.5b 作为回答问题的模型 (ChatOllama)。
  • 写了一个“指令模板” (prompt),告诉模型等会儿要基于给它的资料做总结。
  • | 这个神奇的符号,像流水线一样把“格式化文档”、“填充指令”、“模型处理”、“提取结果”这几个步骤串了起来,形成了一个 chain
  • 最后,我们试着问了个问题,先用 vectorstore.similarity_search 找到相关资料,再把资料“喂”给 chain,让它跑一遍流程,输出总结。

“Chain 表达式”是啥? 它就是 LangChain 里一种组织工作流的方法。你可以想象成用管道 (|) 把不同的工具(数据加载器、文本处理器、模型、输出格式化工具等)连接起来,数据从一端进去,经过一系列处理,最后从另一端出来。非常灵活!

4. 强强联手——实现带检索的问答 (RAG)

激动人心的时刻到了!现在我们把“找资料”(检索)和“回答问题”(生成)这两大功能合体,打造一个完整的 RAG 系统。

python
# 从 LangChain 核心库导入一个传递输入的工具
from langchain_core.runnables import RunnablePassthrough

# 定义一个专门给 RAG 用的提示模板
RAG_TEMPLATE = """
你是一个问答助手。请根据下面提供的上下文信息来回答问题。如果你不知道答案,就直接说不知道。回答尽量简洁,不要超过三句话。

<上下文>
{context}
</上下文>

请回答下面的问题:
{question}
"""

# 基于上面的模板创建 RAG 提示实例
rag_prompt = ChatPromptTemplate.from_template(RAG_TEMPLATE)

# 从我们的向量数据库创建一个“检索器”
# 它能根据问题自动去数据库里捞相关文档
retriever = vectorstore.as_retriever()

# 构建最终的问答链 (QA Chain)
qa_chain = (
    # 并行处理:
    # 1. "context": 用户的问题先经过 retriever 找到相关文档,再用 format_docs 格式化
    # 2. "question": 用户的问题直接传递下去
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    # 上一步的结果 (包含 context 和 question) 被送入 rag_prompt 模板
    | rag_prompt
    # 填充好的提示交给 model 处理
    | model
    # 最后用 StrOutputParser 提取纯文本答案
    | StrOutputParser()
)

# 最终测试!
question = "What is the purpose of the DeepSeek project?"
# 直接把问题“喂”给 qa_chain
# 它会自动完成:检索 -> 整理上下文 -> 生成答案 的全过程
qa_chain.invoke(question)

这段代码的核心就是构建 qa_chain。你看,它巧妙地把 retriever(检索器)整合了进来。当用户提出 question 时:

  1. retriever 先根据 question 从 Chroma 数据库里找出最相关的文档片段 (context)。
  2. 这些 context 和原始的 question 一起被填入 rag_prompt 这个更具体的指令模板里。
  3. 最后,这个包含上下文和问题的完整提示被发送给 model (DeepSeek R1),生成最终的答案。

这就是 RAG 的魔力:它不是凭空回答,而是先“查资料”再“说话”,回答自然就更靠谱了!

大功告成!回顾一下我们的 RAG 之旅

恭喜你!跟着这篇教程,我们一起用 DeepSeek R1 和 Ollama 成功搭建了一个本地运行的 RAG 应用。回顾一下,我们并肩作战,完成了:

  1. 知识准备: 学会了如何加载 PDF 文档,并把它切分成适合模型处理的小块。
  2. 建立索引: 利用 Chroma 和 Ollama 的嵌入模型,为知识块建立了高效的“语义索引”。
  3. 流程编排: 掌握了使用 LangChain 的 Chain 表达式,将数据处理、模型调用等步骤流畅地串联起来。
  4. RAG 落地: 最终将检索和生成两大模块完美融合,实现了能根据给定文档回答问题的智能系统。

现在,你拥有了一个完全跑在你本地电脑上的、具备“学习”能力的 AI 应用雏形。不妨动手试试用你自己的文档来构建知识库,看看它能带来什么惊喜吧!

下一步探索? 如果你想让更多人方便地使用你的 RAG 应用,可以考虑用 Streamlit 或 FastAPI 这样的工具,把它打包成一个网页服务。想象一下,做一个能回答你专业领域文档内容的智能助手,是不是很酷?

我们还在代码仓库里为你准备了一个可以直接运行的 Web 应用示例 app.py记得,在运行它之前,要先确保 Ollama 服务已经在后台启动了哦!

启动这个 app.py 后,在浏览器里就能看到一个简单的对话界面,让你和你的本地 RAG 应用互动起来!

玩得开心!探索 AI 的世界,乐趣无穷!