Ollama + LangChain 本地 RAG 系统搭建完整指南

# Ollama + LangChain 本地 RAG 系统搭建完整指南

## 背景介绍

大语言模型火了一两年,现在已经不是什么新鲜事物了。但真要用来处理自己公司的文档、个人的笔记,有个绕不过去的问题:数据安全怎么办?总不能把敏感的内部资料发给 OpenAI 吧?

还有另一个尴尬:模型再强也是有知识截止日期的。你问它你们公司去年 Q3 的技术方案,它肯定答不上来。

RAG(检索增强生成)就是来解决这两个问题的。原理其实不复杂:先把私有文档存到向量数据库里,用户提问时先找到相关的文档片段,然后把这段内容连同问题一起发给大模型,让它生成答案。

这两年做本地大模型最火的项目是 Ollama。安装简单,一条命令就能跑起来,支持 Llama、Qwen 这些主流模型,不用买显卡,普通笔记本就能跑。LangChain 则是目前做 LLM 应用最成熟的框架,几乎所有做 AI 应用的开发者都在用。

这篇文章的目标很简单:手把手带你搭一个完全本地化的 RAG 系统。所有代码都能跑,所有数据都留在你电脑上。

## 问题描述

我知道很多人想尝试 RAG,但被各种问题卡住了:

– 环境配置太烦了。Python 版本、依赖包兼容性,踩坑踩半天。
– 模型到底该用哪个?Ollama 支持十几种模型,选错了效果不好。
– 向量数据库怎么选?Chroma、FAISS、Milvus,听都没听过。
– 中文分词太头疼了。用英文那套分块方法处理中文,效果一塌糊涂。
– 明明导入了文档,为什么检索结果总是不对?

这些问题我都遇到过,下面一个一个解决。

## 详细步骤

### 第一步:环境准备

确保你的 Python 版本在 3.10 以上。然后创建项目、装依赖:

“`bash
mkdir local-rag-system && cd local-rag-system
python3 -m venv venv
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows

pip install langchain langchain-community langchain-huggingface \
langchain-text-splitters \
chromadb \
sentence-transformers \
uvicorn fastapi \
python-dotenv \
pypdf \
requests
“`

依赖比较多,但这些都是必须的。Chroma 是向量数据库,sentence-transformers 用来做文本向量化,pypdf 用来读取 PDF。

### 第二步:安装和配置 Ollama

Ollama 支持三大主流系统。Linux 上安装很直接:

“`bash
curl -fsSL https://ollama.com/install.sh | sh
“`

装好后下载模型。对于中文 RAG 任务,我推荐 Qwen2-7B:

“`bash
ollama pull qwen2:7b
“`

内存不够的话,3B 版本也能跑:

“`bash
ollama pull qwen2:3b
“`

启动服务(默认端口 11434):

“`bash
ollama serve
“`

### 第三步:处理文档

把文档转成向量是 RAG 的核心步骤。LangChain 提供了各种文档加载器,PDF 用 PyPDFLoader,TXT 直接读就行。

分块是个技术活。块太大了,检索不精准;块太小了,上下文丢失。LangChain 的 `RecursiveCharacterTextSplitter` 做得不错,它会智能分割。

对于中文,我建议把 chunk_size 设成 500 字符,overlap 设成 50。分隔符要包含中文标点:

“`python
from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len,
separators=[‘\n\n’, ‘\n’, ‘。’, ‘!’, ‘?’, ‘.’, ‘!’, ‘?’]
)
“`

### 第四步:选向量数据库

本地跑的话,Chroma 最省事。不用另外开服务器,文件形式存储,跟 LangChain 集成得也很好。

“`python
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings

# 选一个支持中文的 embedding 模型
embeddings = HuggingFaceEmbeddings(
model_name=’sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2′,
model_kwargs={‘device’: ‘cpu’}
)

# 创建向量数据库
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory=’./chroma_db’
)
“`

如果你追求更快检索速度,可以换 FAISS。两个 API 兼容,切换成本很低。

### 第五步:组装 RAG 链

现在把所有东西串起来,变成一个完整的问答流程:

“`python
from langchain_community.chat_models import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough

# 初始化 Ollama 模型
llm = ChatOllama(model=’qwen2:7b’, temperature=0.7)

# 提示词模板
prompt = ChatPromptTemplate.from_template(“””
基于以下上下文回答问题。如果问题无法从上下文中回答,请说明不知道。

上下文:
{context}

问题:{question}
“””)

# 构建 RAG 链
rag_chain = (
{‘context’: vectorstore.as_retriever(), ‘question’: RunnablePassthrough()}
| prompt
| llm
)
“`

这就完成了。问问题的时候,retriever 会先找到最相关的文档片段,然后和问题一起发给模型。

## 完整代码示例

下面是一个完整可运行的版本,把上面的内容都整合在一起了:

“`python
import os
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings
from langchain_community.chat_models import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.documents import Document

class LocalRAGSystem:
def __init__(self, model_name=’qwen2:7b’, embedding_model=’sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2′):
self.model_name = model_name
self.embeddings = HuggingFaceEmbeddings(
model_name=embedding_model,
model_kwargs={‘device’: ‘cpu’}
)
self.llm = ChatOllama(model=model_name, temperature=0.7)
self.vectorstore = None
self.rag_chain = None

def load_pdf(self, file_path):
“””加载 PDF 文档”””
loader = PyPDFLoader(file_path)
return loader.load()

def load_txt(self, file_path):
“””加载文本文件”””
with open(file_path, ‘r’, encoding=’utf-8′) as f:
return [Document(page_content=f.read(), metadata={‘source’: file_path})]

def process_documents(self, documents):
“””处理文档:分块并创建向量数据库”””
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
length_function=len,
separators=[‘\n\n’, ‘\n’, ‘。’, ‘!’, ‘?’, ‘.’, ‘!’, ‘?’]
)

chunks = text_splitter.split_documents(documents)
print(f”文档已分割为 {len(chunks)} 个块”)

self.vectorstore = Chroma.from_documents(
documents=chunks,
embedding=self.embeddings,
persist_directory=’./chroma_db’
)

return len(chunks)

def build_chain(self):
“””构建 RAG 问答链”””
prompt = ChatPromptTemplate.from_template(“””
基于以下上下文回答问题。如果问题无法从上下文中回答,请说明不知道。

上下文:
{context}

问题:{question}
“””)

self.rag_chain = (
{‘context’: self.vectorstore.as_retriever(), ‘question’: RunnablePassthrough()}
| prompt
| self.llm
)

def ask(self, question):
“””提问”””
if not self.rag_chain:
self.build_chain()
return self.rag_chain.invoke(question)

# 使用示例
if __name__ == ‘__main__’:
# 初始化系统
rag = LocalRAGSystem(model_name=’qwen2:7b’)

# 加载文档(PDF 或 TXT)
# documents = rag.load_pdf(‘your_document.pdf’)
documents = rag.load_txt(‘knowledge.txt’)

# 处理文档并创建向量库
rag.process_documents(documents)

# 开始问答
while True:
question = input(“\n请输入问题(输入 q 退出): “)
if question.lower() == ‘q’:
break
answer = rag.ask(question)
print(f”\n回答: {answer.content}”)
“`

这个代码可以直接复制到一个 py 文件里运行。把你的文档改成对应的路径就行。

## 运行结果

跑起来之后,效果大概是这样的:

“`
文档已分割为 127 个块

请输入问题(输入 q 退出): 什么是 RAG 技术?
回答: RAG(检索增强生成)是一种将外部知识检索与大语言模型生成相结合的技术。它通过向量数据库存储私有数据,在回答问题时先检索相关内容,再提供给 LLM 生成答案。这种方式既保证了数据隐私,又能让模型回答最新或特定领域的知识。

请输入问题(输入 q 退出): 如何优化检索效果?
回答: 优化检索效果可以从以下几个方面入手:
1. 调整 chunk_size 和 overlap 参数
2. 选择更适合的 embedding 模型
3. 使用 MMR(最大边际相关)检索策略
4. 对检索结果进行重排序
5. 根据实际效果调整 top_k 参数
“`

可以看到,模型能够根据我们导入的文档内容来回答问题,而不是一本正经地胡说八道。

## 总结

这篇文章把本地 RAG 系统的搭建流程完整走了一遍。从环境配置、Ollama 安装、文档处理、向量数据库创建,到最后组装成完整的问答链。

几个关键点再强调一下:

– Ollama 确实让本地跑大模型变得极其简单,一条命令就搞定。
– Chroma 作为向量数据库,部署成本几乎为零,跟 LangChain 配合得天衣无缝。
– 中文分块要把分隔符配置好,包括中文标点,不然分出来的块驴唇不对马嘴。
– 检索效果取决于 chunk_size、overlap、top_k 这些参数,需要根据实际数据调。

整个系统完全可以离线跑,处理敏感文档特别合适。企业内部知识库、个人笔记管理,这些场景都能用。

后续可以探索的方向还有很多:换更强的 embedding 模型(比如 BGE)、支持更多文件格式、加对话历史让多轮问答更自然、试试看 Milvus 这样更专业的向量数据库。

代码跑起来有问题的话,欢迎交流。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇