使用 LangChain + Ollama 构建本地知识库问答系统(完整指南)

# 使用 LangChain + Ollama 构建本地知识库问答系统

## 背景介绍

在企业日常运营中,我们经常需要处理大量的内部文档、技术手册、会议记录等文本数据。传统的关键词搜索方式往往难以理解用户的真实查询意图,返回的结果也不够精准。随着大语言模型(LLM)技术的快速发展,基于语义理解的智能问答系统成为了可能。

然而,将企业内部数据上传到第三方 API 服务存在数据泄露的风险。对于金融、医疗、法律等敏感行业来说,数据安全是首要考量。这就催生了对本地化部署的强烈需求,在自有服务器上运行 LLM,既能保证数据安全,又能获得智能问答的能力。

Ollama 是一个开源的本地 LLM 运行环境,它支持在本地机器上运行各种开源模型,如 Llama 2、Mistral、Gemma 等。LangChain 则是一个 LLM 应用开发框架,提供了丰富的工具来构建 RAG(检索增强生成)应用。将这两者结合,我们可以构建一个完全本地化的知识库问答系统。

## 问题描述

构建本地知识库问答系统面临以下几个核心挑战:

**第一,文档处理。** 企业的知识文档格式多样,包括 PDF、Word、Markdown、纯文本等。需要一套统一的流程来解析这些不同格式的文件,提取出可用的文本内容。

**第二,向量存储。** 为了实现语义搜索,需要将文本转换为向量并存储到向量数据库中。常用的选择包括 Chroma、FAISS、Milvus 等。

**第三,检索与生成。** 当用户提出问题时,系统需要在向量数据库中检索出最相关的文档片段,然后将这些片段作为上下文提供给 LLM,让它生成准确的回答。

**第四,本地部署。** 所有计算都必须在本地完成,不能依赖任何外部 API。这意味着需要选择适合本地硬件的模型,并优化运行效率。

本文将详细讲解如何从零开始搭建这样一个系统,涵盖环境配置、依赖安装、代码实现、运行测试等完整流程。

## 详细步骤

### 步骤一:环境准备

首先需要确认你的机器满足基本要求。由于要运行 LLM,建议内存至少 16GB,硬盘剩余空间不少于 20GB。操作系统可以是 Linux、macOS 或 Windows(需要 WSL2)。

安装 Ollama 是第一步。访问 Ollama 官网(https://ollama.com),根据你的操作系统下载对应的安装包。安装完成后,在终端运行以下命令下载模型:

“`bash
ollama pull llama2
ollama pull nomic-embed-text
“`

第一个是用于生成回答的主模型,第二个是用于生成文本嵌入的模型。这两个模型加起来需要大约 7GB 的磁盘空间。

### 步骤二:创建项目目录

“`bash
mkdir -p ~/local-kb-qa
cd ~/local-kb-qa
“`

### 步骤三:安装 Python 依赖

建议使用 Python 3.10 或更高版本。创建虚拟环境并安装所需库:

“`bash
python -m venv venv
source venv/bin/activate

pip install langchain langchain-community langchain-ollama \
chromadb beautifulsoup4 pypdf python-docx \
streamlit
“`

这些包的作用分别是:langchain 和 langchain-ollama 用于连接 Ollama 和构建问答链;chromadb 是向量数据库;beautifulsoup4 用于解析网页;pypdf 和 python-docx 用于处理 PDF 和 Word 文档;streamlit 用来快速搭建一个可视化界面。

### 步骤四:准备知识文档

在项目目录下创建一个 data 文件夹,放入你的知识文档。可以是 txt、md、pdf、docx 等格式。为了演示,我们创建几个示例文件:

“`bash
mkdir -p ~/local-kb-qa/data
“`

你也可以放置真实的业务文档进去。

### 步骤五:编写文档加载器

创建 loader.py 文件,实现对不同格式文档的加载:

“`python
from langchain_community.document_loaders import (
TextLoader,
PyPDFLoader,
Docx2txtLoader,
UnstructuredMarkdownLoader,
DirectoryLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter

def load_documents(data_path: str):
“””
加载指定目录下的所有文档
“””
# 根据文件类型选择加载器
loaders = {
“.txt”: TextLoader,
“.md”: UnstructuredMarkdownLoader,
“.pdf”: PyPDFLoader,
“.docx”: Docx2txtLoader,
}

from pathlib import Path
documents = []

# 遍历目录下的所有文件
for file_path in Path(data_path).rglob(“*”):
if file_path.suffix in loaders:
try:
loader_class = loaders[file_path.suffix]
loader = loader_class(str(file_path))
documents.extend(loader.load())
print(f”已加载: {file_path.name}”)
except Exception as e:
print(f”加载 {file_path.name} 失败: {e}”)

return documents

def split_documents(documents, chunk_size=500, chunk_overlap=50):
“””
将文档分割成小块
“””
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=chunk_overlap,
length_function=len,
)
return text_splitter.split_documents(documents)

if __name__ == “__main__”:
# 测试加载
docs = load_documents(“./data”)
print(f”共加载 {len(docs)} 个文档”)
“`

### 步骤六:创建向量存储

创建 vectorstore.py 文件,将文档内容转换为向量并存储:

“`python
from langchain_ollama import OllamaEmbeddings
from langchain_chroma import Chroma

def create_vector_store(documents, persist_directory=”./chroma_db”):
“””
创建向量存储
“””
# 使用 Ollama 的嵌入模型
embeddings = OllamaEmbeddings(
model=”nomic-embed-text”,
base_url=”http://localhost:11434″
)

# 创建 Chroma 向量数据库
vectorstore = Chroma.from_documents(
documents=documents,
embedding=embeddings,
persist_directory=persist_directory
)

print(f”向量数据库已创建,包含 {vectorstore._collection.count()} 个向量”)
return vectorstore

def load_vector_store(persist_directory=”./chroma_db”):
“””
加载已存在的向量数据库
“””
embeddings = OllamaEmbeddings(
model=”nomic-embed-text”,
base_url=”http://localhost:11434″
)

vectorstore = Chroma(
persist_directory=persist_directory,
embedding_function=embeddings
)

return vectorstore

if __name__ == “__main__”:
from loader import load_documents, split_documents

docs = load_documents(“./data”)
chunks = split_documents(docs)
vectorstore = create_vector_store(chunks)
“`

### 步骤七:构建问答系统

创建 qa_system.py 文件,实现完整的问答功能:

“`python
from langchain_ollama import ChatOllama
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

class LocalQA:
def __init__(self, model_name=”llama2″, vectorstore=None):
“””
初始化本地问答系统
“””
self.llm = ChatOllama(
model=model_name,
base_url=”http://localhost:11434″,
temperature=0.7,
)

self.vectorstore = vectorstore

# 自定义提示词模板
self.prompt_template = “””你是一个专业的知识库问答助手。请根据以下参考文档回答用户的问题。

参考文档:
{context}

用户问题:{question}

请基于参考文档给出准确、详细的回答。如果参考文档中没有相关信息,请如实告知用户。”””

def set_vectorstore(self, vectorstore):
“””设置向量数据库”””
self.vectorstore = vectorstore

def answer(self, question: str) -> str:
“””
回答用户问题
“””
if self.vectorstore is None:
return “请先加载知识库”

# 检索相关文档
docs = self.vectorstore.similarity_search(question, k=3)

# 合并检索到的文档内容
context = “\n\n”.join([doc.page_content for doc in docs])

# 构建提示词
prompt = self.prompt_template.format(
context=context,
question=question
)

# 调用 LLM 生成回答
response = self.llm.invoke(prompt)

return response.content

def main():
“””测试问答系统”””
from vectorstore import load_vector_store

vectorstore = load_vector_store()
qa = LocalQA(vectorstore=vectorstore)

test_questions = [
“什么是 LangChain?”,
“Ollama 支持哪些模型?”,
“如何安装 LangChain?”
]

for question in test_questions:
print(f”\n问题: {question}”)
answer = qa.answer(question)
print(f”回答: {answer}”)
print(“-” * 50)

if __name__ == “__main__”:
main()
“`

### 步骤八:创建可视化界面

为了使用更方便,我们用 Streamlit 快速搭建一个 Web 界面。创建 app.py:

“`python
import streamlit as st
from qa_system import LocalQA
from vectorstore import load_vector_store

st.set_page_config(
page_title=”本地知识库问答系统”,
page_icon=”🤖”
)

st.title(“🤖 本地知识库问答系统”)

if qa_system not in st.session_state:
st.session_state.qa_system = None
st.session_state.vectorstore = None

with st.sidebar:
st.header(“设置”)

import requests
try:
response = requests.get(“http://localhost:11434/api/tags”)
if response.status_code == 200:
st.success(“✅ Ollama 服务正常运行”)
else:
st.error(“❌ Ollama 服务未运行”)
except:
st.error(“❌ 无法连接到 Ollama,请确保已启动服务”)

if st.button(“🔄 重新加载知识库”):
with st.spinner(“加载中…”):
try:
st.session_state.vectorstore = load_vector_store()
st.session_state.qa_system = LocalQA(vectorstore=st.session_state.vectorstore)
st.success(f”✅ 知识库已加载,包含 {st.session_state.vectorstore._collection.count()} 个文档片段”)
except Exception as e:
st.error(f”加载失败: {e}”)

if st.session_state.vectorstore is None:
with st.spinner(“首次加载知识库…”):
try:
st.session_state.vectorstore = load_vector_store()
st.session_state.qa_system = LocalQA(vectorstore=st.session_state.vectorstore)
st.success(f”✅ 知识库已加载,包含 {st.session_state.vectorstore._collection.count()} 个文档片段”)
except Exception as e:
st.error(f”加载知识库失败: {e}”)

question = st.text_input(“请输入您的问题:”, placeholder=”例如:什么是 RAG?”)

if question:
if st.session_state.qa_system is None:
st.warning(“请先等待知识库加载完成”)
else:
with st.spinner(“思考中…”):
answer = st.session_state.qa_system.answer(question)

st.markdown(“### 回答”)
st.write(answer)

with st.expander(“查看参考文档”):
docs = st.session_state.vectorstore.similarity_search(question, k=3)
for i, doc in enumerate(docs, 1):
st.markdown(f”**来源 {i}:** {doc.metadata.get(source, 未知)}”)
st.write(doc.page_content)
“`

## 运行结果

完成了所有代码编写后,按以下步骤运行系统:

首先,确保 Ollama 服务正在运行:

“`bash
ollama serve
“`

然后,在项目目录下运行 Streamlit 应用:

“`bash
streamlit run app.py
“`

浏览器会自动打开 http://localhost:8500,你应该能看到一个简洁的 Web 界面。在输入框中输入你的问题,系统会从本地知识库中检索相关内容,并结合 LLM 生成回答。

首次运行时,系统会加载你放入 data 目录中的所有文档,将其分割成小块,转换为向量并存储到 Chroma 数据库中。这个过程可能需要几分钟时间,取决于文档数量和大小。加载完成后,你可以在侧边栏看到已处理的文档片段数量。

系统支持连续提问,每次问题都会基于知识库中的内容给出回答。如果知识库中没有相关信息,系统会如实告知,而不是编造答案。

## 总结

本文详细介绍了如何使用 LangChain 和 Ollama 构建一个完全本地化的知识库问答系统。整个系统运行在本地机器上,不需要连接任何外部 API,有效保护了企业数据的隐私和安全。

核心技术点包括:使用 LangChain 的文档加载器处理多种格式的文件;通过 RecursiveCharacterTextSplitter 将长文档分割成适合处理的小块;利用 Ollama 的嵌入模型将文本转换为向量;使用 Chroma 向量数据库存储和检索相似文档;最后通过 LangChain 的 RetrievalQA 链结合 LLM 生成最终回答。

这个系统具有很强的扩展性。你可以根据实际需求更换不同的开源模型,如 Mistral、Gemma 等;也可以添加更多的文档格式支持;还可以在此基础上实现文件上传、批量问答等功能。对于有更高性能要求的场景,可以考虑使用 GPU 加速或部署到显存更大的服务器上。

暂无评论

发送评论 编辑评论


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