在 AI 应用爆发的今天,大模型虽然拥有强大的语言理解能力,但往往面临"知识过时"、"一本正经地胡说八道(幻觉)"以及"无法读取企业私有数据"三大痛点。检索增强生成(RAG)技术正是解决这些问题的最佳方案。本文将带你使用 Python 和 LangChain 框架,从零搭建一套完全离线、免费、数据安全的本地 RAG 知识库问答系统。
一、 RAG 核心原理与架构设计
RAG(Retrieval-Augmented Generation)的核心思想是"先检索,再生成"。其标准工作流包含以下几个关键步骤:
数据准备:将本地的文档(如 TXT、Markdown、PDF 等)加载到系统中。
文本切块(Chunking):大模型有上下文窗口限制,且长文本检索精度低。因此需要将长文档按一定字符数(如 500 字)切分为小段,并设置一定的重叠区(Overlap)以保持语义连贯。
向量化(Embedding):使用嵌入模型将文本块转换为多维向量(一串数字)。这一步相当于将人类语言翻译成了机器能理解的"语义坐标"。
存入向量数据库:将生成的向量及其对应的原文存入向量数据库中。
检索与生成:当用户提问时,系统将问题转化为向量,在数据库中找出最相似的几个文本块,然后将"原文 + 问题"拼接成 Prompt 发送给大模型,大模型据此生成精准回答。
二、 技术栈选型与环境准备
为了降低入门门槛并保证数据绝对安全,本方案采用纯本地运行的技术栈:
流程编排:LangChain(大模型应用开发事实标准,组件丰富)
向量数据库:Chroma(嵌入式轻量级本地向量库,无需部署服务,开箱即用)
嵌入模型:all-MiniLM-L6-v2(开源轻量模型,本地离线运行,无需 API)
大语言模型:兼容 OpenAI 接口规范的任意模型(如本地部署的 Qwen-7B、Llama 3 等)
首先,确保你的电脑安装了 Python 3.9+,然后一键安装核心依赖:pip install langchain langchain-text-splitters langchain-chroma sentence-transformers openai python-dotenv
三、 核心代码实战:从文档入库到智能问答
以下是一个经过优化的、可直接复制运行的完整 RAG 系统代码。代码包含了文档加载、中文语义分割、向量库构建以及抗幻觉问答链。
python
import os
from dotenv import load_dotenv
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain_community.document_loaders import DirectoryLoader, TextLoader
# ====================== 1. 核心配置项 ======================
KNOWLEDGE_DIR = "./knowledge_base" # 你的本地文档文件夹
VECTOR_DB_PATH = "./chroma_vector_db" # 向量库持久化路径
CHUNK_SIZE = 500 # 文本块大小
CHUNK_OVERLAP = 50 # 文本块重叠大小
RETRIEVE_TOP_K = 3 # 检索最相关的片段数量
# 大模型配置(以本地 Ollama 或兼容 OpenAI 接口的服务为例)
LLM_BASE_URL = "http://localhost:11434/v1"
LLM_API_KEY = "ollama"
LLM_MODEL_NAME = "qwen2:7b"
load_dotenv()
# ====================== 2. 文档加载与切分 ======================
def load_and_split_documents():
loader = DirectoryLoader(KNOWLEDGE_DIR, glob="**/*.txt", loader_cls=TextLoader, loader_kwargs={"encoding": "utf-8"})
documents = loader.load()
# 针对中文优化的递归字符分割器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE, chunk_overlap=CHUNK_OVERLAP,
separators=["\n\n", "\n", "。", "!", "?", ";", ",", " ", ""]
)
chunks = text_splitter.split_documents(documents)
print(f"文档处理完成,共生成 {len(chunks)} 个文本块")
return chunks
# ====================== 3. 构建向量数据库 ======================
def build_vector_db(chunks):
embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2", model_kwargs={"device": "cpu"})
if os.path.exists(VECTOR_DB_PATH) and len(os.listdir(VECTOR_DB_PATH)) > 0:
vector_db = Chroma(persist_directory=VECTOR_DB_PATH, embedding_function=embeddings)
print("已加载本地已有向量库")
else:
vector_db = Chroma.from_documents(documents=chunks, embedding=embeddings, persist_directory=VECTOR_DB_PATH)
print("向量库构建完成并已持久化")
return vector_db
# ====================== 4. 初始化模型与问答链 ======================
def create_rag_chain():
llm = ChatOpenAI(base_url=LLM_BASE_URL, api_key=LLM_API_KEY, model=LLM_MODEL_NAME, temperature=0)
vector_db = build_vector_db(load_and_split_documents())
retriever = vector_db.as_retriever(search_type="similarity", search_kwargs={"k": RETRIEVE_TOP_K})
qa_chain = RetrievalQA.from_chain_type(llm=llm, retriever=retriever, return_source_documents=True)
return qa_chain
# ====================== 5. 交互式问答测试 ======================
if __name__ == "__main__":
qa_chain = create_rag_chain()
while True:
question = input("\n请输入问题(输入 exit 退出):")
if question.lower() == "exit": break
result = qa_chain.invoke({"query": question})
print(f"\n答案:{result['result']}")
print("参考来源:", [doc.metadata['source'] for doc in result['source_documents']])
四、 避坑指南与进阶优化
在搭建本地 RAG 时,新手常遇到以下问题,需特别注意:
中文切分失效:默认的英文空格切分器会破坏中文语义,务必使用 RecursiveCharacterTextSplitter 并传入中文标点作为分隔符。
检索结果不准:如果切块太大(如超过 1000 字),向量语义会被稀释,导致检索不准;建议将 CHUNK_SIZE 控制在 300-500 之间。
大模型幻觉:在 Prompt 中必须加入约束,例如:"请仅根据以下参考内容回答问题,如果参考内容中没有相关信息,请直接回答'我不知道',不要编造。"
性能优化:如果文档量达到数万篇,建议将本地嵌入模型替换为针对中文优化的 m3e-base 或 bge-large-zh,并将向量库升级为 Milvus 或 Elasticsearch 以支持高并发检索。
通过上述步骤,你就能在本地快速搭建起一个专属的 AI 知识库。无论是用于个人学习笔记问答,还是企业内部规章制度检索,RAG 都是目前落地成本最低、效果最显著的技术方案。